mirror of
https://git.osgeo.org/gitea/postgis/postgis
synced 2024-10-24 00:52:40 +00:00
34cb9271c2
git-svn-id: http://svn.osgeo.org/postgis/trunk@9143 b70326c6-7e19-0410-871a-916f4a2858ee
540 lines
19 KiB
XML
540 lines
19 KiB
XML
<?xml version="1.0" encoding="UTF-8"?>
|
|
<chapter>
|
|
<title id="using_postgis_applications">Using PostGIS Geometry: Building Applications</title>
|
|
<sect1 id="Using_MapServer">
|
|
<title>Using MapServer</title>
|
|
|
|
<para>The Minnesota MapServer is an internet web-mapping server which
|
|
conforms to the OpenGIS Web Mapping Server specification.</para>
|
|
|
|
<itemizedlist>
|
|
<listitem>
|
|
<para>The MapServer homepage is at <ulink
|
|
url="http://mapserver.org">http://mapserver.org</ulink>.</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para>The OpenGIS Web Map Specification is at <ulink
|
|
url="http://www.opengeospatial.org/standards/wms">http://www.opengeospatial.org/standards/wms</ulink>.</para>
|
|
</listitem>
|
|
</itemizedlist>
|
|
|
|
<sect2>
|
|
<title>Basic Usage</title>
|
|
|
|
<para>To use PostGIS with MapServer, you will need to know about how to
|
|
configure MapServer, which is beyond the scope of this documentation.
|
|
This section will cover specific PostGIS issues and configuration
|
|
details.</para>
|
|
|
|
<para>To use PostGIS with MapServer, you will need:</para>
|
|
|
|
<itemizedlist>
|
|
<listitem>
|
|
<para>Version 0.6 or newer of PostGIS.</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para>Version 3.5 or newer of MapServer.</para>
|
|
</listitem>
|
|
</itemizedlist>
|
|
|
|
<para>MapServer accesses PostGIS/PostgreSQL data like any other
|
|
PostgreSQL client -- using the <filename>libpq</filename> interface. This means that
|
|
MapServer can be installed on any machine with network access to the
|
|
PostGIS server, and use PostGIS as a source of data. The faster the connection
|
|
between the systems, the better.</para>
|
|
|
|
<orderedlist>
|
|
<listitem>
|
|
<para>Compile and install MapServer, with whatever options you
|
|
desire, including the "--with-postgis" configuration option.</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para>In your MapServer map file, add a PostGIS layer. For
|
|
example:</para>
|
|
|
|
<programlisting>LAYER
|
|
CONNECTIONTYPE postgis
|
|
NAME "widehighways"
|
|
# Connect to a remote spatial database
|
|
CONNECTION "user=dbuser dbname=gisdatabase host=bigserver"
|
|
PROCESSING "CLOSE_CONNECTION=DEFER"
|
|
# Get the lines from the 'geom' column of the 'roads' table
|
|
DATA "geom from roads using srid=4326 using unique gid"
|
|
STATUS ON
|
|
TYPE LINE
|
|
# Of the lines in the extents, only render the wide highways
|
|
FILTER "type = 'highway' and numlanes >= 4"
|
|
CLASS
|
|
# Make the superhighways brighter and 2 pixels wide
|
|
EXPRESSION ([numlanes] >= 6)
|
|
STYLE
|
|
COLOR 255 22 22
|
|
WIDTH 2
|
|
END
|
|
END
|
|
CLASS
|
|
# All the rest are darker and only 1 pixel wide
|
|
EXPRESSION ([numlanes] < 6)
|
|
STYLE
|
|
COLOR 205 92 82
|
|
END
|
|
END
|
|
END</programlisting>
|
|
|
|
<para>In the example above, the PostGIS-specific directives are as
|
|
follows:</para>
|
|
|
|
<variablelist>
|
|
<varlistentry>
|
|
<term>CONNECTIONTYPE</term>
|
|
|
|
<listitem>
|
|
<para>For PostGIS layers, this is always "postgis".</para>
|
|
</listitem>
|
|
</varlistentry>
|
|
|
|
<varlistentry>
|
|
<term>CONNECTION</term>
|
|
|
|
<listitem>
|
|
<para>The database connection is governed by the a 'connection
|
|
string' which is a standard set of keys and values like this
|
|
(with the default values in <>):</para>
|
|
|
|
<para>user=<username> password=<password>
|
|
dbname=<username> hostname=<server>
|
|
port=<5432></para>
|
|
|
|
<para>An empty connection string is still valid, and any of
|
|
the key/value pairs can be omitted. At a minimum you will
|
|
generally supply the database name and username to connect
|
|
with.</para>
|
|
</listitem>
|
|
</varlistentry>
|
|
|
|
<varlistentry>
|
|
<term>DATA</term>
|
|
|
|
<listitem>
|
|
<para>The form of this parameter is "<geocolumn> from
|
|
<tablename> using srid=<srid> using unique <primary key>" where the column is the spatial column to
|
|
be rendered to the map, the SRID is SRID used by the column and the primary key is the table primary key (or any
|
|
other uniquely-valued column with an index).</para>
|
|
<para>You can omit the "using srid" and "using unique" clauses and MapServer will automatically determine the
|
|
correct values if possible, but at the cost of running a few extra queries on the server for each map
|
|
draw.</para>
|
|
</listitem>
|
|
</varlistentry>
|
|
|
|
<varlistentry>
|
|
<term>PROCESSING</term>
|
|
|
|
<listitem>
|
|
<para>Putting in a CLOSE_CONNECTION=DEFER if you have multiple layers reuses existing connections instead of closing them. This improves
|
|
speed. Refer to for <ulink url="http://blog.cleverelephant.ca/2008/10/mapserverpostgis-performance-tips.html">MapServer PostGIS Performance Tips</ulink> for a more detailed explanation. </para>
|
|
</listitem>
|
|
</varlistentry>
|
|
|
|
<varlistentry>
|
|
<term>FILTER</term>
|
|
|
|
<listitem>
|
|
<para>The filter must be a valid SQL string corresponding to
|
|
the logic normally following the "WHERE" keyword in a SQL
|
|
query. So, for example, to render only roads with 6 or more
|
|
lanes, use a filter of "num_lanes >= 6".</para>
|
|
</listitem>
|
|
</varlistentry>
|
|
</variablelist>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para>In your spatial database, ensure you have spatial (GiST)
|
|
indexes built for any the layers you will be drawing.</para>
|
|
|
|
<programlisting>CREATE INDEX [indexname] ON [tablename] USING GIST ( [geometrycolumn] );</programlisting>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para>If you will be querying your layers using MapServer you will
|
|
also need to use the "using unique" clause in your DATA statement.</para>
|
|
|
|
<para>MapServer requires unique identifiers for each spatial record
|
|
when doing queries, and the PostGIS module of MapServer uses the
|
|
unique value you specify in order to provide these unique
|
|
identifiers. Using the table primary key is the best practice.</para>
|
|
</listitem>
|
|
</orderedlist>
|
|
</sect2>
|
|
|
|
<sect2>
|
|
<title>Frequently Asked Questions</title>
|
|
|
|
<qandaset>
|
|
<qandaentry>
|
|
<question>
|
|
<para>When I use an <varname>EXPRESSION</varname> in my map file,
|
|
the condition never returns as true, even though I know the values
|
|
exist in my table.</para>
|
|
</question>
|
|
|
|
<answer>
|
|
<para>Unlike shape files, PostGIS field names have to be
|
|
referenced in EXPRESSIONS using <emphasis>lower
|
|
case</emphasis>.</para>
|
|
|
|
<programlisting>EXPRESSION ([numlanes] >= 6)</programlisting>
|
|
</answer>
|
|
</qandaentry>
|
|
|
|
<qandaentry>
|
|
<question>
|
|
<para>The FILTER I use for my Shape files is not working for my
|
|
PostGIS table of the same data.</para>
|
|
</question>
|
|
|
|
<answer>
|
|
<para>Unlike shape files, filters for PostGIS layers use SQL
|
|
syntax (they are appended to the SQL statement the PostGIS
|
|
connector generates for drawing layers in MapServer).</para>
|
|
|
|
<programlisting>FILTER "type = 'highway' and numlanes >= 4"</programlisting>
|
|
</answer>
|
|
</qandaentry>
|
|
|
|
<qandaentry>
|
|
<question>
|
|
<para>My PostGIS layer draws much slower than my Shape file layer,
|
|
is this normal?</para>
|
|
</question>
|
|
|
|
<answer>
|
|
<para>In general, the more features you are drawing into a given map,
|
|
the more likely it is that PostGIS will be slower than Shape files.
|
|
For maps with relatively few features (100s), PostGIS will often be faster.
|
|
For maps with high feature density (1000s), PostGIS will always be slower.
|
|
</para>
|
|
|
|
<para>If you are finding substantial draw performance problems, it
|
|
is possible that you have not built a spatial index on your
|
|
table.</para>
|
|
|
|
<programlisting>postgis# CREATE INDEX geotable_gix ON geotable USING GIST ( geocolumn );
|
|
postgis# VACUUM ANALYZE;</programlisting>
|
|
</answer>
|
|
</qandaentry>
|
|
|
|
<qandaentry>
|
|
<question>
|
|
<para>My PostGIS layer draws fine, but queries are really slow.
|
|
What is wrong?</para>
|
|
</question>
|
|
|
|
<answer>
|
|
<para>For queries to be fast, you must have a unique key for your
|
|
spatial table and you must have an index on that unique
|
|
key.</para>
|
|
|
|
<para>You can specify what unique key for mapserver to use with
|
|
the <varname>USING UNIQUE</varname> clause in your
|
|
<varname>DATA</varname> line:</para>
|
|
|
|
<programlisting>DATA "geom FROM geotable USING UNIQUE gid"</programlisting>
|
|
|
|
</answer>
|
|
</qandaentry>
|
|
|
|
<qandaentry>
|
|
<question>
|
|
<para>Can I use "geography" columns (new in PostGIS 1.5) as a source for
|
|
MapServer layers?</para>
|
|
</question>
|
|
|
|
<answer>
|
|
<para>Yes! MapServer understands geography columns as being the same as
|
|
geometry columns, but always using an SRID of 4326. Just make sure to include
|
|
a "using srid=4326" clause in your <varname>DATA</varname> statement. Everything else
|
|
works exactly the same as with geometry.</para>
|
|
|
|
<programlisting>DATA "geog FROM geogtable USING SRID=4326 USING UNIQUE gid"</programlisting>
|
|
|
|
</answer>
|
|
</qandaentry>
|
|
|
|
|
|
</qandaset>
|
|
</sect2>
|
|
|
|
<sect2>
|
|
<title>Advanced Usage</title>
|
|
|
|
<para>The <varname>USING</varname> pseudo-SQL clause is used to add some
|
|
information to help mapserver understand the results of more complex
|
|
queries. More specifically, when either a view or a subselect is used as
|
|
the source table (the thing to the right of "FROM" in a
|
|
<varname>DATA</varname> definition) it is more difficult for mapserver
|
|
to automatically determine a unique identifier for each row and also the
|
|
SRID for the table. The <varname>USING</varname> clause can provide
|
|
mapserver with these two pieces of information as follows:</para>
|
|
|
|
<programlisting>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"</programlisting>
|
|
|
|
<variablelist>
|
|
<varlistentry>
|
|
<term>USING UNIQUE <uniqueid></term>
|
|
|
|
<listitem>
|
|
<para>MapServer requires a unique id for each row in order to
|
|
identify the row when doing map queries. Normally it identifies
|
|
the primary key from the system tables. However, views and subselects don't
|
|
automatically have an known unique column. If you want to use MapServer's
|
|
query functionality, you need to ensure your view
|
|
or subselect includes a uniquely valued column, and declare it with <varname>USING UNIQUE</varname>.
|
|
For example, you could explicitly select nee of the table's primary key
|
|
values for this purpose, or any other column which is guaranteed
|
|
to be unique for the result set.</para>
|
|
|
|
<note>
|
|
<para>"Querying a Map" is the action of clicking on a map to ask
|
|
for information about the map features in that location. Don't
|
|
confuse "map queries" with the SQL query in a
|
|
<varname>DATA</varname> definition.</para>
|
|
</note>
|
|
</listitem>
|
|
</varlistentry>
|
|
|
|
<varlistentry>
|
|
<term>USING SRID=<srid></term>
|
|
|
|
<listitem>
|
|
<para>PostGIS needs to know which spatial referencing system is
|
|
being used by the geometries in order to return the correct data
|
|
back to MapServer. Normally it is possible to find this
|
|
information in the "geometry_columns" table in the PostGIS
|
|
database, however, this is not possible for tables which are
|
|
created on the fly such as subselects and views. So the
|
|
<varname>USING SRID=</varname> option allows the correct SRID to
|
|
be specified in the <varname>DATA</varname> definition.</para>
|
|
</listitem>
|
|
</varlistentry>
|
|
</variablelist>
|
|
|
|
</sect2>
|
|
|
|
<sect2>
|
|
<title>Examples</title>
|
|
|
|
<para>Lets start with a simple example and work our way up. Consider the
|
|
following MapServer layer definition:</para>
|
|
|
|
<programlisting>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</programlisting>
|
|
|
|
<para>This layer will display all the road geometries in the roads table
|
|
as black lines.</para>
|
|
|
|
<para>Now lets say we want to show only the highways until we get zoomed
|
|
in to at least a 1:100000 scale - the next two layers will achieve this
|
|
effect:</para>
|
|
|
|
<programlisting>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</programlisting>
|
|
|
|
<para>The first layer is used when the scale is greater than 1:100000,
|
|
and displays only the roads of type "highway" as black lines. The
|
|
<varname>FILTER</varname> option causes only roads of type "highway" to
|
|
be displayed.</para>
|
|
|
|
<para>The second layer is used when the scale is less than 1:100000, and
|
|
will display highways as double-thick red lines, and other roads as
|
|
regular black lines.</para>
|
|
|
|
<para>So, we have done a couple of interesting things using only
|
|
MapServer functionality, but our <varname>DATA</varname> SQL statement
|
|
has remained simple. Suppose that the name of the road is stored in
|
|
another table (for whatever reason) and we need to do a join to get it
|
|
and label our roads.</para>
|
|
|
|
<programlisting>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</programlisting>
|
|
|
|
<para>This annotation layer adds green labels to all the roads when the
|
|
scale gets down to 1:20000 or less. It also demonstrates how to use an
|
|
SQL join in a <varname>DATA</varname> definition.</para>
|
|
</sect2>
|
|
</sect1>
|
|
|
|
<sect1>
|
|
<title>Java Clients (JDBC)</title>
|
|
|
|
<para>Java clients can access PostGIS "geometry" objects in the PostgreSQL
|
|
database either directly as text representations or using the JDBC
|
|
extension objects bundled with PostGIS. In order to use the extension
|
|
objects, the "postgis.jar" file must be in your CLASSPATH along with the
|
|
"postgresql.jar" JDBC driver package.</para>
|
|
|
|
<programlisting>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 {
|
|
/*
|
|
* Load the JDBC driver and establish a connection.
|
|
*/
|
|
Class.forName("org.postgresql.Driver");
|
|
String url = "jdbc:postgresql://localhost:5432/database";
|
|
conn = DriverManager.getConnection(url, "postgres", "");
|
|
/*
|
|
* Add the geometry types to the connection. Note that you
|
|
* must cast the connection to the pgsql-specific connection
|
|
* implementation before calling the addDataType() method.
|
|
*/
|
|
((org.postgresql.PGConnection)conn).addDataType("geometry",Class.forName("org.postgis.PGgeometry"));
|
|
((org.postgresql.PGConnection)conn).addDataType("box3d",Class.forName("org.postgis.PGbox3d"));
|
|
/*
|
|
* Create a statement and execute a select query.
|
|
*/
|
|
Statement s = conn.createStatement();
|
|
ResultSet r = s.executeQuery("select geom,id from geomtable");
|
|
while( r.next() ) {
|
|
/*
|
|
* Retrieve the geometry as an object then cast it to the geometry type.
|
|
* Print things out.
|
|
*/
|
|
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();
|
|
}
|
|
}
|
|
}</programlisting>
|
|
|
|
<para>The "PGgeometry" object is a wrapper object which contains a
|
|
specific topological geometry object (subclasses of the abstract class
|
|
"Geometry") depending on the type: Point, LineString, Polygon, MultiPoint,
|
|
MultiLineString, MultiPolygon.</para>
|
|
|
|
<programlisting>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());
|
|
}
|
|
}
|
|
}</programlisting>
|
|
|
|
<para>The JavaDoc for the extension objects provides a reference for the
|
|
various data accessor functions in the geometric objects.</para>
|
|
</sect1>
|
|
|
|
<sect1>
|
|
<title>C Clients (libpq)</title>
|
|
|
|
<para>...</para>
|
|
|
|
<sect2>
|
|
<title>Text Cursors</title>
|
|
|
|
<para>...</para>
|
|
</sect2>
|
|
|
|
<sect2>
|
|
<title>Binary Cursors</title>
|
|
|
|
<para>...</para>
|
|
</sect2>
|
|
</sect1>
|
|
</chapter>
|