じゆうけんきゅう: シンガポールのhaze情報のページからPSI情報を取得してアニメーションGIFにしてみた

Oct 4, 2015   #cron  #linux  #ruby  :

組み合わせたらいい感じにできそうなことは知っているんだけど、実際にやったこと無いからやってみたシリーズです。

今回は

  • Ruby
  • ImageMagick
  • RMagick
  • PhantomJS
  • Capybara

を組み合わせて、シンガポールのhaze情報のページからPSI情報を取得してみました。自由研究の成果はこんな感じです(11:00くらいからウェブページのレイアウトが変更されたみたいで日時が表示されなくなりました。。。):

PSI 2015/10/04


Hazeとは何か?

WikipediaによるとHazeとは

ヘイズ (haze) とは、現代の気象用語としては煙霧を意味する英語だが、伝統的には広く靄、塵煙霧など、微粒子により視界が悪くなる大気現象全般を含む。原因は微細な水滴のほか、黄砂、工業などの煤煙、スモッグ、山火事・焼畑などがある。

というように書かれていますが、下の方をよく見ると発生原因として以下が挙げられています:

インドネシアのスマトラ島やカリマンタン島などにおけるプランテーションでの野焼きや森林火災など。人為的な着火による処理の対象としては、パームヤシ農園や、製紙用パルプ材の植林地などでの残渣が主である。インドネシアにおける大手パーム油企業による野焼き規制は強化されているが、2015年現在改善の兆しはない。

インドネシア、お前か。。。健康的にかなり良くないもので、ひどい時は外に出たくないくらいです:

ヘイズは人体に対して深刻な被害を及ぼす危険性がある。目、鼻、喉などの粘膜や、皮膚のかゆみ、気管から肺などの呼吸器系や循環器系への健康被害が報告されている。特にぜんそくや心臓疾患がある場合は注意が必要であると外務省・現地日本大使館から案内されている。

指標は以下のとおりで今日の午前中はUnhealthyでしたが、まぁ軽い方でしたね。。。

haze_table

今回の目的

シンガポールのhaze情報のページからPSI情報をスクリーンショットとして取得し、アニメーションGIFに合成します。下の赤で囲んだ部分を切り取ります:

target

スクリーンショットの取得

ヘッドレスブラウザのPhantomJSをRubyから呼び出してスクリーンショットを取得しました。関係する部分だけを書くと、こんな感じです:

#!/usr/bin/env ruby

require 'capybara'
require 'capybara/poltergeist'

URL                  = 'http://www.haze.gov.sg/haze-updates/psi'
YYYYMMDDHHMM         = Time.now.strftime('%Y%m%d%H%M')

STOREDIR             = "#{File.expand_path(File.dirname(__FILE__))}/screenshots"
screenshot_file_name = "#{STOREDIR}/#{YYYYMMDDHHMM}.png"


################################################
# Get Screenshots
################################################
begin
  Capybara.register_driver :poltergeist do |app|
    Capybara::Poltergeist::Driver.new(app,  {:js_errors => false,  :timeout => 1000 })
  end

  browser = Capybara::Session.new(:poltergeist)

  browser.visit URL
  browser.save_screenshot(screenshot_file_name, full: true)
rescue Exception => e
  puts e.message
  exit 1
end

指定範囲の切り出し

スクリーンショットはウェブページ全体を取得しているので、そこから指定範囲を切り出します。ImageMagickを使いました。使用例はこんな感じです:

% convert <em>screenshot.png</em> -crop '330x340+640+490' output.png

ImageMagickでできることを確認したら、Rubyから同じコマンドを呼び出してあげました。

日時情報の生成

これだけだと日時情報がなくてわかりづらかったため、日時情報の画像を生成してみました。こんな感じです:

% convert -background none label:"2015/10/03 15:00" -pointsize 18 sample01.png

できあがるのはこんな画像です:

sample01

切り出した画像と日時情報の画像を結合

2つをくっつけてみました:

% convert output.png sample01.png -gravity southwest -composite sample02.png

これを一つのRubyスクリプトにまとめてみました

こうなりました:

#!/usr/bin/env ruby

require 'capybara'
require 'capybara/poltergeist'
require 'rmagick'

URL                  = 'http://www.haze.gov.sg/haze-updates/psi'
YYYYMMDDHHMM         = Time.now.strftime('%Y%m%d%H%M')
OUT_STR              = Time.now.strftime('%Y/%m/%d %H:%M')

STOREDIR             = "#{File.expand_path(File.dirname(__FILE__))}/screenshots"
screenshot_file_name = "#{STOREDIR}/#{YYYYMMDDHHMM}.png"
crop_file_name       = "#{STOREDIR}/#{YYYYMMDDHHMM}_crop.png"
caption_file_name    = "#{STOREDIR}/#{YYYYMMDDHHMM}_caption.png"
result               = "#{STOREDIR}/#{YYYYMMDDHHMM}_result.png"

################################################
# Get Screenshots
################################################
begin
  Capybara.register_driver :poltergeist do |app|
    Capybara::Poltergeist::Driver.new(app,  {:js_errors =&gt; false,  :timeout =&gt; 1000 }) #追加のオプションはググってくださいw
  end

  browser = Capybara::Session.new(:poltergeist)

  browser.visit URL
  browser.save_screenshot(screenshot_file_name, full: true)
rescue Exception =&gt; e
  puts e.message
  exit 1
end

################################################
# Crop & Date-time image generation
################################################
begin
  original = Magick::Image.read(screenshot_file_name).first

  crop = original.crop(285, 490, 340, 365)
  crop.write(crop_file_name)

  caption = Magick::Image.new(170, 30) {
    self.background_color = 'none'
  }
  draw    = Magick::Draw.new

  draw.annotate(caption, 0, 0, 4, 4, OUT_STR) do
    self.pointsize = 14
    self.gravity   = Magick::SouthWestGravity
  end

  caption.write(caption_file_name)
rescue Exception =&gt; e
  puts e.message
  exit 2
end

################################################
# Composite
################################################
begin
  save_image = crop.composite(caption, Magick::NorthWestGravity, Magick::OverCompositeOp);
  save_image.write(result)
rescue Exception =&gt; e
  puts e.message
  exit 3
ensure
  File.delete screenshot_file_name
end

################################################
# Post processing
################################################
begin
  File.delete crop_file_name
  File.delete caption_file_name
rescue Exception =&gt; e
  puts e.message
  exit 4
end

exit 0

その後

作成したスクリプトを5分おきに実行して、しばらく放ったらかしてから以下のコマンドを実行してアニメーションGIFを作成しました。どうやら「+repage」してあげないと、切り取り前の画像サイズをもってしまっているようで、すごく1024×600超の画像サイズのアニメーションGIFが出来上がってしまいました。。。

% ls -1 | xargs -I % -n 1 convert % -trim +repage %.trim
% rm *.png
% for i in `ls -1`
do
mv ${i} ${i/.trim/}
done
% convert -limit memory 1 -limit map 1 -delay 15 20151004*_result.png anime.gif

参考にしたサイト