川の水位変化をグラフにする

現在住んでいる場所は大きめの川が近くにあるところで、シミュレーションによっては洪水で5m近くの浸水になる可能性が示唆されているところです。
幸い引っ越してきてからはそういった水害はありませんが、一応河川の水量がどんな風に変化しているのか見てみたいなと思ってグラフ化に挑戦してみました。
Ruby を使って書いています。

なお、リアルタイム 川の防災情報というサイトで観測所ごとに水位変化をグラフで見られるので今回の作業はほぼ趣味です。
もっとスマートな書き方とかあるんだろうなぁ…。

川の防災情報のサイトからデータを取ろうと思ったのですが、Q&A を見ると水情報国土データ管理センター クリアリングハウスから取ってねという文章があったので、クリアリングハウスからデータを頂くことにします。

目的の観測所のリアルタイム10分水位一覧表を見るとデータが一覧になっています。
フロッピーディスクのアイコンからデータをダウンロードできるようです。
このフロッピーディスクからのリンク先が動的に変化するようなので、HTMLを取得してリンク先を探した後にダウンロードするスクリプトをまずは書きました。

f:id:k-side:20150706170206p:plain

取得するリアルタイム10分水位一覧表のURLは外部のJSONに保存しました。

{
   "url": "%URL%"
}

データを取得するスクリプトget_river_level.rbという名前で保存しています。

#!/usr/bin/env ruby
# coding: utf-8

require 'open-uri'
require 'nokogiri'
require 'json'

url = ""

json_data = open('./config.json') do |io|
   url = JSON.load(io)
end

url = json_data['url']
uri = "http://www1.river.go.jp/"

html = open(url, 'r:euc-jp').read

html.split("\n").each do |line|
   if line =~ /download.gif/
      href = Nokogiri::HTML(line)
      uri += href.css('a').attribute('href').value
   end
end

dat = open(uri, 'r:sjis').read

File.open('./level.dat', 'w:UTF-8:sjis') do |data|
   data << dat
end

次に取得したデータを SQLite3 に突っ込むスクリプトです。
set_river_level.rb という名前にしました。

#!/usr/bin/env ruby
# coding: utf-8

require 'sqlite3'

sqlite = './level.sqlite'

unless File.exist?(sqlite)
   db = SQLite3::Database.new(sqlite)
   
   sql = <<-SQL
   CREATE TABLE level (
     time text,
     level real
   );
   SQL
   
   db.execute_batch(sql)
   
   db.close
end

river_level = ""
open('./level.dat') do |dat|
   river_level = dat.readlines[9..-1]
end

db = SQLite3::Database.new(sqlite)

river_level.each do |line|
    data  = line.split(',')
    time = data[0..1].join(' ')
    unless data[2] == '-'
       sql = <<-SQL
SELECT * FROM level
WHERE time="#{time}"; 
       SQL
       result = db.execute(sql)
       if result == []
          sql = <<-SQL
INSERT INTO level VALUES(
   "#{time}",
   "#{data[2]}"
);
          SQL
          db.execute_batch(sql)
       end
    end
end

db.close

最後にグラフ化するスクリプトです。
HTML をガリガリ書きたくなかったので sinatra と slim を使っていますが、sinatra がうまく動かなかったので Ruby のバージョンを上げたりとか違うところで苦労しました。
myapp.rb という名前で保存しています。

#!/usr/bin/env ruby
# coding: utf-8

require 'sinatra'
require 'sinatra/reloader'
require 'sqlite3'
require 'slim'
require 'json'

set :bind, '0.0.0.0'

get '/' do
   slim :index
end

get '/data' do

   db = SQLite3::Database.new('./level.sqlite')
   count = db.execute("SELECT COUNT(time) FROM level;")[0][0] - 150

   sql = "SELECT * FROM level LIMIT #{count}, 150;"
   
   data = db.execute(sql)
   db.close

   data.to_json
end

views ディレクトリを作成して、中に index.slim を作成しています。

doctype html
html
   head
      title River Level Data
      meta charset='utf-8'
   body
      script src='jquery.min.js' type='text/javascript'
      script src='https://www.google.com/jsapi' type='text/javascript'
      script src='chartkick.js' type='text/javascript'
      div id='chart'

javascript:
   $(function(){
      $.ajax({
         type: 'GET',
         url: '/data',
         dataType: 'json',
      }).done(function(d){
            new Chartkick.AreaChart("chart",d);
         });
      });

myapp.rb を実行してからブラウザで接続するとグラフが現れます。
直近概ね48時間のデータをグラフ化させています。

f:id:k-side:20150706170207p:plain

各種フレームワークのおかげで思ったより簡単に作成することができました。

参考にしたサイト