mirror of
https://git.osgeo.org/gitea/postgis/postgis
synced 2024-10-24 09:02:37 +00:00
76a87e711b
git-svn-id: http://svn.osgeo.org/postgis/trunk@59 b70326c6-7e19-0410-871a-916f4a2858ee
884 lines
52 KiB
XML
884 lines
52 KiB
XML
<?xml version="1.0"?>
|
|
<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook V3.1//EN">
|
|
<book>
|
|
<title>PostGIS Manual</title>
|
|
<bookinfo>
|
|
<editor><firstname>Paul</firstname><surname>Ramsey</surname><affiliation><orgname><ulink url="http://www.refractions.net">Refractions Research Inc</ulink></orgname><address><street>209 - 560 Johnson Street</street><city>Victoria</city><state>British Columbia</state><country>Canada</country><email>pramsey@refractions.net</email></address></affiliation></editor><abstract>
|
|
<para>PostGIS is an extension to the PostgreSQL object-relational database
|
|
system which allows GIS (Geographic Information Systems) objects to be stored
|
|
in the database. PostGIS includes support for GiST indexes, and functions for
|
|
basic analysis of GIS objects.</para>
|
|
</abstract>
|
|
</bookinfo>
|
|
<chapter>
|
|
<title>Introduction</title>
|
|
<para>PostGIS is developed by Refractions Research Inc, as a research
|
|
project into spatial database technology. Refractions is a GIS and database
|
|
consulting company in Victoria, British Columbia, specializing in data
|
|
integration and custom software development. We plan on supporting and
|
|
developing PostGIS to support a range of important GIS functionality, including
|
|
advanced topological constructs (coverages, surfaces, networks), desktop user
|
|
interface tools for viewing and editing GIS data, and web-based access
|
|
tools.</para>
|
|
<sect1>
|
|
<title>Credits</title>
|
|
<variablelist>
|
|
<varlistentry>
|
|
<term>Dave Blasby <dblasby@refractions.net></term>
|
|
<listitem><para>The principal developer of PostGIS. Dave maintains the
|
|
server side objects and index support, as well as the server side analytical
|
|
functions.</para></listitem>
|
|
</varlistentry>
|
|
<varlistentry>
|
|
<term>Paul Ramsey <pramsey@refractions.net></term>
|
|
<listitem><para>Maintains the JDBC objects and keeps track of the documentation and packaging.</para></listitem>
|
|
</varlistentry>
|
|
<varlistentry>
|
|
<term>Jeff Lounsbury <jeffloun@refractions.net></term>
|
|
<listitem><para>Maintains the Shape loader/dumper.</para></listitem>
|
|
</varlistentry>
|
|
</variablelist>
|
|
</sect1>
|
|
<sect1>
|
|
<title>More Information</title>
|
|
<itemizedlist>
|
|
<listitem><para>The latest software, documentation and news items are available at the PostGIS web site, <ulink url="http://postgis.refractions.net">http://postgis.refractions.net</ulink>.</para></listitem>
|
|
<listitem><para>More information about the PostgreSQL database server is available at the PostgreSQL main site <ulink url="http://www.postgreql.org">http://www.postgreql.org</ulink>.</para></listitem>
|
|
<listitem><para>More information about GiST indexing is available at the GiST development site, <ulink url="http://www.sai.msu.su/~megera/postgres/gist">http://www.sai.msu.su/~megera/postgres/gist</ulink>.</para>
|
|
</listitem>
|
|
</itemizedlist>
|
|
</sect1>
|
|
</chapter>
|
|
|
|
<chapter>
|
|
<title>Installation</title>
|
|
<sect1 id="PGInstall">
|
|
<title>PostGIS</title>
|
|
<para>The PostGIS module is a extension to the PostgreSQL backend server.
|
|
As such, PostGIS <emphasis>requires</emphasis> a full copy of the PostgreSQL
|
|
source tree in order to compile. The PostgreSQL source code is available at<ulink url="http://www.postgresql.org">http://www.postgresql.org</ulink>.</para>
|
|
<para>PostGIS 0.5 has been built and tested against PostgreSQL 7.1.2. It
|
|
will probably work with any of the 7.1.x versions. It will
|
|
<emphasis>not</emphasis> work with any version prior to 7.1.</para>
|
|
<orderedlist>
|
|
<listitem><para>Before you can compile the postgis server modules, you must
|
|
compile and install the PostgreSQL package. </para></listitem>
|
|
<listitem><para>Retrieve the PostGIS source archive from http://postgis.refractions.net/postgis-0.5.tar.gz. Uncompress and untar the
|
|
archive in the "contrib" directory of the PostgreSQL source tree.</para><literallayout>cd [postgresql source tree]/contrib
|
|
zcat postgis-0.5.tar.gz | tar xvf -</literallayout>
|
|
</listitem>
|
|
<listitem><para>Once your PostgreSQL installation is up-to-date, enter the
|
|
"postgis" directory, and run the compile and install commands. </para><literallayout>cd ./postgis-0.5
|
|
make
|
|
make install</literallayout>
|
|
</listitem>
|
|
<listitem><para>Before you can use the PostGIS objects in your database, you
|
|
must load the object and function definitions. </para><literallayout>psql -d [yourdatabase] < postgis.sql</literallayout>
|
|
<para>The PostGIS server extensions are now loaded and ready to
|
|
use.</para>
|
|
</listitem>
|
|
</orderedlist>
|
|
</sect1>
|
|
<sect1>
|
|
<title>JDBC</title>
|
|
<para>The JDBC extensions provide Java objects corresponding to the
|
|
internal PostGIS types. These objects can be used to write Java clients which
|
|
query the PostGIS database and draw or do calculations on the GIS data in
|
|
PostGIS.</para>
|
|
<orderedlist>
|
|
<listitem><para>Enter the "jdbc" sub-directory of the PostGIS distribution.
|
|
</para></listitem>
|
|
<listitem><para>Edit the "Makefile" to provide the correct paths of your java
|
|
compiler (JAVAC) and interpreter (JAVA).
|
|
</para></listitem>
|
|
<listitem><para>Run the "make" command. Copy the "postgis.jar" file to
|
|
wherever you keep your java libraries.</para>
|
|
</listitem>
|
|
</orderedlist>
|
|
</sect1>
|
|
<sect1>
|
|
<title>Loader/Dumper</title>
|
|
<para>The data loader should compile very simple.</para><literallayout>cd postgis-0.5/loader
|
|
make</literallayout><para>The loader is called "shp2pgsql" and converts ESRI Shape files into SQL suitable for loading in PostGIS/PostgreSQL.</para>
|
|
</sect1>
|
|
</chapter>
|
|
<chapter>
|
|
<title>Frequently Asked Questions</title>
|
|
<qandaset>
|
|
<qandaentry>
|
|
<question><para>What kind of geometric objects can I store?</para></question>
|
|
<answer><para>You can store point, line, polygon, multipoint, multiline,
|
|
multipolygon, and geometrycollections. These are specified in the Open GIS Well
|
|
Known Text Format (with 3d extentions).</para></answer>
|
|
</qandaentry>
|
|
<qandaentry>
|
|
<question><para>How do I insert a GIS object into the database?</para></question>
|
|
<answer><para>First, you need to create a table with a column of type "geometry"
|
|
to hold your GIS data. Connect to your database with "psql" and try the
|
|
following SQL: <emphasis>CREATE TABLE gtest ( ID int4, NAME varchar(20), GEOM
|
|
geometry)</emphasis>. If the table creation fails, you probably have not loaded
|
|
the PostGIS functions and objects into this database. See the
|
|
<link linkend="PGInstall">installation instructions</link>.</para>
|
|
<para>Then, you can insert a geometry into the table using a SQL insert
|
|
statement. The GIS object itself is formatted using the OpenGIS Consortium
|
|
"well-known text" format: <emphasis>INSERT INTO gtest (ID, NAME, GEOM) VALUES
|
|
(1, 'First Geometry', 'LINESTRING(2 3,4 5,6 5,7 8)')</emphasis>. For more
|
|
information about other GIS objects, see the <link linkend="RefObject">object
|
|
reference</link>.</para>
|
|
<para>To view your GIS data in the table: <emphasis>SELECT * FROM
|
|
gtest</emphasis>. The return value should look something like this:</para> <literallayout> id | name | geom
|
|
----+----------------+-----------------------------
|
|
1 | First Geometry | LINESTRING(2 3,4 5,6 5,7 8)
|
|
(1 row)
|
|
</literallayout>
|
|
</answer>
|
|
</qandaentry>
|
|
<qandaentry>
|
|
<question><para>How do I construct a spatial query?</para></question>
|
|
<answer><para>There are a number of spatial operators available to PostgreSQL,
|
|
and several of them have been implemented by PostGIS in order to provide
|
|
indexing support. However, all the operators have been implemented with the
|
|
following important simplifying assumption: <emphasis>all features shall be
|
|
represented by their bounding boxes</emphasis>.</para>
|
|
<para>We recognize that using bounding boxes to proxy for features is a
|
|
limiting assumption, but it was an important one in moving from the conception
|
|
of a PostgreSQL spatial database to the implementation. Using bounding boxes
|
|
makes queries faster, indexes smaller, and operators simpler.</para>
|
|
<para>The most important spatial operator from a user's perspective is
|
|
the "&&" operator, which tests whether one feature's bounding box
|
|
overlaps that of another. An example of a spatial query using && is:
|
|
<emphasis>SELECT * FROM GTEST WHERE GEOM && 'BOX3D(3 4,4
|
|
5)'::box3d</emphasis>. Note that the bounding box used for querying must be
|
|
explicitly declared as a "box3d" using the "::box3d" casting operation.</para>
|
|
</answer>
|
|
</qandaentry>
|
|
<qandaentry>
|
|
<question><para>How do I speed up spatial queries on large tables?</para></question>
|
|
<answer><para>Fast queries on large tables is the <emphasis>raison
|
|
d'etre</emphasis> of spatial databases (along with transaction support) so
|
|
having a good index in important.</para>
|
|
<para>To build a spatial index on a table with a "geometry" column, use
|
|
the "CREATE INDEX" function as follows: <emphasis>CREATE INDEX [indexname] ON
|
|
[tablename] USING GIST ( [geometrycolumn] gist_geometry_ops) WITH (
|
|
islossy)</emphasis>.</para>
|
|
<para>The "USING GIST" option tells the server to use a GiST (Generalized
|
|
Search Tree) index. The reference to "gist_geometry_ops" tells the server to
|
|
use a particular set of comparison operators for building the index: the
|
|
"gist_geometry_ops" are part of the PostGIS extension. Finally, the "islossy"
|
|
option tells the server that the features being indexed can be proxied by a
|
|
smaller data structure -- in the case of geometries, the features are
|
|
represented in the index by their bounding boxes.</para>
|
|
</answer>
|
|
</qandaentry>
|
|
<qandaentry>
|
|
<question><para>How can I get my search to return things that really are inside
|
|
the search box, not just overlapping bounding boxes?</para></question>
|
|
<answer><para>The '&&' operator only checks bounding box overlaps, but
|
|
you can use the "truly_inside()" function to get only those feature which
|
|
<emphasis>actually</emphasis> intersect the search box. For example, by
|
|
combining the use of "&&" for a fast index search and truly_inside()
|
|
for an accurate final check of the result set, you can get only those features
|
|
inside the search box (note that this <emphasis>only</emphasis> works for
|
|
search boxes right now, not any arbitrary geometry):</para>
|
|
<para><emphasis>SELECT * FROM [TABLE] WHERE [GEOM_COLUMN] &&
|
|
[BOX3d] AND truly_inside([GEOM_COLUMN],[BOX3d]);</emphasis></para>
|
|
</answer>
|
|
</qandaentry> <qandaentry>
|
|
<question><para>Why aren't R-Tree's recommended?</para></question>
|
|
<answer><para>R-Trees have two limitations which make them undesirable for use
|
|
with GIS feature in the PostgreSQL database (note that these limitations are
|
|
due to the PostgreSQL implementations, not the R-Tree concept in
|
|
general):</para>
|
|
<itemizedlist>
|
|
<listitem><para>Building an R-Tree index on a large table of geometries can
|
|
take over twice as long as a GiST index on the same table.</para>
|
|
</listitem>
|
|
<listitem><para>R-Tree indexes in PostgreSQL cannot handle features which are
|
|
larger than 8K in size. GiST indexes can, using the "lossy" trick of
|
|
substituting the bounding box for the feature itself.</para>
|
|
</listitem>
|
|
</itemizedlist>
|
|
</answer>
|
|
</qandaentry>
|
|
</qandaset>
|
|
</chapter>
|
|
<chapter>
|
|
<title>Using PostGIS</title>
|
|
<sect1 id="RefObject">
|
|
<title>GIS Objects</title>
|
|
<para>The GIS objects supported by PostGIS are the "Simple Features"
|
|
defined by the OpenGIS Consortium (OGC). Note that PostGIS currently support
|
|
the features and the representation APIs, but the various comparison and
|
|
convolution operators given in the OGC "Simple Features for SQL"
|
|
specification.</para>
|
|
<para>Examples of the text representations of the features are as
|
|
follows:</para>
|
|
<itemizedlist>
|
|
<listitem><para>POINT(0 0 0)</para>
|
|
</listitem>
|
|
<listitem><para>LINESTRING(0 0,1 1,1 2)</para>
|
|
</listitem>
|
|
<listitem><para>POLYGON((0 0 0,4 0 0,4 4 0,0 4 0,0 0 0),(1 1 0,2 1 0,2 2 0,1
|
|
2 0,1 1 0))</para>
|
|
</listitem>
|
|
<listitem><para>MULTIPOINT(0 0 0,1 2 1)</para>
|
|
</listitem>
|
|
<listitem><para>MULTILINESTRING((0 0 0,1 1 0,1 2 1),(2 3 1,3 2 1,5 4 1))</para>
|
|
</listitem>
|
|
<listitem><para>MULTIPOLYGON(((0 0 0,4 0 0,4 4 0,0 4 0,0 0 0),(1 1 0,2 1 0,2
|
|
2 0,1 2 0,1 1 0)),((-1 -1 0,-1 -2 0,-2 -2 0,-2 -1 0,-1 -1 0)))</para>
|
|
</listitem>
|
|
<listitem><para>GEOMETRYCOLLECTION(POINT(2 3 9),LINESTRING((2 3 4,3 4 5)))</para>
|
|
</listitem>
|
|
</itemizedlist>
|
|
<para>Note that in the examples above there are features with both
|
|
2-dimensional and 3-dimensional coordinates. PostGIS supports both 2d and 3d
|
|
coordinates -- if you describe a feature with 2D coordinates when you insert
|
|
it, the database will return that feature to you with 2D coordinates when you
|
|
extract it. See the sections on the 2d() and 3d() functions for information on
|
|
converting features to a particular coordinate dimension representation.</para>
|
|
|
|
</sect1>
|
|
<sect1>
|
|
<title>Creating a Table with Geometry</title>
|
|
<para>Creating a table with spatial data is done using the "CREATE TABLE"
|
|
SQL command as normal, declaring the spatial columns as type "geometry". Note
|
|
that:</para>
|
|
<itemizedlist>
|
|
<listitem><para>There is no distinction between points, lines and polygons at
|
|
the SQL datatype level. All GIS objects are declared as "geometry", no matter
|
|
when their underlying topology. This means that you can have points, lines, and
|
|
polygons all sharing the same spatial column of a database table. If you are
|
|
making assumptions about feature type homogeneity, you will have to enforce
|
|
them at the time of data loading or retrieval, the database will not do it for
|
|
you.</para>
|
|
</listitem>
|
|
<listitem><para>Unlike some commercial spatial databases, there is no
|
|
limitation on the number of spatial columns you can put in a table.</para>
|
|
</listitem>
|
|
<listitem><para>"Geometry" is a reserved word now (it is the GIS datatype) so
|
|
you cannot name your spatial column "geometry". Try "geom" or "parks" or
|
|
"frank".</para>
|
|
</listitem>
|
|
</itemizedlist>
|
|
<para>Here are some examples of "CREATE TABLE" commands using the
|
|
"geometry" type:</para>
|
|
<itemizedlist>
|
|
<listitem><para>CREATE TABLE bc_parks ( PID int4, PIN int4, PARK_NAME
|
|
varchar(80), PARK_DATE date, PARK_GEOM geometry)</para>
|
|
</listitem>
|
|
<listitem><para>CREATE TABLE road_segments ( GID int4, GEOM geometry, SURFACE
|
|
varchar(20), NUM_LANES int2)</para>
|
|
</listitem>
|
|
</itemizedlist>
|
|
</sect1>
|
|
<sect1>
|
|
<title>Loading GIS Data</title>
|
|
<para>Once you have created a spatial table, you are ready to upload GIS
|
|
data to the database. Currently, there are two ways to get data into a
|
|
PostGIS/PostgreSQL database: using formatted SQL statements or using the Shape
|
|
file loader/dumper.</para>
|
|
<sect2>
|
|
<title>Using SQL</title>
|
|
<para>If you can convert your data to a text representation, then using
|
|
formatted SQL might be the easiest way to get your data into PostGIS. As with
|
|
Oracle and other SQL databases, data can be bulk loaded by piping a large text
|
|
file full of SQL "INSERT" statements into the SQL terminal monitor.</para>
|
|
<para>A data upload file ("roads.sql" for example) might look like
|
|
this:</para> <literallayout>INSERT INTO ROADS_GEOM (ID,GEOM,NAME ) VALUES (1,'LINESTRING(191232 243118,191108 243242)','Jeff Rd');
|
|
INSERT INTO ROADS_GEOM (ID,GEOM,NAME ) VALUES (2,'LINESTRING(189141 244158,189265 244817)','Geordie Rd');
|
|
INSERT INTO ROADS_GEOM (ID,GEOM,NAME ) VALUES (3,'LINESTRING(192783 228138,192612 229814)','Paul St');
|
|
INSERT INTO ROADS_GEOM (ID,GEOM,NAME ) VALUES (4,'LINESTRING(189412 252431,189631 259122)','Graeme Ave');
|
|
INSERT INTO ROADS_GEOM (ID,GEOM,NAME ) VALUES (5,'LINESTRING(190131 224148,190871 228134)','Phil Tce');
|
|
INSERT INTO ROADS_GEOM (ID,GEOM,NAME ) VALUES (6,'LINESTRING(198231 263418,198213 268322)','Dave Cres');
|
|
</literallayout>
|
|
<para>The data file can be piped into PostgreSQL very easily using the
|
|
"psql" SQL terminal monitor:</para><literallayout>psql -d [database] -f roads.sql</literallayout>
|
|
</sect2>
|
|
<sect2>
|
|
<title>Using the Loader</title>
|
|
<para>The data loader converts ESRI Shape files into SQL suitable for insertion into a PostGIS/PostgreSQL database. The loader has several operating modes distinguished by command line flags:</para><variablelist><varlistentry><term>-d</term><listitem><para>Drops the database table before creating a new table with the data in the Shape file.</para></listitem></varlistentry><varlistentry><term>-a</term><listitem><para>Appends data from the Shape file into the database table. Note that to use this option to load multiple files, the files must have the same attributes and same data types.</para></listitem></varlistentry><varlistentry><term>-c</term><listitem><para>Creates a new table and populates it from the Shape file. <emphasis>This is the default mode.</emphasis></para></listitem></varlistentry><varlistentry><term>-dump</term><listitem><para>Creates a new table and populates it from the Shape file. This uses the PostgreSQL "dump" format for the output data and is much faster to load than the default "insert" SQL format. Use this for very large data sets.</para></listitem></varlistentry></variablelist><para>An example session using the loader to create an input file and uploading it might look like this:</para><literallayout>shp2pgsql shaperoads roadstable > roads.sql
|
|
psql -d roadsdb -f roads.sql</literallayout><para>A conversion and upload can be done all in one step using UNIX pipes:</para><literallayout>shp2pgsql shaperoads roadstable | psql -d roadsdb</literallayout></sect2>
|
|
</sect1>
|
|
<sect1>
|
|
<title>Retrieving GIS Data</title>
|
|
<para>Data can be extracted from the database using either SQL or the
|
|
Shape file loader/dumper. In the section on SQL we will discuss some of the
|
|
operators available to do comparisons and queries on spatial tables.</para>
|
|
<sect2>
|
|
<title>Using SQL</title>
|
|
<para>The most straightforward means of pulling data out of the
|
|
database is to use a SQL select query and dump the resulting columns into a
|
|
parsable text file:</para> <literallayout>db=# SELECT * FROM ROADS_GEOM;
|
|
id | geom | name
|
|
1 | LINESTRING(191232 243118,191108 243242) | Jeff Rd
|
|
2 | LINESTRING(189141 244158,189265 244817 | Geordie Rd
|
|
3 | LINESTRING(192783 228138,192612 229814) | Paul St
|
|
4 | LINESTRING(189412 252431,189631 259122) | Graeme Ave
|
|
5 | LINESTRING(190131 224148,190871 228134) | Phil Tce
|
|
6 | LINESTRING(198231 263418,198213 268322) | Dave Cres
|
|
(6 rows)
|
|
|
|
db=#
|
|
</literallayout>
|
|
<para>However, there will be times when some kind of restriction is
|
|
necessary to cut down the number of fields returned. In the case of
|
|
attribute-based restrictions, just use the same SQL syntax as normal with a
|
|
non-spatial table. In the case of spatial restrictions, the following operators
|
|
are available/useful:</para>
|
|
<variablelist><varlistentry><term>&&</term><listitem><para>This operator tells whether the bounding box of one geometry
|
|
overlaps the bounding box of another.</para>
|
|
</listitem></varlistentry><varlistentry><term>~=</term><listitem><para>This operators tests whether two geometries are geometrically
|
|
identical. For example, if "POLYGON((0 0,1 1,1 0,0 0))" is the same as
|
|
"POLYGON((0 0,1 0,1 1,0 0))" (it is).</para>
|
|
</listitem></varlistentry><varlistentry><term>=</term><listitem><para>This operator is a little more naive, it only tests whether
|
|
the bounding boxes of to geometries are the same.</para>
|
|
</listitem></varlistentry></variablelist><para>Next, you can use these operators in queries. Note that when
|
|
specifying geometries and boxes on the SQL command line, you should explicitly
|
|
cast the string representations to their correct type using the "::" casting
|
|
operator. So, for example:</para><literallayout>SELECT * FROM ROADS_GEOM WHERE GEOM ~= 'LINESTRING(191232 243118,191108 243242)'::geometry;</literallayout>
|
|
<para>The above query would return the single record from the
|
|
"ROADS_GEOM" table in which the geometry was equal to that value.</para>
|
|
<para>When using the "&&" operator, you can specify either a
|
|
BOX3D as the comparison feature or a GEOMETRY. When you specify a GEOMETRY,
|
|
however, the bounding box will be used for the comparison.</para><literallayout>SELECT * FROM ROADS_GEOM WHERE GEOM && 'POLYGON((191232 243117,191232 243119,191234 243117,191232 243117))'::geometry;</literallayout>
|
|
<para>The above query will use the bounding box of the polygon for
|
|
comparison purposes.</para>
|
|
<para>The most common spatial query will probably be a "frame-based"
|
|
query, used by client software, like data browsers and web mappers, to grab a
|
|
"map frame" worth of data for display. Using a "BOX3D" object for the frame,
|
|
such a query looks like this:</para> <literallayout>SELECT GEOM FROM ROADS_GEOM WHERE GEOM && 'BOX3D(191232 243117,191232 243119)'::box3d;</literallayout>
|
|
<para>Note the use of the "::box3d" casting opertator at the end of the
|
|
SQL statement to ensure that the BOX3D string representation is properly cast
|
|
into a BOX3D object before the query executes.</para>
|
|
</sect2>
|
|
<sect2>
|
|
<title>Using the Dumper</title>
|
|
<para>This section to be written.</para>
|
|
</sect2>
|
|
</sect1>
|
|
<sect1>
|
|
<title>Building Indexes</title>
|
|
<para>Indexes are what make using a spatial database for large databases
|
|
possible. Without indexing, any search for a feature would require a
|
|
"sequential scan" of every record in the database. Indexing speeds up searching
|
|
by organizing the data into a search tree which can be quickly traversed to
|
|
find a particular record. PostgreSQL supports three kinds of indexes by
|
|
default: B-Tree indexes, R-Tree indexes, and GiST indexes. B-Trees are used for
|
|
data which can be sorted along one axis; for example, numbers, letters, dates.
|
|
GIS data cannot be rationally sorted along one axis (which is greater, (0,0) or
|
|
(0,1) or (1,0)?) so B-Tree indexing is of no use for us. Both R-Tree and GiST
|
|
indexing can be used to index GIS data, and PostGIS has implementations for
|
|
both. Note however, that <emphasis>only GiST indexes are supported and R-Trees
|
|
will likely be dropped in the future</emphasis>.</para>
|
|
<sect2>
|
|
<title>GiST Indexes</title>
|
|
<para>GiST stands for "Generalized Search Tree" and is a generalized
|
|
form of R-Tree indexing. In addition to GIS indexing, GiST is used to speed up
|
|
searches on all kinds of irregular data structures (integer arrays, spectral
|
|
data, etc) which are not amenable to normal B-Tree indexing.</para>
|
|
<para>Once a GIS data table exceeds a few thousand rows, you will want
|
|
to build an index to speed up spatial searches of the data (unless all your
|
|
searches are based on attributes, in which case you'll want to build a normal
|
|
index on the attribute fields).</para>
|
|
<para>The syntax for building a GiST index on a "geometry" column is as
|
|
follows:</para><literallayout>CREATE INDEX [indexname] ON [tablename] USING GIST
|
|
( [geometryfield] GIST_GEOMETRY_OPS ) WITH ( ISLOSSY );</literallayout>
|
|
<para>Building a spatial index is a computationally intensive exercise:
|
|
on tables of around 1 million rows, on a 300MHz Solaris machine, we have found
|
|
building a GiST index takes about 1 hour.</para>
|
|
<para>GiST indexes have two advantages over R-Tree indexes in
|
|
PostgreSQL. Firstly, GiST indexes build much faster than R-Trees; we have found
|
|
that it takes about 4 times more time to build an R-Tree than a GiST index on
|
|
an identical table. Secondly, GiST indexes support the concept of "lossiness"
|
|
which is important when dealing with GIS objects larger than the PostgreSQL 8K
|
|
page size. Lossiness allows PostgreSQL to store only the "important" part of an
|
|
object in an index -- in the case of GIS objects, just the bounding box. GIS
|
|
objects larger than 8K will cause R-Tree indexes to fail in the build
|
|
phase.</para>
|
|
</sect2>
|
|
<sect2>
|
|
<title>R-Tree Indexes</title>
|
|
<para>R-Tree indexes use rectangles to break up a spatial plane into
|
|
chunks for searching. Big rectangles are parents of smaller rectangles which
|
|
are in turn parents of yet smaller rectangles, and so on. The algorithms are
|
|
complex, but the principal is the same as that of the B-Tree: by traversing
|
|
from parent to child down the structure of rectangles, you can locate
|
|
particular spatial objects extremely quickly.</para>
|
|
<para>R-Trees are not recommended for GIS objects: they take much
|
|
longer to build, and do not support objects larger than 8K in size.</para>
|
|
<para>The syntax for building an R-Tree index on a "geometry" column is
|
|
as follows:</para> <literallayout>CREATE INDEX [indexname] ON [tablename] USING RTREE
|
|
( [geometryfield] RT_GEOMETRY_OPS );</literallayout>
|
|
</sect2>
|
|
<sect2>
|
|
<title>Using Indexes</title>
|
|
<para>Ordinarily, indexes invisibly speed up data access: once the
|
|
index is built, the query planner tranparently decides when to use index
|
|
information to speed up a query plan. Unfortunately, the PostgreSQL query
|
|
planner does not optimize the use of R-Tree and GiST indexes well, so very
|
|
often searches which should use a spatial index instead default to a sequence
|
|
scan of the whole table.</para>
|
|
<para>If you find your spatial indexes are not being used (or your
|
|
attribute indexes, for that matter) there are a couple things you can
|
|
do:</para>
|
|
<itemizedlist>
|
|
<listitem><para>Firstly, make sure you run the "VACUUM ANALYZE
|
|
[tablename]" command on the tables you are having problems with. "VACUUM
|
|
ANALYZE" gathers statistics about the number and distributions of values in a
|
|
table, to provide the query planner with better information to make decisions
|
|
around index usage.</para>
|
|
</listitem>
|
|
<listitem><para>Secondly, you can force the planner to use the index
|
|
information by using the "SET ENABLE_SEQSCAN=OFF" command. You should only use
|
|
this command sparingly, and only on spatially indexed queries: generally
|
|
speaking, the planner knows better than you do about when to use normal B-Tree
|
|
indexes. Once you have run your query, you should consider setting
|
|
"ENABLE_SEQSCAN" back on, so that other queries will utilize the planner as
|
|
normal.</para>
|
|
</listitem>
|
|
</itemizedlist>
|
|
</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.Connection)conn).addDataType("geometry","org.postgis.PGgeometry");
|
|
((org.postgresql.Connection)conn).addDataType("box3d","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 acessor 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>
|
|
<chapter>
|
|
<title>PostGIS Reference</title>
|
|
<para>The functions given below are the ones which a user of PostGIS is likely to need. There are other functions which are required support functions to the PostGIS objects which are not of use to a general user.</para>
|
|
<sect1>
|
|
<title>OpenGIS Functions</title>
|
|
<variablelist>
|
|
<varlistentry>
|
|
<term>AsBinary(geometry)</term>
|
|
<listitem><para>Returns the geometry in the OGC "well-known-binary" format,
|
|
using the endian encoding of the server on which the database is running. This is useful in binary cursors to pull data out
|
|
of the database without converting it to a string representation.</para>
|
|
</listitem>
|
|
</varlistentry><varlistentry>
|
|
<term>Dimension(geometry)</term>
|
|
<listitem><para>Returns '2' if the geometry is two dimensional and '3' if the geometry is three dimensional.</para></listitem>
|
|
</varlistentry>
|
|
|
|
<varlistentry>
|
|
<term>Envelope(geometry)</term>
|
|
<listitem><para>Returns a POLYGON representing the bounding box of the geometry.</para></listitem>
|
|
</varlistentry>
|
|
<varlistentry>
|
|
<term>GeometryType(geometry)</term>
|
|
<listitem><para>Returns the type of the geometry. Eg: LINESTRING, POLYGON, MULTIPOINT, etc.</para></listitem>
|
|
</varlistentry><varlistentry>
|
|
<term>X(geometry)</term>
|
|
<listitem><para>Find and return the X coordinate of the first point in the geometry. Return NULL if there is no point in the geometry.</para></listitem>
|
|
</varlistentry>
|
|
<varlistentry>
|
|
<term>Y(geometry)</term>
|
|
<listitem><para>Find and return the Y coordinate of the first point in the geometry. Return NULL if there is no point in the geometry.</para></listitem>
|
|
</varlistentry>
|
|
<varlistentry>
|
|
<term>Z(geometry)</term>
|
|
<listitem><para>Find and return the Z coordinate of the first point in the geometry. Return NULL if there is no point in the geometry.</para></listitem>
|
|
</varlistentry>
|
|
<varlistentry>
|
|
<term>NumPoints(geometry)</term>
|
|
<listitem><para>Find and return the number of points in the first linestring in the geometry. Return NULL if there is no linestring in the geometry.</para></listitem>
|
|
</varlistentry>
|
|
<varlistentry>
|
|
<term>PointN(geometry,integer)</term>
|
|
<listitem><para>Return the N'th point in the first linestring in the geometry. Return NULL if there is no linestring in the geometry.</para></listitem>
|
|
</varlistentry>
|
|
<varlistentry>
|
|
<term>ExteriorRing(geometry)</term>
|
|
<listitem><para>Return the exterior ring of the first polygon in the geometry. Return NULL if there is no polygon in the geometry.</para></listitem>
|
|
</varlistentry>
|
|
<varlistentry>
|
|
<term>NumInteriorRings(geometry)</term>
|
|
<listitem><para>Return the number of interior rings of the first polygon in the geometry. Return NULL if there is no polygon in the geometry.</para></listitem>
|
|
</varlistentry>
|
|
<varlistentry>
|
|
<term>InteriorRingN(geometry,integer)</term>
|
|
<listitem><para>Return the N'th interior ring of the first polygon in the geometry. Return NULL if there is no polygon in the geometry.</para></listitem>
|
|
</varlistentry>
|
|
<varlistentry>
|
|
<term>NumGeometries(geometry)</term>
|
|
<listitem><para>If geometry is a GEOMETRYCOLLECTION return the number of geometries, otherwise return NULL.</para></listitem>
|
|
</varlistentry>
|
|
<varlistentry>
|
|
<term>GeometryN(geometry)</term>
|
|
<listitem><para>Return the N'th geometry if the geometry is a GEOMETRYCOLLECTION. Otherwise, return NULL.</para></listitem>
|
|
</varlistentry>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
</variablelist>
|
|
</sect1>
|
|
<sect1><title>Other Functions</title><variablelist>
|
|
|
|
|
|
|
|
<varlistentry>
|
|
<term>A <& B</term>
|
|
<listitem><para>The "<&" operator returns true if A's bounding box
|
|
overlaps or is to the right of B's bounding box.</para>
|
|
</listitem>
|
|
</varlistentry>
|
|
<varlistentry>
|
|
<term>A &> B</term>
|
|
<listitem><para>The "&>" operator returns true if A's bounding box
|
|
overlaps or is to the left of B's bounding box.</para>
|
|
</listitem>
|
|
</varlistentry>
|
|
<varlistentry>
|
|
<term>A << B</term>
|
|
<listitem><para>The "<<" operator returns true if A's bounding box
|
|
is strictly to the right of B's bounding box.</para>
|
|
</listitem>
|
|
</varlistentry>
|
|
<varlistentry>
|
|
<term>A >> B</term>
|
|
<listitem><para>The ">>" operator returns true if A's bounding box
|
|
is strictly to the left of B's bounding box.</para>
|
|
</listitem>
|
|
</varlistentry>
|
|
<varlistentry>
|
|
<term>A ~= B</term>
|
|
<listitem><para>The "~=" operator is the "same as" operator. It tests
|
|
actual geometric equality of two features. So if A and B are the same feature,
|
|
vertex-by-vertex, the operator returns true.</para>
|
|
</listitem>
|
|
</varlistentry>
|
|
<varlistentry>
|
|
<term>A ~ B</term>
|
|
<listitem><para>The "~" operator returns true of A's bounding box is
|
|
completely contained by B's bounding box.</para>
|
|
</listitem>
|
|
</varlistentry>
|
|
<varlistentry>
|
|
<term>A && B</term>
|
|
<listitem><para>The "&&" operator is the "overlaps" operator. If
|
|
A's bounding boux overlaps B's bounding box the operator returns true.</para>
|
|
</listitem>
|
|
</varlistentry>
|
|
<varlistentry>
|
|
<term>area2d(geometry)</term>
|
|
<listitem><para>Returns the area of the geometry if it is a polygon or
|
|
multi-polygon.</para>
|
|
</listitem>
|
|
</varlistentry>
|
|
<varlistentry>
|
|
<term>asbinary(geometry,'NDR')</term>
|
|
<listitem><para>Returns the geometry in the OGC "well-known-binary" format,
|
|
using little-endian encoding. This is useful in binary cursors to pull data out
|
|
of the database without converting it to a string representation.</para>
|
|
</listitem>
|
|
</varlistentry><varlistentry>
|
|
<term>asbinary(geometry,'XDR')</term>
|
|
<listitem><para>Returns the geometry in the OGC "well-known-binary" format,
|
|
using big-endian encoding. This is useful in binary cursors to pull data out
|
|
of the database without converting it to a string representation.</para>
|
|
</listitem>
|
|
</varlistentry><varlistentry>
|
|
<term>box3d(geometry)</term>
|
|
<listitem><para>Returns a BOX3D representing the maximum extents of the geometry.</para>
|
|
</listitem>
|
|
</varlistentry><varlistentry>
|
|
<term>extent(geometry)</term>
|
|
<listitem><para>The extent() function is an "aggregate" function in the
|
|
terminology of PostgreSQL. That means that it operators on lists of data, in
|
|
the same way the sum() and mean() functions do. For example, "SELECT
|
|
EXTENT(GEOM) FROM GEOMTABLE" will return a BOX3D giving the maximum extend of
|
|
all features in the table. Similarly, "SELECT EXTENT(GEOM) FROM GEOMTABLE GROUP
|
|
BY CATEGORY" will return one extent result for each category.</para>
|
|
</listitem>
|
|
</varlistentry><varlistentry>
|
|
<term>force_collection(geometry)</term>
|
|
<listitem><para>Converts the geometry into a GEOMETRYCOLLECTION. This is useful for simplifying the WKB representation.</para>
|
|
</listitem>
|
|
</varlistentry><varlistentry>
|
|
<term>force_2d(geometry)</term>
|
|
<listitem><para>Forces the geometries into a "2-dimensional mode" so that
|
|
all output representations will only have the X and Y coordinates. This is
|
|
useful for force OGC-compliant output (since OGC only specifies 2-D
|
|
geometries).</para>
|
|
</listitem>
|
|
</varlistentry>
|
|
<varlistentry>
|
|
<term>force_3d(geometry)</term>
|
|
<listitem><para>Forces the geometries into a "3-dimensional mode" so that
|
|
all output representations will have the X, Y and Z coordinates.</para>
|
|
</listitem>
|
|
</varlistentry>
|
|
|
|
<varlistentry>
|
|
<term>length2d(geometry)</term>
|
|
<listitem><para>Returns the 23-dimensional length of the geometry if it is
|
|
a linestring or multi-linestring.</para>
|
|
</listitem>
|
|
</varlistentry><varlistentry>
|
|
<term>length3d(geometry)</term>
|
|
<listitem><para>Returns the 3-dimensional length of the geometry if it is a
|
|
linestring or multi-linestring.</para>
|
|
</listitem>
|
|
</varlistentry><varlistentry>
|
|
<term>length_spheroid(geometry,spheroid)</term>
|
|
<listitem><para>Calculates the length of of a geometry on an elipsoid. This is useful if the coordinates of the geometry are in latitude/longitude and a length is desired without reprojection. The elipsoid is a separate database type and can be constructed as follows:<literallayout>SPHEROID[<NAME>,<SEMI-MAJOR AXIS>,<INVERSE FLATTENING>]
|
|
Eg: SPHEROID["GRS_1980",6378137,298.257222101]</literallayout>An example calculation might look like this:<literallayout>SELECT length_spheroid(geometry_column,'SPHEROID["GRS_1980",6378137,298.257222101]') from geometry_table;</literallayout></para>
|
|
</listitem>
|
|
</varlistentry><varlistentry>
|
|
<term>length3d_spheroid(geometry,spheroid)</term>
|
|
<listitem><para>Calculates the length of of a geometry on an elipsoid, taking the elevation into account. This is just like length_spheroid except vertical coordinates (expressed in the same units as the spheroid axes) are used to calculate the extra distance vertical displacement adds.</para>
|
|
</listitem>
|
|
</varlistentry><varlistentry>
|
|
<term>mem_size(geometry)</term>
|
|
<listitem><para>Returns the amount of space (in bytes) the geometry takes.</para>
|
|
</listitem>
|
|
</varlistentry><varlistentry>
|
|
<term>npoints(geometry)</term>
|
|
<listitem><para>Returns the number of points in the geometry.</para>
|
|
</listitem>
|
|
</varlistentry>
|
|
<varlistentry>
|
|
<term>nrings(geometry)</term>
|
|
<listitem><para>If the geometry is a polygon or multi-polygon returns the
|
|
number of rings.</para>
|
|
</listitem>
|
|
</varlistentry><varlistentry>
|
|
<term>numb_sub_objects(geometry)</term>
|
|
<listitem><para>Returns the number of objects stored in the geometry. This
|
|
is useful for MULTI-geometries and GEOMETRYCOLLECTIONs.</para>
|
|
</listitem>
|
|
</varlistentry><varlistentry>
|
|
|
|
<term>perimeter2d(geometry)</term>
|
|
<listitem><para>Returns the 2-dimensional perimeter of the geometry, if it
|
|
is a polygon or multi-polygon.</para>
|
|
</listitem>
|
|
</varlistentry><varlistentry><term>perimeter3d(geometry)</term>
|
|
<listitem><para>Returns the 3-dimensional perimeter of the geometry, if it
|
|
is a polygon or multi-polygon.</para>
|
|
</listitem>
|
|
</varlistentry><varlistentry><term>point_inside_circle(geometry,float,float,float)</term>
|
|
<listitem><para>The syntax for this functions is point_inside_circle(<geometry>,<circle_center_x>,<circle_center_y>,<radius>). Returns the true if the geometry is a point and is inside the circle. Returns false otherwise.</para>
|
|
</listitem>
|
|
</varlistentry><varlistentry>
|
|
<term>summary(geometry)</term>
|
|
<listitem><para>Returns a text summary of the contents of the geometry.</para>
|
|
</listitem>
|
|
</varlistentry><varlistentry>
|
|
<term>translate(geometry,float8,float8,float8)</term>
|
|
<listitem><para>Translates the geometry to a new location using the numeric
|
|
parameters as offsets. Ie: translate(geom,X,Y,Z).</para>
|
|
</listitem>
|
|
</varlistentry><varlistentry>
|
|
<term>truly_inside(geometryA,geometryB)</term>
|
|
<listitem><para>Returns true if any part of A is within the bounding box
|
|
of B.</para>
|
|
</listitem>
|
|
</varlistentry></variablelist></sect1></chapter>
|
|
<chapter>
|
|
<title>Programming Information</title>
|
|
<para> The following sections provide information about the internal structure of the PostGIS spatial objects, and some information about certain design decisions.</para>
|
|
<sect1>
|
|
<title>Internal Object Representation</title>
|
|
<para>The internal representation information is useful to people who intend to write C functions to directly manipulate the backend objects at
|
|
the server. Reading <filename>postgis.h</filename> will give the exact type definitions used, and reading <filename>postgis.c</filename> will give some examples of how to
|
|
manipulate the objects.</para>
|
|
<sect2><title>Basic Structure</title><para>In the most basic sense, a GEOMETRY type is a list of sub-objects and a bounding volume. In an ideal world, the type should be defined
|
|
as: </para><programlisting>typedef struct
|
|
{
|
|
int32 size; // postgres variable-length type requirement
|
|
int32 type; // this type of geometry
|
|
bool is3d; // true if the points are 3d (only for output)
|
|
BOX3D bvol; // bounding volume of all the geo objects
|
|
int32 nobjs; // how many sub-objects in this object
|
|
int32 objType[1]; // type of object
|
|
int32 objOffset[1]; // offset (in bytes) into this structure where the object is located
|
|
char objData[1]; // store for actual objects
|
|
} GEOMETRY;</programlisting><para>However, because of the way PostgreSQL handles memory allocation for objects, the type is actually organized as:</para><programlisting>typedef struct
|
|
{
|
|
int32 size; // postgres variable-length type requirement
|
|
int32 type; // this type of geometry
|
|
bool is3d; // true if the points are 3d (only for output)
|
|
BOX3D bvol; // bounding volume of all the geo objects
|
|
int32 nobjs; // how many sub-objects in this object
|
|
char data[?]; //info about the sub-objects
|
|
} GEOMETRY;</programlisting><para>This is because a Postgres type is a just a chuck of memory - it cannot contain any pointers. In a normal program <varname>objType</varname> and
|
|
objData would be pointers to separate chunks of memory for these two lists (in fact, <varname>objData</varname> would likely be a list of pointers to other memory locations). That would look something like:</para><para>Since we cannot have pointers, the geometry structure actually has pseudo-pointers to its data portion, and looks like:
|
|
</para><para>Since we cannot have pointers, the geometry structure actually has pseudo-pointers to its data portion, and looks like:
|
|
</para><itemizedlist><listitem><para>The structure is assumed to start off double-aligned (see the SQL define type).</para></listitem><listitem><para>Inside the structure, <varname>bvol</varname> and all the objects are double-aligned (there is unused space between <varname>is3d</varname> and <varname>bvol</varname>, there could be
|
|
unused space between <varname>objOffset[n]</varname> and object 0, and there could be unused space between object i and object i+1.</para></listitem><listitem><para>The actual location of <varname>objType[0]</varname> is always <varname>geometry->objType[0]</varname>.</para></listitem><listitem><para>The actual location of <varname>objOffset[0]</varname> is <varname>sizeof(int32)*geometry->nobjs</varname> after objType[0]. </para></listitem><listitem><para>The actual location of object 0 is <varname>objOffset[0]</varname> bytes into the structure . </para></listitem><listitem><para>Object 0 is also <varname>sizeof(int32)*geometry->nobjs</varname> bytes after <varname>objOffset[0]</varname> (could be 4 more bytes along if this isn't
|
|
double-aligned) . </para></listitem><listitem><para>The actual location of object 1 is <varname>objOffset[1]</varname> bytes into the structure.
|
|
</para></listitem></itemizedlist></sect2><sect2><title>Usage</title><para>Normally, one will want to find the location of <varname>objOffset[0]</varname> so it is accessible:</para><programlisting>Int32 *offsets;
|
|
offsets = (int32 *) ( ((char *) &(geom1->objType[0] ))+ sizeof(int32) * geom1->nobjs );</programlisting><para>Next, one might want to get object i:</para><programlisting>Char *obji;
|
|
oi = (char *) geom1 +offsets[i] ;
|
|
typei= geom1->objType[i];</programlisting><para>Then one can re-cast the object:</para><programlisting>POLYGON3D *poly;
|
|
LINE3D *line;
|
|
POINT3D *point;
|
|
if (typei == POINTTYPE) //point
|
|
{
|
|
point = (LINE3D *) oi;
|
|
...
|
|
}
|
|
if (type1 == LINETYPE) //line
|
|
{
|
|
line = (LINE3D *) oi;
|
|
...
|
|
}
|
|
if (type1 == POLYGONTYPE) //POLYGON
|
|
{
|
|
poly = (POLYGON3D *) oi;
|
|
...
|
|
}</programlisting><para>What does everything mean? </para><variablelist><varlistentry><term>Size </term><listitem><para>The total size of the structure.</para></listitem></varlistentry><varlistentry><term>Type</term><listitem><para>Generic type for this Structure.</para><para>This is mostly used so output knows how the object was originally entered into the system. </para></listitem></varlistentry><varlistentry><term>Is3d</term><listitem><para>TRUE if and only if, when the object was created, any of the points were given in 3D.</para><para>For example,
|
|
|
|
MULTIPOINT(1 1,2 2) -> is3d = FALSE
|
|
|
|
MULTIPOINT(1 1, 2 2, 3 3 3) -> is3d = TRUE</para><para>Since POINT(1 1) is stored as a POINT(1 1 0), we use the is3d flag during output to give POINT( 1 1) or POINT(1 1 0). Its not
|
|
used for anything else.</para></listitem></varlistentry><varlistentry><term>Bvol</term><listitem><para>Bounding Volume for all the points in this geometry </para></listitem></varlistentry><varlistentry><term>nobjs</term><listitem><para>The number of point/line/polygons there are in this geometry.</para></listitem></varlistentry><varlistentry><term>objType[i]</term><listitem><para>The type of each of the sub-objects.</para><programlisting>#define POINTTYPE 1
|
|
#define LINETYPE 2
|
|
#define POLYGONTYPE 3</programlisting></listitem></varlistentry><varlistentry><term>objOffset[i]</term><listitem><para>Offset, in bytes, into the structure where the object i is actually stored.</para></listitem></varlistentry><varlistentry><term>objdata</term><listitem><para>Data block with all the objects stored in it.</para></listitem></varlistentry></variablelist></sect2><sect2><title>Input / Output</title><para>The geometry type tries really hard to consistently reproduce objects. For example, if you insert a MULTIPOINT(), you will get a
|
|
MULTIPOINT() when you select.
|
|
|
|
This is more difficult than you might expect because, internally, all the objects listed below are stored the same:
|
|
</para><itemizedlist><listitem><para>MULTIPOINT( 1 1, 2 2, 3 3)
|
|
|
|
|
|
|
|
</para></listitem><listitem><para>GEOMETRYCOLLECTION(POINT(1 1), POINT(2 2), POINT(3 3) )</para></listitem><listitem><para>GEOMETRYCOLLECTION(MULTIPOINT( 1 1, 2 2), POINT( 3 3) ) </para></listitem><listitem><para>GEOMETRYCOLLECTION(MULTIPOINT( 1 1, 2 2, 3 3))
|
|
|
|
</para></listitem></itemizedlist><para>Every GEOMETRY is a set (GEOMETRYCOLLECTION) of sub-objects (point, line, polygon). So, truthfully, the 2nd example is the best
|
|
OGC WKT of the internal structure.
|
|
|
|
</para><para>Therefore, if you start with a MULTI-geometry (MULTIPOINT, MULTILINE, MULTIPOLYGON), the type member will record that fact. On
|
|
output, it does a simple conversion: </para><itemizedlist><listitem><para>GEOMETRYCOLLECTION(POINT(1 1),POINT(2 2)) becomes MULTIPOINT(1 1 , 2 2)</para></listitem><listitem><para>GEOMETRYCOLLECTION(LINESTRING(1 1,2 2),LINESTRING(10 10, 20 20)) becomes MULTILINESTRING((1 1,2 2), (10
|
|
10, 20 20))</para></listitem><listitem><para>GEOMETRYCOLLECTION(POLYGON((0 0, 0 1, 1 1, 0 0)), POLYGON((0 0, 0 10, 10 10 , 0 0) ) becomes
|
|
MULTIPOLYGON( ((0 0, 0 1, 1 1, 0 0)), ((0 0, 0 10, 10 10 , 0 0))) </para></listitem></itemizedlist><para>Single point, line, or polygon objects are returned as single point, line or polygon objects.
|
|
|
|
Unfortunately, GEOMETRYCOLLECTIONS() are not always returned the same as they are entered. They are always returned as a set
|
|
of base types.
|
|
|
|
For example: </para><itemizedlist><listitem><para>GEOMETRYCOLLECTION(POINT(1 1),POINT(2 2),POINT(3 3)) becomes GEOMETRYCOLLECTION(POINT(1 1),POINT(2
|
|
2),POINT(3 3)) </para></listitem><listitem><para>GEOMETRYCOLLECTION(MULTIPOINT(1 1,2 2,3 3)) becomes GEOMETRYCOLLECTION(POINT(1 1),POINT(2 2),POINT(3 3)) </para></listitem></itemizedlist></sect2><sect2><title>Fundamental Geometry Types</title><para>Inside each GEOMETRY object there are only four different possible geomtry types, and the <varname>Type</varname> variable of the GEOMETRY controls how the collection will be presented to the outside world (as a GEOMETRYCOLLECTION or a MULTIPOLYGON, for example).</para><sect3><title>POINT3D</title><para>This is the base geometry used by all the other sub-points. Its simply the X,Y,Z location represented as a double-precision floating point
|
|
number. 2D locations are represented with the Z location set to 0.0. See above for a discussion on how to tell the difference between a
|
|
2D point, and 3D points with Z=0.0.</para><programlisting>typedef struct
|
|
{
|
|
double x,y,z;
|
|
} POINT3D;</programlisting></sect3><sect3><title>LINE3D</title><para>A lines is a set of connected points. The line moves from <varname>points[0]->points[1]->points[2]</varname>
|
|
</para><programlisting>typedef struct
|
|
{
|
|
int32 npoints; // how many points in the line
|
|
int32 junk; // double-word alignment
|
|
POINT3D points[1]; // array of actual points
|
|
} LINE3D;</programlisting></sect3><sect3><title>POLYGON3D</title><para>A polygon is a set of rings. Each ring is a closed line.
|
|
</para><itemizedlist><listitem><para>The first ring is the outer ring, the others are holes.</para></listitem><listitem><para><varname>Npoints[i]</varname> is the number of points in ring i.</para></listitem><listitem><para><varname>&points[0]</varname> is at <varname>&polygon->npoints[0] + sizeof(int32)*polygon->nrings</varname>.</para></listitem><listitem><para>Once you've calculated <varname>&points[0]</varname>, ensure it is double-aligned.</para></listitem></itemizedlist><para>For example: </para><programlisting>POINT3D *pts;
|
|
pts = (POINT3D *) ( (char *)&(poly->npoints[poly->nrings] ) );
|
|
if ((int32) pts1 != (((int32)pts1>>3)<<3) )
|
|
pts1 = (POINT3D *) ((char *) pts1 + 4);</programlisting><para>Therefore, </para><itemizedlist><listitem><para>Ring 0's points are <varname>points[0]</varname> to <varname>points[npoint[0]-1]</varname>.</para></listitem><listitem><para>Ring 1's points are from <varname>points[npoint[0]]</varname> to <varname>points[npoints[0] + npoints[1]-1]</varname>.</para></listitem></itemizedlist><programlisting>typedef struct
|
|
{
|
|
int32 nrings; // how many rings in this polygon
|
|
int32 npoints[1]; //how many points in each ring
|
|
/* could be 4 byes of filler here to make sure points[] is
|
|
double-word aligned*/
|
|
POINT3D points[1]; // array of actual points
|
|
} POLYGON3D;</programlisting></sect3><sect3><title>BOX3D</title><para>Representation of a long diagonal of a cube, from the lower-left bottom (LLB) to the upper-right top (URT). </para><itemizedlist><listitem><para>Minimum X is box3d->LLB.x</para></listitem><listitem><para>Minimum Y is box3d->LLB.y</para></listitem><listitem><para>Minimum Z is box3d->LLB.z</para></listitem><listitem><para>Maximum X is box3d->URT.x</para></listitem><listitem><para>Maximum Y is box3d->URT.y</para></listitem><listitem><para>Maximum Y is box3d->URT.z</para></listitem></itemizedlist><programlisting>typedef struct
|
|
{
|
|
POINT3D LLB,URT; /* corner POINT3Ds on long diagonal */
|
|
} BOX3D;</programlisting></sect3></sect2></sect1>
|
|
<sect1>
|
|
<title>Internal Functions</title>
|
|
<para>To be done.</para>
|
|
</sect1>
|
|
</chapter>
|
|
</book>
|