Chapter 3. Frequently Asked Questions

1. What kind of geometric objects can I store?
2. How do I insert a GIS object into the database?
3. How do I construct a spatial query?
4. How do I speed up spatial queries on large tables?
5. How can I get my search to return things that really are inside the search box, not just overlapping bounding boxes?
6. Why aren't PostgreSQL R-Tree indexes supported?
7. Why should I use the AddGeometryColumn() function and all the other OpenGIS stuff?
8. What is the best way to find all objects with a radius of another object?
9. How do I perform a coordinate reprojection as part of a query?

1. What kind of geometric objects can I store?

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).

2. How do I insert a GIS object into the database?

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:

  CREATE TABLE gtest ( ID int4, NAME varchar(20) );
  SELECT AddGeometryColumn('dbname','gtest','geom',-1,'LINESTRING',2);

If the geometry column addition fails, you probably have not loaded the PostGIS functions and objects into this database. See the installation instructions.

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:

  INSERT INTO gtest (ID, NAME,	GEOM) 
    VALUES (1, 'First Geometry', GeometryFromText('LINESTRING(2 3,4 5,6 5,7 8)', -1));

For more information about other GIS objects, see the object reference.

To view your GIS data in the table:

  SELECT id, name,	AsText(geom) AS geom FROM gtest;

The return value should look something like this:

   id | name           | geom
  ----+----------------+-----------------------------
    1 | First Geometry |	LINESTRING(2 3,4 5,6 5,7 8) 
  (1 row)

3. How do I construct a spatial query?

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.

In order to do a spatial query with index support, you must use the "overlap operator" (&&) which uses the following important simplifying assumption: all features shall be represented by their bounding boxes.

We recognize that using bounding boxes to proxy for features is a limiting assumption, but it is an important one in providing spatial indexing capabilities. Commercial spatial databases use the same assumption -- bounding boxes are important to most indexing schemes.

The most important spatial operator from a user's perspective is the "&&" overlap operator, which tests whether one feature's bounding box overlaps that of another. An example of a spatial query using && is:

  SELECT id,name FROM GTEST WHERE GEOM && 'BOX3D(3 4,4	5)'::box3d

Note that the bounding box used for querying must be explicitly declared as a box3d using the "::box3d" casting operation.

4. How do I speed up spatial queries on large tables?

Fast queries on large tables is the raison d'etre of spatial databases (along with transaction support) so having a good index in important.

To build a spatial index on a table with a geometry column, use the "CREATE INDEX" function as follows:

  CREATE INDEX [indexname] ON [tablename]  
    USING GIST ( [geometrycolumn] gist_geometry_ops);

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.

Note: For PostgreSQL version 7.1.x, you can specifically request a "lossy" index by appending WITH (ISLOSSY) to the index creation command. For PostgreSQL 7.2.x and above all GiST indexes are assumed to be lossy. Lossy indexes uses a proxy object (in the spatial case, a bounding box) for building the index.

5. How can I get my search to return things that really are inside the search box, not just overlapping bounding boxes?

The '&&' operator only checks bounding box overlaps, but you can use the "truly_inside()" function to get only those feature which actually 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 only works for search boxes right now, not any arbitrary geometry):

  SELECT [COLUMN1],[COLUMN2],AsText([GEOMETRYCOLUMN])
    	FROM [TABLE] WHERE [GEOM_COLUMN] && [BOX3d] 
     AND	truly_inside([GEOM_COLUMN],[BOX3d]);

6. Why aren't PostgreSQL R-Tree indexes supported?

Early versions of PostGIS used the PostgreSQL R-Tree indexes. However, PostgreSQL R-Trees have been completely discarded since version 0.6, and spatial indexing is provided with an R-Tree-over-GiST scheme.

Our tests have shown search speed for native R-Tree and GiST to be comparable. Native PostgreSQL R-Trees have two limitations which make them undesirable for use with GIS features (note that these limitations are due to the current PostgreSQL native R-Tree implementation, not the R-Tree concept in general):

  • 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.

  • R-Tree indexes in PostgreSQL are not "null safe", so building an index on a geometry column which contains null geometries will fail.

7. Why should I use the AddGeometryColumn() function and all the other OpenGIS stuff?

If you do not want to use the OpenGIS support functions, you do not have to. Simply create tables as in older versions, defining your geometry columns in the CREATE statement. All your geometries will have SRIDs of -1, and the OpenGIS meta-data tables will not be filled in properly. For most current applications, this will not matter.

However, in the future it is likely that client software will use the meta-data tables to interrogate the database about available layers and projections before rendering data. An obvious early example is the Mapserver internet mapping software, which could be altered to interrogate the SPATIAL_REF_SYS table for projection information on the layers it is rendering.

For these reasons it is probably wise to learn and use the OpenGIS concepts from early on.

8. What is the best way to find all objects with a radius of another object?

To use the database most efficiently, it is best to do radius queries which combine the radius test with a bounding box test: the bounding box test uses the spatial index, giving fast access to a subset of data which the radius test is then applied to.

For example, to find all objects with 100 meters of POINT(1000 1000) the following query would work:

  SELECT * 
  FROM GEOTABLE 
  WHERE 
    GEOM && GeometryFromText('BOX3D(900 900,1100 1100)',-1)
  AND
  Distance(GeometryFromText('POINT(1000 1000)',-1),GEOM) < 100;

9. How do I perform a coordinate reprojection as part of a query?

To perform a reprojection, both the source and destination coordinate systems must be defined in the SPATIAL_REF_SYS table, and the geometries being reprojected must already have an SRID set on them. Once that is done, a reprojection is as simple as referring to the desired destination SRID.

  SELECT Transform(GEOM,4269) FROM GEOTABLE;