end0tknr's kipple - 新web写経開発

http://d.hatena.ne.jp/end0tknr/ から移転します

postgisを使って、perlやjavaでも幾何学計算

http://www.finds.jp/docs/pgisman/1.5.1/

postgisのリファレンスを読んで、perljava、c等言語を問わず、幾何学計算を実現できそうな気がしたので試してみました。
(postgisのinstallは別のエントリをご覧下さい)

回転や拡大縮小、移動は、html5 canvasでも容易に実現できますが、postgisを使えば、図形のマージ(和・合成)や差分、積 も次のようなsrcで実現できます。

#!/usr/local/bin/perl
use strict;
use DBI;
use Math::Trig qw/pi deg2rad rad2deg/;
use Data::Dumper;

main();

sub main {
    my $dbh = connect_db();

    #元の図形
    my $polygon_txt =
	'POLYGON((-0.2 -0.2, 0.2 -0.2, 0.2 0.2, -0.2 0.2, -0.2 -0.2))';
    #text -> geometry
    my $geom = test_ST_GeomFromText($dbh,$polygon_txt);
    my $geom_ret;
    #回転(rad指定)
    $geom_ret = test_ST_RotateZ($dbh,$geom, deg2rad(45) );
    #拡大縮小
    $geom_ret = test_ST_Scale($dbh,$geom, 0.5,0.5);
    print "ST_Scale : ",test_ST_AsText($dbh,$geom_ret),"\n";
    #移動
    $geom_ret = test_ST_Translate($dbh,$geom, 0.2,0.2);
    print "ST_Translate : ",test_ST_AsText($dbh,$geom_ret),"\n";
    #重心算出
    $geom_ret = test_ST_Centroid($dbh,$geom);
    print "ST_Centroid : ",test_ST_AsText($dbh,$geom_ret),"\n";


    #距離算出
    my $polygon_txt_2 =
	'POLYGON((0.3 0.0, 0.4 0.0, 0.4 0.1, 0.3 0.1, 0.3 0.0))';
    my $geom_2 = test_ST_GeomFromText($dbh,$polygon_txt_2);
    my $ret = test_ST_Distance($dbh,$geom,$geom_2);
    print "ST_Distance : $ret\n";

    #合成(和)
    $polygon_txt_2 =
	'POLYGON((0.0 -0.2, 0.3 -0.2, 0.3 0.2, 0.0 0.2, 0.0 -0.2))';
    $geom_2 = test_ST_GeomFromText($dbh,$polygon_txt_2);
    $geom_ret = test_ST_Union($dbh,$geom,$geom_2);
    print "ST_Union : ",test_ST_AsText($dbh,$geom_ret),"\n";
    #差分
    $geom_ret = test_ST_Difference($dbh,$geom,$geom_2);
    print "ST_Difference : ",test_ST_AsText($dbh,$geom_ret),"\n";
    #積
    $geom_ret = test_ST_Intersection($dbh,$geom,$geom_2);
    print "ST_Intersection : ",test_ST_AsText($dbh,$geom_ret),"\n";

    $dbh->disconnect();
}

sub test_ST_Union {
    my ($dbh,$geom_1,$geom_2) = @_;
    my $sth = $dbh->prepare("select ST_Union(?,?)");
    $sth->execute($geom_1,$geom_2);
    return $sth->fetchrow_array();
}

sub test_ST_Intersection {
    my ($dbh,$geom_1,$geom_2) = @_;
    my $sth = $dbh->prepare("select ST_Intersection(?,?)");
    $sth->execute($geom_1,$geom_2);
    return $sth->fetchrow_array();
}

sub test_ST_Difference {
    my ($dbh,$geom_1,$geom_2) = @_;
    my $sth = $dbh->prepare("select ST_Difference(?,?)");
    $sth->execute($geom_1,$geom_2);
    return $sth->fetchrow_array();
}

sub test_ST_Distance {
    my ($dbh,$geom_1,$geom_2) = @_;
    my $sth = $dbh->prepare("select ST_Distance(?,?)");
    $sth->execute($geom_1,$geom_2);
    return $sth->fetchrow_array();
}

sub test_ST_Translate {
    my ($dbh,$geom,$dx,$dy) = @_;
    my $sth = $dbh->prepare("select ST_Translate(?,?,?)");
    $sth->execute($geom,$dx,$dy);
    return $sth->fetchrow_array();
}

sub test_ST_Centroid {
    my ($dbh,$geom) = @_;
    my $sth = $dbh->prepare("select ST_Centroid(?)");
    $sth->execute($geom);
    return $sth->fetchrow_array();
}

sub test_ST_Scale {
    my ($dbh,$geom,$scale_x,$scale_y) = @_;
    my $sth = $dbh->prepare("select ST_Scale(?,?,?)");
    $sth->execute($geom,$scale_x,$scale_y);
    return $sth->fetchrow_array();
}

sub test_ST_RotateZ {
    my ($dbh,$geom,$rad) = @_;
    my $sth = $dbh->prepare("select ST_RotateZ(?,?)");
    $sth->execute($geom,$rad);
    return $sth->fetchrow_array();
}

sub test_ST_GeomFromText {
    my ($dbh,$txt) = @_;
    my $sth = $dbh->prepare("SELECT ST_GeomFromText(?)");
    $sth->execute($txt);
    return $sth->fetchrow_array();
}

sub test_ST_AsText {
    my ($dbh,$geom) = @_;
    my $sth = $dbh->prepare("SELECT ST_AsText(?)");
    $sth->execute($geom);
    return $sth->fetchrow_array();
}

sub connect_db {
    my $dbh = DBI->connect('DBI:Pg:dbname=postgis;host=localhost',
			   'postgres',
			   '');
    return $dbh;
}

1;

実行結果

$ ./gis.pl 
ST_Scale : POLYGON((-0.1 -0.1,0.1 -0.1,0.1 0.1,-0.1 0.1,-0.1 -0.1))
ST_Translate : POLYGON((0 0,0.4 0,0.4 0.4,0 0.4,0 0))
ST_Centroid : POINT(-0 -0)
ST_Distance : 0.1
ST_Union : POLYGON((0 -0.2,-0.2 -0.2,-0.2 0.2,0 0.2,0.2 0.2,0.3 0.2,0.3 -0.2,0.2 -0.2,0 -0.2))
ST_Difference : POLYGON((0 -0.2,-0.2 -0.2,-0.2 0.2,0 0.2,0 -0.2))
ST_Intersection : POLYGON((0.2 -0.2,0 -0.2,0 0.2,0.2 0.2,0.2 -0.2))

精度(丸め誤差)や、座標→メートル変換等の課題はありますが、使える気がします。