GeoRuby を Ruby 1.9 でも動かす方法
GeoRuby は Array#to_s の関係で、Ruby の 1.8 では問題なく動作しますが、1.9 ではうまく動作しない部分があります。とりあえず、Shapefile を読む部分について、GeoRuby を 1.9 対応にするための修正プログラムを作成してみました。
経過
d:id:hfu:20100513:1273695379 の Shapefile の属性読み出しコードは、Ruby 1.9 ではうまく動作しないとの情報を得ました。確かに、次のような結果となります。
$ ruby1.9 open_rdedg.rb open_rdedg.rb:11:in `block (2 levels) in <main>': undefined method `each' for nil:NilClass (NoMethodError) from /opt/local/lib/ruby1.9/gems/1.9.1/gems/GeoRuby-1.3.4/lib/geo_ruby/shp4r/shp.rb:122:in `block in each' from /opt/local/lib/ruby1.9/gems/1.9.1/gems/GeoRuby-1.3.4/lib/geo_ruby/shp4r/shp.rb:121:in `each' from /opt/local/lib/ruby1.9/gems/1.9.1/gems/GeoRuby-1.3.4/lib/geo_ruby/shp4r/shp.rb:121:in `each' from open_rdedg.rb:10:in `block in <main>' from /opt/local/lib/ruby1.9/gems/1.9.1/gems/GeoRuby-1.3.4/lib/geo_ruby/shp4r/shp.rb:56:in `open' from open_rdedg.rb:9:in `<main>'
そこで、該当する部分を確認し、原因を追いかけてみると、Array#to_s を使っているメソッド GeoRuby::Shp4r::Dbf::Reader#active_record? あたりが原因であることが分かります。
to_s (Array) - Rubyリファレンス
にあるとおり、Ruby 1.9 からは、Array#to_s は Array#inspect の結果を戻しますから、Ruby 1.8 での挙動を前提にしたこのあたりの記述は、Ruby 1.9 では意図に反する動作をしてしまいます。
d:id:hfu:20100513:1273695379 の Shapefile の属性読み出しコードをとりあえず動作させるには、Ruby 1.9 における Array の動作を書き戻して、
class Array def to_s self.join end end
とすることも出来ますが、Ruby の組み込みクラスの挙動を書き換えることになり、気持ちも悪ければ危険でもあります。
そこで、GeoRuby の側を Ruby 1.9 に対応させることにしました。
処置
まず、
るびま
すなわち
Rubyの互換性警告ライブラリを作ってみました / Je viens de faire un librairie qui alerte la compatibilité de Ruby. - ふぇみにん日記(2009-08-15)
にある、compatibility_warning.rb を導入して、情報出力を停止したコード:
require 'rubygems' require 'geo_ruby' require 'iconv' require 'compatibility_warning' include GeoRuby::Shp4r include GeoRuby::SimpleFeatures c = Iconv.new('UTF-8', 'Shift_JIS') ShpFile.open('rdedg.shp') {|shp| shp.each {|r| r.data.each {|k, v| # print "#{c.iconv(k)}: #{c.iconv(v.to_s)}\n" } } }
を、Ruby 1.8 で動作させます。すると、
pro:13 hfu$ ruby open_rdedg.rb (compatibility warning) Array#to_s used in /opt/local/lib/ruby/gems/1.8/gems/GeoRuby-1.3.4/lib/geo_ruby/shp4r/dbf.rb:94:in `active_record?' (compatibility warning) Array#to_s used in /opt/local/lib/ruby/gems/1.8/gems/GeoRuby-1.3.4/lib/geo_ruby/shp4r/dbf.rb:148:in `unpack_string'
のような出力が得られます。
これで、直すべき部分が同定されましたので、該当部分のメソッド定義を書き換える、次のコードを作成します。
#geo_ruby_fix19.rb module GeoRuby module Shp4r module Dbf class Reader private def active_record? @data_file.read(1).unpack('H2').join == '20' rescue false end def unpack_string(field) unpack_field(field).join end end end end end
次に、いま作成した修正コードを読み込むように修正した、元のプログラム:
#open_rdedg.rb require 'rubygems' require 'geo_ruby' require 'iconv' require 'geo_ruby_fix19' include GeoRuby::Shp4r include GeoRuby::SimpleFeatures c = Iconv.new('UTF-8', 'Shift_JIS') ShpFile.open('rdedg.shp') {|shp| shp.each {|r| r.data.each {|k, v| print "#{c.iconv(k)}: #{c.iconv(v.to_s)}\n" } } }
を Ruby 1.9 で実行すると、次のように問題なく動作することが確認できます。
$ ruby1.9 open_rdedg.rb
id: …
今後の作業では、require 'geo_ruby_fix19' を入れておこうと思います。そうしておけば、Ruby 1.8 でも Ruby 1.9 でも動作するからです。
本来行うべき処置について
こういった修正は、いわゆる「本家」に戻すべきだと思うのですが、「本家」は活動が泊まっているようであり、github 上の2つのフォークも、どちらかというとそれぞれの方の目的のためのフォークであるようですので、「本家に戻す」処置については、あとで考えるつもりです。他の方によって「本家に戻す」ことを妨げるものではありません。