Geo::FeatureList で MultiPolygon のあとに Polygon を書き込むと例外発生
id:yellow_73 さんのご指摘により、Geo::FeatureList で MultiPolygon を write したあと Polygon を write しようとすると、例外が発生することが分かりました。この問題の当面の回避策をご紹介します。
不具合再現
d:id:yellow_73:20071219 で、以下のようなご指摘をいただきました。
気を良くして県別ポリゴンで同じことをしようとしたら、
2007-12-19
DefaultFeature.java:229:in `org.geotools.feature.DefaultFeature.setAttribute': org.geotools.feature.IllegalAttributeException: expected com.vividsolutions.jts.geom.MultiPolygon , but got com.vividsolutions.jts.geom.Polygon (NativeException)
と怒られました。
マルチポリゴンとしてデータを入れてるのに(シングルの)ポリゴンが来た、とか怒ってるくさいところまでは分かるのですが…。
この現象を再現するスクリプトを作ってみました:
# multi.rb require 'geotools' Geo::FeatureList.open('multi.shp') do |w| wkts = ['MULTIPOLYGON (((0 0, 1 0, 1 1, 0 0)), ((2 2, 3 2, 3 3, 2 2)))', 'POLYGON ((4 4, 5 4, 5 5, 4 4))'] wkts.each do |wkt| g = Geo::import_wkt_geometry(wkt) w.write({:the_geom => g}) end end
このスクリプトで、ご指摘の例外が発生します:
kuro:19 hfu$ jruby multi.rb DEBUG: jruby mode DefaultFeature.java:229:in `org.geotools.feature.DefaultFeature.setAttribute': org.geotools.feature.IllegalAttributeException: expected com.vividsolutions.jts.geom.MultiPolygon , but got com.vividsolutions.jts.geom.Polygon (NativeException) from DefaultFeature.java:293:in `org.geotools.feature.DefaultFeature.setAttribute' from NativeMethodAccessorImpl.java:-2:in `sun.reflect.NativeMethodAccessorImpl.invoke0' (以下略)
最近、(JRuby では使えない SQLite3 を使いたい場合があるという下心で、)Rjb でも再び geotools.rb を使えるようにしましたので、CRuby でも同じ例外が発生するかどうか確認しました:
$ ruby multi.rb DEBUG: rjb primitive_conversion mode /opt/local/lib/ruby/site_ruby/1.8/i686-darwin8.9.1/geotools.rb:571:in `method_missing': expected com.vividsolutions.jts.geom.MultiPolygon , but got com.vividsolutions.jts.geom.Polygon (IllegalAttributeException) from /opt/local/lib/ruby/site_ruby/1.8/i686-darwin8.9.1/geotools.rb:571:in `r2gt' from /opt/local/lib/ruby/site_ruby/1.8/i686-darwin8.9.1/geotools.rb:563:in `each' from /opt/local/lib/ruby/site_ruby/1.8/i686-darwin8.9.1/geotools.rb:563:in `r2gt' from /opt/local/lib/ruby/site_ruby/1.8/i686-darwin8.9.1/geotools.rb:681:in `write' from multi.rb:8 from multi.rb:6:in `each' from multi.rb:6 from /opt/local/lib/ruby/site_ruby/1.8/i686-darwin8.9.1/geotools.rb:410:in `open' from multi.rb:3
当面の回避策
この問題は、ユーザスクリプトレベルで Polygon を MultiPolygon に変換することによって回避できます。com.vividsolutions.jts.geom.GeometryFactory#createMultiPolygon を使うことになります。createMultiPolygon が Java 配列をとるメソッドなので、スクリプトが多少長くなってしまいますが、上記の multi.rb に対して、下記 multi_fixed.rb のような変更をすることで、例外の発生を防ぐことができます:
# multi_fixed.rb require 'geotools' gf = Geo::Tools::GeometryFactory.new Geo::FeatureList.open('multi.shp') do |w| wkts = ['MULTIPOLYGON (((0 0, 1 0, 1 1, 0 0)), ((2 2, 3 2, 3 3, 2 2)))', 'POLYGON ((4 4, 5 4, 5 5, 4 4))'] wkts.each do |wkt| g = Geo::import_wkt_geometry(wkt) if g.getClass.getName == 'com.vividsolutions.jts.geom.Polygon' if(Geo::Tools::IMPLEMENTATION == 'rjb') g = gf.createMultiPolygon([g]) elsif(Geo::Tools::IMPLEMENTATION == 'java') array = com.vividsolutions.jts.geom.Polygon[1].new array[0] = g g = gf.createMultiPolygon(array) end end w.write({:the_geom => g}) end end
$ jruby multi_fixed.rb DEBUG: jruby mode (問題なく終了)
ruby multi_fixed.rb DEBUG: rjb primitive_conversion mode (問題なく終了)
長期的な対策について
この問題は、geotools.rb のレベルか、Java の GeoTools のレベルで修正が必要だと思っており、前者なら私による修正、後者なら GeoTools の人たちにバグレポートをすればいいのかなと思っているところです。長期的な対策について整理できたら、あとで書くかもしれません。