Chapter 6. Programming Information

Table of Contents
Internal Object Representation
Internal Functions

The following sections provide information about the internal structure of the PostGIS spatial objects, and some information about certain design decisions.

Internal Object Representation

The internal representation information is useful to people who intend to write C functions to directly manipulate the backend objects at the server. Reading postgis.h will give the exact type definitions used, and reading postgis.c will give some examples of how to manipulate the objects.

Basic Structure

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:

  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;

However, because of the way PostgreSQL handles memory allocation for objects, the type is actually organized as:

  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;

This is because a Postgres type is a just a chuck of memory - it cannot contain any pointers. In a normal program objType and objData would be pointers to separate chunks of memory for these two lists (in fact, objData would likely be a list of pointers to other memory locations). That would look something like:

Since we cannot have pointers, the geometry structure actually has pseudo-pointers to its data portion, and looks like:

Since we cannot have pointers, the geometry structure actually has pseudo-pointers to its data portion, and looks like:

  • The structure is assumed to start off double-aligned (see the SQL define type).

  • Inside the structure, bvol and all the objects are double-aligned (there is unused space between is3d and bvol, there could be unused space between objOffset[n] and object 0, and there could be unused space between object i and object i+1.

  • The actual location of objType[0] is always geometry->objType[0].

  • The actual location of objOffset[0] is sizeof(int32)*geometry->nobjs after objType[0].

  • The actual location of object 0 is objOffset[0] bytes into the structure .

  • Object 0 is also sizeof(int32)*geometry->nobjs bytes after objOffset[0] (could be 4 more bytes along if this isn't double-aligned) .

  • The actual location of object 1 is objOffset[1] bytes into the structure.

Usage

Normally, one will want to find the location of objOffset[0] so it is accessible:

  Int32 *offsets;
  offsets = (int32 *) ( ((char *) &(geom1->objType[0] ))+ sizeof(int32) * geom1->nobjs );

Next, one might want to get object i:

  Char *obji;
  oi = (char *) geom1 +offsets[i] ;
  typei= geom1->objType[i];

Then one can re-cast the object:

  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;
  ...
  }

What does everything mean?

Size

The total size of the structure.

Type

Generic type for this Structure.

This is mostly used so output knows how the object was originally entered into the system.

Is3d

TRUE if and only if, when the object was created, any of the points were given in 3D.

For example, MULTIPOINT(1 1,2 2) -> is3d = FALSE MULTIPOINT(1 1, 2 2, 3 3 3) -> is3d = TRUE

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.

Bvol

Bounding Volume for all the points in this geometry

nobjs

The number of point/line/polygons there are in this geometry.

objType[i]

The type of each of the sub-objects.

  #define POINTTYPE 1
  #define LINETYPE 2
  #define POLYGONTYPE 3
objOffset[i]

Offset, in bytes, into the structure where the object i is actually stored.

objdata

Data block with all the objects stored in it.

Input / Output

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:

  • MULTIPOINT( 1 1, 2 2, 3 3)

  • GEOMETRYCOLLECTION(POINT(1 1), POINT(2 2), POINT(3 3) )

  • GEOMETRYCOLLECTION(MULTIPOINT( 1 1, 2 2), POINT( 3 3) )

  • GEOMETRYCOLLECTION(MULTIPOINT( 1 1, 2 2, 3 3))

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.

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:

  • GEOMETRYCOLLECTION(POINT(1 1),POINT(2 2)) becomes MULTIPOINT(1 1 , 2 2)

  • GEOMETRYCOLLECTION(LINESTRING(1 1,2 2),LINESTRING(10 10, 20 20)) becomes MULTILINESTRING((1 1,2 2), (10 10, 20 20))

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

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:

  • GEOMETRYCOLLECTION(POINT(1 1),POINT(2 2),POINT(3 3)) becomes GEOMETRYCOLLECTION(POINT(1 1),POINT(2 2),POINT(3 3))

  • GEOMETRYCOLLECTION(MULTIPOINT(1 1,2 2,3 3)) becomes GEOMETRYCOLLECTION(POINT(1 1),POINT(2 2),POINT(3 3))

Fundamental Geometry Types

Inside each GEOMETRY object there are only four different possible geomtry types, and the Type variable of the GEOMETRY controls how the collection will be presented to the outside world (as a GEOMETRYCOLLECTION or a MULTIPOLYGON, for example).

POINT3D

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.

  typedef struct
  {
  double x,y,z;
  } POINT3D;

LINE3D

A lines is a set of connected points. The line moves from points[0]->points[1]->points[2]

  typedef struct
  {
  int32 npoints; // how many points in the line
  int32 junk; // double-word alignment
  POINT3D points[1]; // array of actual points
  } LINE3D;

POLYGON3D

A polygon is a set of rings. Each ring is a closed line.

  • The first ring is the outer ring, the others are holes.

  • Npoints[i] is the number of points in ring i.

  • &points[0] is at &polygon->npoints[0] + sizeof(int32)*polygon->nrings.

  • Once you've calculated &points[0], ensure it is double-aligned.

For example:

  POINT3D *pts;
  pts = (POINT3D *) ( (char *)&(poly->npoints[poly->nrings] ) );
  if ((int32) pts1 != (((int32)pts1>>3)<<3) )
  pts1 = (POINT3D *) ((char *) pts1 + 4);

Therefore,

  • Ring 0's points are points[0] to points[npoint[0]-1].

  • Ring 1's points are from points[npoint[0]] to points[npoints[0] + npoints[1]-1].

  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;

BOX3D

Representation of a long diagonal of a cube, from the lower-left bottom (LLB) to the upper-right top (URT).

  • Minimum X is box3d->LLB.x

  • Minimum Y is box3d->LLB.y

  • Minimum Z is box3d->LLB.z

  • Maximum X is box3d->URT.x

  • Maximum Y is box3d->URT.y

  • Maximum Y is box3d->URT.z

  typedef struct
  {
  POINT3D LLB,URT; /* corner POINT3Ds on long diagonal */
  } BOX3D;