Minnesota MapServer は、OpenGIS Web Map Service仕様に準拠したインターネットWebマッピングサーバです。
MapServerのウェブサイトはhttp://mapserver.org/です。
OpenGIS Web Map Serviceの仕様書はhttp://www.opengeospatial.org/standards/wmsにあります。
PostGISとMapServerとを併用するには、MapServerの設定方法を知る必要がありますが、本文書の範囲外です。本節では、PostGIS独特の問題と設定詳細について説明します。
PostGISをMapServerで使うには、次のものが必要です。
PostGIS 0.6以上
MapServer 3.5以上
MapServerは、他のPostgreSQLクライアントと同じくPostGIS/PostgreSQLデータにアクセスします。アクセスにはlibpqインタフェースを使います。つまり、MapServerは、PostGISサーバにアクセスするあらゆるネットワークに繋がっている計算機にインストールすることができ、PostGISをデータソースとして利用できます。システム間の接続が速いほど良くなります。
"--with-postgis"と好きなconfigureオプションを付けてMpaServerのコンパイルとインストールを行います。
Mapserverのmapファイルの中に、PostGISレイヤを追加します。たとえば次のようになります。
LAYER
CONNECTIONTYPE postgis
NAME "widehighways"
# リモートの空間データベースに接続します
CONNECTION "user=dbuser dbname=gisdatabase host=bigserver"
PROCESSING "CLOSE_CONNECTION=DEFER"
# 'roads'テーブルの'geom'カラムからラインを取得します
DATA "geom from roads using srid=4326 using unique gid"
STATUS ON
TYPE LINE
# 範囲内のラインである広い高速道路のみ描画します
FILTER "type = 'highway' and numlanes >= 4"
CLASS
# 非常に広い高速道路はより明るい色かつ2ピクセル幅にします
EXPRESSION ([numlanes] >= 6)
STYLE
COLOR 255 22 22
WIDTH 2
END
END
CLASS
# 残りは、暗い色かつ1ピクセル幅です
EXPRESSION ([numlanes] < 6)
STYLE
COLOR 205 92 82
END
END
END上の例におけるPostGIS特有のディレクティブは次の通りです。
PostGISレイヤでは常に"postgis"とします。
データベース接続は「接続文字列」によって制御されます。接続文字列は、次に示すような標準的なキーと値からなります(<>内はデフォルト値)。
user=<ユーザ名> password=<パスワード> dbname=<ユーザ名> hostname=<サーバ> port=<5432>
空の接続文字列も妥当とされますし、あらゆるキーと値のペアは省略できます。接続するためには一般的にはdbnameとusernameとが最少で与えるものとなります。
このパラメータの形式は "<カラム名> from <テーブル名> using srid=<SRID> using unique <主キー>"となります。ここで、カラム名は地図に描画したい空間カラムを指し、SRIDはそのカラムで使われるSRIDで、主キーはそのテーブルの主キー (またはインデックスを伴う一意の値を持つカラム)です。
"using srid"と"using unique"節は省略できます。MapServerは可能なら自動的に正しい値を判断しますが、地図を描画するサーバ上で余分なクエリを若干実行するコストがかかります。
接続を閉じずに複数のレイヤで再利用する場合にCLOSE_CONNECTION=DEFERとします。速度が改善します。詳細な説明についてはMapServer PostGIS Performance Tipsを参照して下さい。
フィルタは、妥当なSQL文字列でなければなりません。この文字列は、通常はSQLクエリにおける"WHERE"に続く論理式に対応します。たとえば、6レーン以上の道路だけを描画する場合には、"num_lanes >= 6"というフィルタを使います。
空間データベースにおいては、空間 (GiST)インデックスを、マップに描かれるレイヤ全てに構築していることを保証して下さい。
CREATE INDEX [インデックス名] ON [テーブル名] USING GIST ( [ジオメトリカラム] );
MapServerを使用するレイヤのクエリを実行する場合には、"using unique"節もDATAステートメントに追加しなければなりません。
MapServerでは、クエリ実行の際には、それぞれの空間レコードを識別するための一意な識別子が必要です。MapServerのPostGISモジュールは、一意な識別子を提供するために、ユーザ指定の一意な値を使います。テーブルの主キーを使うのが最も良い方法です。
USING疑似SQL節を使ってMapServerがより複雑なクエリの結果を理解できるようにするための情報を追加します。より詳しく言うと、ビューまたは副問い合わせが元テーブル (DATA定義で"FROM"の右にあるもの)として使われる時、MapServerが自動的に一意な識別子がそれぞれの行にあるか、また、SRIDがテーブルにあるかを判別するのは困難です。USING節によって、MapServerがこれらの情報を得ることができます。例を次に挙げます。
DATA "geom FROM (
SELECT
table1.geom AS geom,
table1.gid AS gid,
table2.data AS data
FROM table1
LEFT JOIN table2
ON table1.id = table2.id
) AS new_table USING UNIQUE gid USING SRID=4326"MapServerは、マップクエリを実行する際、行識別のために、それぞれの行に一意な識別子を求めます。通常ならシステムテーブルから主キーを識別しますが、ビューや副問い合わせでは、一意性のあるカラムを自動的に知ることができません。MapServerのクエリ機能を使いたいなら、一意性のあるカラムをビューまたは副問い合わせに追加する必要があり、USING UNIQUE宣言を付ける必要があります。たとえば、この目的のための主キー値のテーブルでのカラム名や、結果セットで一意性が保障されたカラムを明示的にSELECTに入れることができます。
![]() | |
「マップクエリ」はマップ上でクリックして、その場所におけるフィーチャーに関する情報を問い合わせる動作です。「マップクエリ」と |
PostGISは、MapServerに正しいデータを返すために、ジオメトリがどの空間参照系を使っているかを知る必要があります。通常は、この情報はPostGISデータベースの"geometry_columns"テーブルから得ることができます。しかし、副問い合わせやビューのような一時テーブルでは、この方法は不可能です。そこで、 USING SRID=オプションを使って、正しいSRIDがDATA定義で使われるように指定します。
簡単な例から始めて、ステップアップしていきましょう。次のMapServerレイヤ定義を考えて下さい。
LAYER
CONNECTIONTYPE postgis
NAME "roads"
CONNECTION "user=theuser password=thepass dbname=thedb host=theserver"
DATA "geom from roads"
STATUS ON
TYPE LINE
CLASS
STYLE
COLOR 0 0 0
END
END
ENDこのレイヤは"roads"テーブルにある道路ジオメトリの全部を黒線で表示するものです。
では、少なくとも1:100000にズームするまでは高速道路だけを表示したい、としましょう。次の二つのレイヤで、その効果が実現できます。
LAYER
CONNECTIONTYPE postgis
CONNECTION "user=theuser password=thepass dbname=thedb host=theserver"
PROCESSING "CLOSE_CONNECTION=DEFER"
DATA "geom from roads"
MINSCALE 100000
STATUS ON
TYPE LINE
FILTER "road_type = 'highway'"
CLASS
COLOR 0 0 0
END
END
LAYER
CONNECTIONTYPE postgis
CONNECTION "user=theuser password=thepass dbname=thedb host=theserver"
PROCESSING "CLOSE_CONNECTION=DEFER"
DATA "geom from roads"
MAXSCALE 100000
STATUS ON
TYPE LINE
CLASSITEM road_type
CLASS
EXPRESSION "highway"
STYLE
WIDTH 2
COLOR 255 0 0
END
END
CLASS
STYLE
COLOR 0 0 0
END
END
END一つ目のレイヤはスケールが1:100000以上であるときに使われ、道路タイプが"highway"である道路のみ黒線で表示されます。FILTERオプションによって、道路タイプが"highway"の場合のみ表示することになります。
二つ目のレイヤはスケールが1:100000未満である時に使われ、"highway"は赤い二重細線で表示され、他の道路は黒線で表示されます。
さて、MapServerの機能を使うだけで、二つのおもしろいことを実行しました。しかし、DATAのSQLステートメントは、単純なままです。道路名が (どういう理由かは知りませんが)他のテーブルに収められていて、それのデータを取得するためにテーブルを連結して、道路のラベルを取る必要がある、とします。
LAYER
CONNECTIONTYPE postgis
CONNECTION "user=theuser password=thepass dbname=thedb host=theserver"
DATA "geom FROM (SELECT roads.gid AS gid, roads.geom AS geom,
road_names.name as name FROM roads LEFT JOIN road_names ON
roads.road_name_id = road_names.road_name_id)
AS named_roads USING UNIQUE gid USING SRID=4326"
MAXSCALE 20000
STATUS ON
TYPE ANNOTATION
LABELITEM name
CLASS
LABEL
ANGLE auto
SIZE 8
COLOR 0 192 0
TYPE truetype
FONT arial
END
END
ENDこのANNOTAIONレイヤでは、縮尺が1:20000以下のときに、全ての道路に緑色のラベルを表示します。また、この例は、 DATA定義で、SQLのJOINを使用する方法も示しています。
Javaクライアントは、直接的にテキスト表現として、またはPostGISに同梱されているJDBC拡張オブジェクトを使用して、PostgreSQLデータベース内にある、PostGISの"geometry"オブジェクトにアクセスできます。JDBC拡張オブジェクトを使うためには、"postgis.jar"ファイルを、JDBCドライバパッケージの"postgresql.jar"とともに、 CLASSPATHに置く必要があります。
import java.sql.*;
import java.util.*;
import java.lang.*;
import org.postgis.*;
public class JavaGIS {
public static void main(String[] args) {
java.sql.Connection conn;
try {
/*
* JDBCドライバをロードして接続を確立します。
*/
Class.forName("org.postgresql.Driver");
String url = "jdbc:postgresql://localhost:5432/database";
conn = DriverManager.getConnection(url, "postgres", "");
/*
* ジオメトリ型を接続に追加します。
* ご注意 : addDateType()を呼ぶ前に
* 接続をpgsql特有の接続実装にキャストしなければなりません。
*/
((org.postgresql.PGConnection)conn).addDataType("geometry",Class.forName("org.postgis.PGgeometry"));
((org.postgresql.PGConnection)conn).addDataType("box3d",Class.forName("org.postgis.PGbox3d"));
/*
* ステートメントの生成とSELECTクエリの実行を行います。
*/
Statement s = conn.createStatement();
ResultSet r = s.executeQuery("select geom,id from geomtable");
while( r.next() ) {
/*
* ジオメトリをオブジェクトとして検索してジオメトリ型にキャストします。
* オブジェクトを印字します
*/
PGgeometry geom = (PGgeometry)r.getObject(1);
int id = r.getInt(2);
System.out.println("Row " + id + ":");
System.out.println(geom.toString());
}
s.close();
conn.close();
}
catch( Exception e ) {
e.printStackTrace();
}
}
}"PGeometry"オブジェクトは、Point、LineString、Polygon、MultiPoint、MultiLineString、MultiPolygonの各型に依存する、特定のトポロジカルジオメトリオブジェクト ("Geometory"抽象クラスの子クラス)を持つラッパオブジェクトです。
PGgeometry geom = (PGgeometry)r.getObject(1);
if( geom.getType() == Geometry.POLYGON ) {
Polygon pl = (Polygon)geom.getGeometry();
for( int r = 0; r < pl.numRings(); r++) {
LinearRing rng = pl.getRing(r);
System.out.println("Ring: " + r);
for( int p = 0; p < rng.numPoints(); p++ ) {
Point pt = rng.getPoint(p);
System.out.println("Point: " + p);
System.out.println(pt.toString());
}
}
}幾何オブジェクトのさまざまなデータアクセサ関数に関する参照情報については、拡張オブジェクトのJavaDocをご覧下さい。