DE-9IM predicates inspector r(geom1, geom2)
d:id:yellow_73:20080117 を拝見してこの手の話題の需要を感じたので、エントリしてみます。
DE-9IM のマトリクスを簡単にみることができるメソッドがあったら便利かも
PostGISのマニュアルを見てたら、intersectとcrossの語が使い分けられているふうにみえます。
2008-01-16
これらの言葉の定義は、OpenGIS Simple Features Specification for SQL にあります。定義はかなり数学的で、DE-9IM と呼ばれる体系を使って書かれています。
ただ、DE-9IM を理解するよりも、具体的にこれらの predicates がどのように動くかを見てみるほうが「役に立つ」のではないか、DE-9IM のマトリクスを簡単にみることができるメソッドがあったら便利かも、と思いました。
それ、geotools.rb でできるよ。
これは geotools. rb でできそうだ、と思い、irb で Ruby 標準の p のように使うことを想定した、グローバル関数 r を用意してみました。r は relate の r ということで。r は p の次でもあります。
# de-9im.rb require 'geotools' def r(g1, g2) g1 = Geo::import_wkt_geometry(g1) if g1.class == String g2 = Geo::import_wkt_geometry(g2) if g2.class == String m = g1.relate(g2) print <<-EOS DE-9IM IntersectionMatrix = '#{m.toString}' i.e. g2 Interior Boundary Exterior Interior #{sprintf('% 2d', m.get(0, 0))} #{sprintf('% 2d', m.get(0, 1))} #{sprintf('% 2d', m.get(0, 2))} g1 Boundary #{sprintf('% 2d', m.get(1, 0))} #{sprintf('% 2d', m.get(1, 1))} #{sprintf('% 2d', m.get(1, 2))} Exterior #{sprintf('% 2d', m.get(2, 0))} #{sprintf('% 2d', m.get(2, 1))} #{sprintf('% 2d', m.get(2, 2))} SFSQL Predicates: EOS e = %w{crosses} %w{equals disjoint touches within overlaps crosses intersects contains}.each do |predicate| v = eval("g1.#{predicate}(g2)") print " #{predicate}:\t#{v}#{e.include?(predicate) ? "\t(with JTS extension)" : ''}\n" end print "\nJTS-specific Predicates:\n" %w{covers coveredBy}.each do |predicate| v = eval("g1.#{predicate}(g2)") print " #{predicate}:\t#{v}\n" end nil end
この関数を使うには、geotools.rb が必要です。デフォルト名前空間(というのかな)を汚すので、関数 r 自身は geotools.rb には含めていません。
r は幾何二つを引数に取ります。com.vividsolutions.jts.geom.Geometry のサブクラスのインスタンスでもかまいませんし、幾何の WKT 表現でもかまいません。
例外対応を全然考えていないところについてはあとで考えるかもしれません、JRuby でも CRuby + Rjb でも動くようになっていると思います。
実際に使うとこうなります
上記のスクリプトを de-9im.rb という名前で Ruby が探せるところに保存しておけば、次のようにして二つの幾何の関係を表示することができるようになります。
$ jirb -rde-9im DEBUG: jruby mode irb(main):001:0> r 'POINT (0 0)', 'POINT (0 0)' DE-9IM IntersectionMatrix = '0FFFFFFF2' i.e. g2 Interior Boundary Exterior Interior 0 -1 -1 g1 Boundary -1 -1 -1 Exterior -1 -1 2 SFSQL Predicates: equals: false disjoint: false touches: false within: true overlaps: false crosses: false (with JTS extension) intersects: true contains: true JTS-specific Predicates: covers: true coveredBy: true => nil
CRuby (1.8.6) + Rjb (1.0.11) でも動きます:
$ irb -rde-9im DEBUG: rjb primitive_conversion mode irb(main):001:0> r 'POINT (0 0)', 'POINT (0 0)' DE-9IM IntersectionMatrix = '0FFFFFFF2' i.e. g2 Interior Boundary Exterior Interior 0 -1 -1 g1 Boundary -1 -1 -1 Exterior -1 -1 2 SFSQL Predicates: equals: true disjoint: false touches: false within: true overlaps: false crosses: false (with JTS extension) intersects: true contains: true JTS-specific Predicates: covers: true coveredBy: true => nil
d:id:yellow_73:20080117 の例を r してみる。
d:id:yellow_73:20080117 にある 5 つの例題を r に与えてみると、次のような結果が得られます。
irb(main):001:0> r 'POLYGON( (1 2, 1 -2, -1 -2, -1 2, 1 2) )', 'POLYGON( (2 1, 2 -1, -2 -1, -2 1, 2 1) )' DE-9IM IntersectionMatrix = '212101212' i.e. g2 Interior Boundary Exterior Interior 2 1 2 g1 Boundary 1 0 1 Exterior 2 1 2 SFSQL Predicates: equals: false disjoint: false touches: false within: false overlaps: true crosses: false (with JTS extension) intersects: true contains: false JTS-specific Predicates: covers: false coveredBy: false => nil irb(main):002:0> r 'LINESTRING(-2 0, 2 0)', 'POLYGON( (1 1, 1 -1, -1 -1, -1 1, 1 1) )' DE-9IM IntersectionMatrix = '101FF0212' i.e. g2 Interior Boundary Exterior Interior 1 0 1 g1 Boundary -1 -1 0 Exterior 2 1 2 SFSQL Predicates: equals: false disjoint: false touches: false within: false overlaps: false crosses: true (with JTS extension) intersects: true contains: false JTS-specific Predicates: covers: false coveredBy: false => nil irb(main):003:0> r 'LINESTRING(0 0, 2 0)', 'POLYGON( (1 1, 1 -1, -1 -1, -1 1, 1 1) )' DE-9IM IntersectionMatrix = '1010F0212' i.e. g2 Interior Boundary Exterior Interior 1 0 1 g1 Boundary 0 -1 0 Exterior 2 1 2 SFSQL Predicates: equals: false disjoint: false touches: false within: false overlaps: false crosses: true (with JTS extension) intersects: true contains: false JTS-specific Predicates: covers: false coveredBy: false => nil irb(main):004:0> r 'LINESTRING(1 1, -1 -1)', 'LINESTRING(-1 1, 1 -1)' DE-9IM IntersectionMatrix = '0F1FF0102' i.e. g2 Interior Boundary Exterior Interior 0 -1 1 g1 Boundary -1 -1 0 Exterior 1 0 2 SFSQL Predicates: equals: false disjoint: false touches: false within: false overlaps: false crosses: true (with JTS extension) intersects: true contains: false JTS-specific Predicates: covers: false coveredBy: false => nil irb(main):005:0> r 'LINESTRING(1 1, -1 -1)', 'LINESTRING(2 2, -1 -1)' DE-9IM IntersectionMatrix = '1FF00F102' i.e. g2 Interior Boundary Exterior Interior 1 -1 -1 g1 Boundary 0 0 -1 Exterior 1 0 2 SFSQL Predicates: equals: false disjoint: false touches: false within: true overlaps: false crosses: false (with JTS extension) intersects: true contains: false JTS-specific Predicates: covers: false coveredBy: true => nil
geotools.rb がもたらす機心というものがあるとすれば、このエントリはまさに geotools.rb がもたらす機心に促されて書き出したものです。そういう機心を持つことができてうれしいです。