In the first chapter we gave you a brief taste of what PostGIS is and which basic geometries it supports. This chapter continues by explaining how PostGIS manages geometry data stored in the database. You’ll learn about those tables in all PostGIS-enabled databases that provide an inventory of the geometry table columns and of the available spatial reference systems. We then show you the definition and characteristics of points, linestrings, and polygons and how to work with them in a PostGIS-enabled database. After covering these single geometries, we move on to geometries that are made up of collections of single geometries: multipoints, multi-linestrings, multipolygons, and geometrycollections. We then demonstrate creating the less-commonly used curved geometries and 3D geometries and outline the issues to consider when using these less-common geometry types.
PostGIS extends PostgreSQL by introducing a data type called geometry. Most of the functions that come packaged with PostGIS work with the core set of geometry types (points, linestrings, polygons, and their multi counterparts), some are specific to linestrings such as the linear referencing functions, some ignore the third and fourth coordinates, and some reject curved geometries or don’t work well with geometrycollections. For all intents and purposes, you can treat geometry on a par with other PostgreSQL data types such as dates, numbers, and text.
PostgreSQL does have its own built-in geometry data types. These are incompatible with the PostGIS geometry data type and have little or no third-party visualization support. These geometry types have existed since the dawn of PostgreSQL and don’t follow the OpenGIS Consortium standards, nor do they support spatial coordinate systems. These types are divided into individual types called point, polygon, lseg, box, circle, and path. The built-in PostgreSQL box type and the PostGIS box2d support type have similar names but are different, incompatible data types.
PostGIS uses a table named geometry_columns to store metadata associated with the geometry columns in the database. The installation of PostGIS automatically creates this table. The geometry_columns table provides housekeeping information about geometry columns in the database and is commonly used by third-party tools to gather a list of geometry layers in the database. All other OGC-compliant spatial databases have a table with a similar or the same name, because this table is defined in the OGC Simple Features for SQL (SF SQL) specs.
Geometry columns in a spatial table are often referred to as layers or feature classes when displayed in mapping applications.
As of PostGIS 1.4, the geometry_columns table is made up of seven columns. Four of these—f_table_catalog, f_table_schema, f_table_name, and f_geometry_column—are used to store the name of the database (also known as the catalog), table schema, table name, and geometry column name. The three final columns merit more discussion: coord_dimension, SRID, and type.
This is the coordinate dimension of the geometry column; permissible values are 2, 3, and 4. Yes, PostGIS supports up to four dimensions. The fourth dimension is non-spatial and often referred to as the M coordinate (the M stands for “measure”). All of the geometry manipulation features supported by PostGIS treat the fourth dimension as an extra attribute of a point in the geometry rather than as another spatial dimension. The fourth dimension can be a temporal dimension, but you could use it as an index for anything. We’ll talk more about this M coordinate when we get to points.
In spatial speak, there are two kinds of dimensions.
The coordinate dimension defines the number of axes you have. For example, geometries that occupy X, Y, Z or X, Y, M have a coordinate dimension of 3. Those that have X, Y, Z, M have a coordinate dimension of 4.
The second type of dimension is the geometry type dimension. The geometry type dimension can never be greater than the coordinate dimension. A point and multipoint, regardless of what coordinate dimension they have, always have a geometric dimension of 0. A linestring and multilinestring are one dimensional, and a polygon and multipolygon are two dimensional. You’ll notice that we have no three-dimensional geometry types. Such types would be volumetric surfaces such as boxes and spheres and amorphous objects you’d find in real 3D space. These aren’t supported in PostGIS 1.* versions, but in PostGIS 2+, these will be supported in new types called polyhedral surfaces and triangulated irregular network (TIN).
SRID stands for spatial reference identifier and is an integer that relates back to the primary key of the metatable spatial_ref_sys. PostGIS uses this table to catalog all the spatial reference systems available to the database. The spatial_ref_sys metatable contains the name of the spatial reference system, the parameters needed to reproject to another system, and by which authority the system has been defined.
In common GIS lingo, there’s another term with similar meaning called SRS ID (spatial reference system identifier), which is usually represented as the authority name plus the unique identifier used by the authority for the spatial reference system.
For example, the common WGS 84 lon lat has an SRS ID of EPSG:4326, where EPSG stands for European Petroleum Survey Group (www.epsg.org). Most of the spatial reference systems defined in PostGIS are from EPSG, so the SRID used in the table is usually the same as the EPSG identifier. This isn’t the case with all spatial databases, and the same spatial reference system can go under multiple identifiers.
Keep in mind that using a different SRID doesn’t change the fact that the coordinate system underlying PostGIS is rectangular Cartesian. This fact comes to prominence when we start to deal with geographical features where the curvature of the earth comes into play. Suppose we’re trying to measure the distance between two points that represent New York and Los Angeles using the PostGIS function ST_Distance(). Because PostGIS is based on a regular X-Y coordinate system, ST_Distance() will return the distance calculated using the Pythagorean theorem, not the great circle distance that you might expect. Even if we were to use spherical coordinates like latitudes and longitudes to map our features, the underlying calculations would treat these as planar. So to correctly calculate distances when your data is stored in spherical coordinates, you need to first transform to a planar-based spatial reference system or use the PostGIS geography data type introduced in PostGIS 1.5 to store lon lat data. The PostGIS geography data type has similar functions for inserting data and uses the same text representations, except that all coordinates are input and expressed in WGS 84 lon lat, measurements are always in units of meters/square meters, there are fewer functions, and it uses a view called geography_columns that’s always in sync with the tables. We’ll briefly cover this type in later chapters of this book.
The default SRID in pre-2.0 versions of PostGIS is -1 to represent the unknown SRID. Should you use the unknown SRID? The answer is no if you’re working with geographic data. If you know the spatial reference system of your data, and presumably you should if you have real geographic data, then you should explicitly specify it. If you’re using PostGIS for non-geographical purposes, such as modeling a localized architecture plan or demonstrating analytic geometry principles, it’s perfectly fine keeping your spatial reference as unknown.
In OGC, the unknown spatial reference system is 0 instead of -1. Future versions of PostGIS after 1.5 will use the more standard 0 to represent the unknown spatial reference system. For most functions that requires SRID (minus ST_Transform), you can leave out the SRID if you wish the spatial reference system to be treated as unknown.
You should also know that even though the spatial_ref_sys table has close to 4,000 entries, you’ll encounter plenty of instances where you have to add SRIDs not already in the table. You can also be adventurous and define your own custom spatial reference system and add it to the spatial_ref_sys table in any PostGIS database.
This final column stores the geometry type as a varying character field—‘POINT’, ‘LINESTRING’, ‘POLYGON’, ‘MULTIPOLYGON’, and others. Another admissible data value here is ‘GEOMETRY’. ‘GEOMETRY’ defines a heterogeneous geometry column that can store any geometry type.
The final admonition we need to make about the geometry_columns table is that in pre-2.0 versions of PostGIS, it’s for informational purposes only. Manipulating the values in the table has no bearing whatsoever on the actual geometry column referred to by the table. For example, you can start by creating a column to be two-dimensional polygons. You can even insert a few rows of polygons into the new column, but should you return to the geometry_columns table and change the type from polygons to linestrings, your data won’t be revalidated. You’ll end up with metadata that’s out of sync with the actual data. For this reason, we recommend that you not edit the geometry_ columns table directly.
To avoid editing the records directly in the metadata tables, PostGIS offers five functions, which, when used, will handle any necessary interactions with the geometry_columns table:
Of the five functions described, only Populate_Geometry_Columns can be used to register views in the PostGIS geometry_columns table; however, you’re still free to register these and other tables manually by directly inserting into geometry_columns.
In all of our examples thus far, we used the AddGeometryColumn function to handle the creation of new geometry columns. Although you don’t need to use these functions for creating and maintaining the geometry columns, for pre-2.0 versions of PostGIS we highly recommend doing so because the functions will automatically register and maintain entries in the geometry_columns table. To demonstrate the advantage of using the maintenance functions, we’ll add a new geometry column of points to a table without using the AddGeometryColumn function. We’d need to go through the following steps to achieve the same result:
As you can see, using maintenance functions wherever possible greatly simplifies matters and reduces the likelihood of you forgetting a step. Now that you have a general idea of the supporting structures and maintenance functions PostGIS offers for geometries, we’ll explore what kinds of geometries PostGIS offers and how to create and add them to your database.
PostGIS has a large variety of geometry types to choose from to help you model the real world or, for that matter, anything you can think of that involves shapes. In this section we’ll explore the geometric data types in PostGIS in detail. We’ll concentrate on the defining attributes of geometries, addressing questions such as “What makes a linestring a linestring?”
In this book, we use the term geometry to both indicate the general idea of a geometric shape as used in GIS and to mean PostGIS geometric data types.
PostGIS geometric data types follow the OpenGIS standard geometry definitions. This compliance allows you to apply the knowledge you learn in working with one spatial database to another so long as they’re both generally OGC compliant.
The best way to think about a geometry data type is to draw an analogy to numeric data types. In general, setting up a column in a data table and declaring that it will store numeric data isn’t precise enough. You have to go further and must make distinctions between floating point and fixed numbers. With fixed numbers, you can specify more attributes, like the number of places after the decimal point or whether a column will be signed or unsigned. Like numeric data types, a geometry data type is akin to a base data type from which you can derive more specific data types. At the root of geometry data types, you find points, linestrings, polygons, and curves, each with its own defining attributes.
Let’s delve into some concrete examples. We start by creating a table to store all the geometries to be shown:
CREATE TABLE my_geometries (id serial NOT NULL PRIMARY KEY, name varchar(20));
For now, our table has nothing more than an autonumbered column (also known as a serial column in PostgreSQL parlance) and a column to store the name of the geometries. For the sake of brevity, we’re placing our new table in the default public schema. Without prefixing the table name, PostgreSQL follows the schema search path. Unless you’ve changed the search path order, the default search always starts with $user followed by the public schema. For production work involving databases with many tables, we strongly urge you to create your own schemas and to organize your tables around these schemas. Not only will you keep your tables in logical units, but you’ll find it easier when it comes time to upgrade PostGIS and for performing selective backups and restores.
All PostGIS geometries are based on the Cartesian coordinate system. A point in 2D coordinate space is specified by its X and Y coordinates. In 3D space, a point has X, Y, and Z coordinates; in 2DM space, a point has X, Y, and M coordinates and a PointM geometry (a PostGIS geometry type in its own right to distinguish it from points in 3D space). In 3DM space, a point has an X, Y, Z, and M coordinates. (In OGC nomenclature, this is often represented as a distinct data type called Point MZ, but PostGIS prior to the 2.0 series represents this as data type POINT with four coordinates.)
The M coordinate stands for “measure” and is an additional numeric double-precision value that can be stored for each point in geometry. It can be negative or positive, and its units need not have any relationship to the underlying spatial reference system of the geometry. There are two variants of such geometries: 2DM and 3DM. 2DM has X, Y, M and 3DM has X, Y, Z, M. You can use the measure coordinate to store additional information associated with the spatial coordinates. Scientific data often uses the extra variable to hold a measurement taken at the point, hence the term measure.
The benefit of using M to store additional information directly becomes clear as soon as you move beyond points. Suppose that you have a linestring made up of many points, each with its own measure. Without the M coordinate, you would always have to add an additional table that divides the linestring into points for the sake of storing the measure data.
Several functions in PostGIS (many of which are defined by the OGC SFS standard) deal specifically with M coordinate data. We’ll explore these in a later chapter.
The following call to the AddGeometryColumn function creates a new geometry point column, after which we add two pizza parlors and your home to our simple Cartesian world.
SELECT AddGeometryColumn('public','my_geometries', 'my_points',-1,'POINT',2); INSERT INTO my_geometries (name,my_points) VALUES ('Home',ST_GeomFromText('POINT(0 0)')); INSERT INTO my_geometries (name,my_points) VALUES ('Pizza 1',ST_GeomFromText('POINT(1 1)')) ; INSERT INTO my_geometries (name,my_points) VALUES ('Pizza 2',ST_GeomFromText('POINT(1 -1)'));
The code in listing 2.1 adds your home to the origin and two pizza parlors, one at (1,1) and one at (1,-1). Pull out a GIS desktop tool and you can see the three points, as shown in figure 2.1.
Linestrings are defined by at least two distinct points. Like points, there are four dimensional variants of linestrings: linestring with points represented with X, Y coordinates; linestrings with points in X, Y, Z coordinates; linestrings with points in X, Y, M coordinates (also known as a LINESTRINGM); and finally linestrings with points represented in X, Y, Z, M coordinates. Let’s use the following listing to add a simple 2D linestring column with two rows.
SELECT AddGeometryColumn ('public','my_geometries', 'my_linestrings',-1,'LINESTRING',2); INSERT INTO my_geometries (name,my_linestrings) VALUES ('Linestring Open', ST_GeomFromText('LINESTRING(0 0,1 1,1 -1)')); INSERT INTO my_geometries (name,my_linestrings) VALUES ('Linestring Closed', ST_GeomFromText('LINESTRING(0 0,1 1,1 -1, 0 0)'));
The first INSERT statement in listing 2.2 adds a linestring starting at the origin, going to (1,1) and terminating at (1,-1). This is an example of an open linestring where the starting and end points aren’t the same. The second INSERT statement adds a closed linestring. A closed linestring is a linestring where the starting and end points are the same. In modeling real-world geographic features, open linestrings predominate over closed linestrings. Rivers, streams, fault lines, and roads rarely start where they end. Closed linestrings, as you can see in figure 2.2, are the basis for constructing polygons.
The concept of simple and non-simple geometries comes into play when describing linestrings. A simple linestring can’t have self-intersections except at the starting and end points. A linestring that crosses itself isn’t simple. PostGIS provides a function, ST_IsSimple, that tests to see if a geometry is simple. The following query will return false:
SELECT ST_IsSimple(ST_GeomFromText('LINESTRING(2 0,0 0,1 1,1 -1)'));
The output of the SELECT is shown in figure 2.3.
Another important idea is that although a linestring is defined using a finite set of points, in reality you should think of it as being composed of an infinite number of points. This distinction becomes clear with questions relating to the closest point on a linestring to a polygon or other geometry. The closest point rarely coincides with any point used to define the linestring.
Now things get a little more interesting. We process from familiar geometries to form polygons. We start with a simple triangle: Take a closed linestring with at least three distinct points. This linestring takes the shape of a triangle, as shown in figure 2.4. All points enclosed by the linestring and the points on the linestring itself form the polygon. The closed linestring delineating the outer boundary of the polygon is called the ring of the polygon when used in this context; more specifically, it’s the exterior ring.
SELECT AddGeometryColumn('public','my_geometries', 'my_polygons',-1,'POLYGON',2); INSERT INTO my_geometries (name,my_polygons) VALUES ('Triangle', ST_GeomFromText('POLYGON((0 0, 1 1, 1 -1, 0 0))'));
Many polygons used in geographical modeling consist of a single ring, but polygons can also have multiple rings. To be precise, a polygon can have one exterior ring and zero or more inner rings. Each interior ring creates a hole in the overall polygon, as shown in figure 2.5. This is why we need the seemingly redundant set of parentheses in the text representation of polygons. The well-known text (WKT) of a polygon is a set of closed line strings, with the first being the exterior ring and all subsequent designating the inner rings.
INSERT INTO my_geometries (name,my_polygons) VALUES ('Square with 2 holes', ST_GeomFromText('POLYGON( (-0.25 -1.25,-0.25 1.25,2.5 1.25,2.5 -1.25,-0.25 -1.25), (2.25 0,1.25 1,1.25 -1,2.25 0),(1 -1,1 1,0 0,1 -1))'));
Always add the extra set of parentheses in the WKT, even if your polygon has just a single ring. Some tools may work with single-ringed polygons using only one set of parentheses without complaining, but not PostGIS.
In the real world, multiringed polygons play an important part in excluding bodies of water within geographical boundaries. For example, if we were planning a surface transit system in the greater Seattle area, we could start by outlining a big polygon bounded by Interstate 5 on the west and Interstate 405 on the east, as shown in figure 2.6. We could then start to pin down starting and terminal points of popular bus lines and let the computer choose the shortest path within the polygon. Soon enough, we’d realize that most of those popular routes are over water, Lake Washington to be specific. To have the computer pick routes correctly, our polygon of greater Seattle would need an inner ring outlining the shape of Lake Washington. This way, if we were to run a query asking for the shortest path between two points on the polygon and completely within the polygon, we wouldn’t end up with buses driving into the water.
With polygons we have the concept of validity. The rings of a valid polygon may only intersect at distinct points. What this means is that rings can’t overlap each other and that two rings can’t share a common boundary. A polygon whose inner rings partly lie outside its exterior ring is also invalid.
Figure 2.7 shows an example of a single polygon with self-intersections. (Visually, you can’t discern that it’s an invalid geometry because such a visual can be created with two valid polygons or one valid multipolygon that happens to be touching at a point.)
Not every invalid polygon lends itself to a pictorial representation. Degenerate polygons such as polygons with not enough points and polygons with non-closed rings are difficult to illustrate. Fortunately these polygons are difficult to generate in PostGIS and don’t serve any purpose in real-world modeling.
To demonstrate the concept of collection geometries, we ask you to mentally picture the 50 states of the United States as polygons. Interior rings allow us to handle states with large bodies of water within their boundaries, such as Utah (the Great Salt Lake), Florida (Lake Okeechobee), and Minnesota with its 10,000-plus lakes. There’s at least one state that our polygon has trouble handling: Hawaii. Hawaii comes in at least five big pieces. We could conceivably model Hawaii as five separate polygons, but this complicates our storage. For example, if we wanted to create a table of states, we’d expect to have 50 rows. Breaking states into different polygons would call for storing a state using a state-polygon table, where each state could have up to hundreds of geometries depending on how fractured the state is. We’d lose the simplicity associated with one geometry per state.
To overcome this problem, PostGIS and the OGC standard offer geometry collections as data types in their own right. A collection of geometries is just that. It groups separate geometries that logically belong together. With the use of collections, each of our 50 states becomes a collection of polygons—a multipolygon.
To give you a flavor of real-world GIS, we’ll look at state polygon data from the U.S. Census Bureau’s TIGER (Topologically Integrated Geographic Encoding and Referencing) data set. In the TIGER data set, only the following states are modeled as multi-polygons: Alaska, California, Hawaii, Florida, Kentucky, New York, and Rhode Island.
In reality more states are really multipolygons based on geography alone. Almost all states border large bodies of water and have detached islands. Because the census data is more concerned with people living in the state than its physical outline, it uses the political boundary of the state for its table of states. State political boundaries extend to adjacent bodies of water and stretch for a few miles into oceans. These more encompassing boundaries eliminate most states as multipolygons, leaving only the seven as multipolygons.
In PostGIS each of the single geometry data types has a collection counterpart: multi-points, multilinestrings, multipolygons, and multicurves. In addition, PostGIS has a data type called geometrycollection. This data type can contain any kind of geometry as long as all geometries in the set have the same spatial reference system and the same coordinate dimension.
We start with multipoints, which are nothing more than collections of points. Figure 2.8 shows an example of a multipoint.
In order to represent a multipoint in WKT syntax, you’d use one of the following. If you have only X, Y coordinates for a multipoint, each comma-delimited value would have two coordinates:
MULTIPOINT(-1 1, 0 0, 2 3) (pictured)
For a 3DM multipoints, those having X, Y, Z, and M, you’d have four coordinates:
MULTIPOINT(-1 1 3 4, 0 0 1 2, 2 3 1 2)
For a regular 3D multipoint composed of (X, Y, Z), you’d have the following:
MULTIPOINT(-1 1 3, 0 0 1, 2 3 1)
For a multipoint where each point is composed of X, Y, M, you’d use MULTIPOINTM to distinguish it from an X, Y, Z multipoint:
MULTIPOINTM(-1 1 4, 0 0 2, 2 3 2)
An alternate and acceptable WKT representation for multipoint uses parentheses to separate each point, for example: MULTIPOINT ((-1 1), (0 0), (2 3)). PostGIS will accept this format as input but will output the non-parenthetical version in the ST_AsText and ST_AsEWKT functions.
We included MULTIPOINTM in our listings to remind you that all of the geometry collection data types have the M dimension type just like their single-geometry counterparts.
Of no surprise, a multilinestring is a collection of linestrings. Be mindful of the extra sets of parentheses in the WKT representation of a multilinestring that separate each individual linestring in the set. The following examples of multi-linestring are shown in figure 2.9.
MULTILINESTRING((0 0,0 1,1 1),(-1 1,-1 -1)) MULTILINESTRING((0 0 1 1,0 1 1 2,1 1 1 3),(-1 1 1 1,-1 -1 1 2)) MULTILINESTRINGM((0 0 1,0 1 2,1 1 3),(-1 1 1,-1 -1 2))
Before moving on to multipolygons, let’s return to the concept of simplicity. In section 2.2.3, we tested a linestring for simplicity. Simplicity is relevant for all one-dimensional linestring type geometries. We consider multilinestrings simple if all constituent linestrings are simple and the collective set of linestrings doesn’t intersect each other at any point except boundary points. For example, if we create a multilinestring with two intersecting simple linestrings, the resultant multilinestring isn’t simple.
Be careful! The WKT of multipolygons has even more parentheses than its singular counterpart. Because we use parentheses to represent each ring of a polygon, we’ll need another set of outer parentheses to represent multipolygons. With multipolygons, we highly recommend that you follow the PostGIS conventions and don’t omit any inner parentheses for single-ringed polygons. Following are some examples of multipolygons, the first of which is shown in figure 2.10:
MULTIPOLYGON(((2.25 0,1.25 1,1.25 -1,2.25 0)), ((1 -1,1 1,0 0,1 -1))) (pictured in figure 2.10) MULTIPOLYGON(((2.25 0 1,1.25 1 1,1.25 -1 1,2.25 0 1)), ((1 -1 2,1 1 2,0 0 2,1 -1 2)) ) MULTIPOLYGON(((2.25 0 1 1,1.25 1 1 2,1.25 -1 1 1,2.25 0 1 1)), ((1 -1 2 1,1 1 2 2,0 0 2 3,1 -1 2 4)) MULTIPOLYGONM(((2.25 0 1,1.25 1 2,1.25 -1 1,2.25 0 1)), ((1 -1 1,1 1 2,0 0 3,1 -1 4)) )
Recall from the discussion on single polygons that a polygon is considered valid if all its rings don’t intersect or intersect only at distinct points. For a multipolygon to qualify as valid, it must pass two tests:
Geometrycollection is a PostGIS data type that can contain heterogeneous geometries. Unlike multigeometries where the constituent geometries must be of the same type, the geometrycollection data type can include points, linestrings, polygons, and their collection counterparts. It can even contain other geometrycollections. In short, you can cram every geometry type known to PostGIS into a geometrycollection.
In listing 2.3, we present the WKT for geometrycollections, but instead of just showing you the WKTs, we include the SQL that generates them. We do this for a reason: In real-world applications, you should rarely define a data column as geometrycollection. Although having a heterogeneous collection is perfectly reasonable for storage purposes, most PostGIS functions won’t make any sense when used with these data types. For example, you can ask what the area is of a multipolygon, but you can’t ask what the area is of a geometrycollection that has linestrings and points in addition to polygons. Geometrycollections generally originate as a result of queries rather than as predefined columns. You should be prepared when you have to work with them, but avoid using them in your table design. The next listing shows you the result of union queries that generate geometrycollections.
The output of the first geometrycollection of listing 2.3 is shown in figure 2.11. The output of the multim geometry would look the same, except that the M coordinate has no visual representation.
In this example we use ST_AsEWKT and ST_GeomFromEWKT to define our geometrycollection with an M coordinate. (The E stands for “extended.”) The reason for that is that the OGC-compliant functions of ST_AsText and ST_GeomFromText aren’t designed for anything above 2D, whereas ST_GeomFromEWKT and ST_AsEWKT are PostGIS constructs and can be used to display and create geometries of all dimensions. The other benefit of ST_AsEWKT over ST_AsText is that ST_AsEWKT will also return the spatial reference system if it’s known. The distinction between the two sets of functions may change in later versions of PostGIS.
Finally, a geometry collection is considered valid if all the geometries in the collection are valid. It’s invalid if any of the geometries in the collection are invalid.
PostGIS 1.3 and above have rudimentary support for curved geometries. If you plan to use curved geometries, you definitely should use the latest PostGIS release. Curved geometries were introduced in the OGC SQL-MM Part 3 specs, and PostGIS has partial support of what’s defined in the specs.
Curved geometries aren’t as mature as other geometries and aren’t widely supported. Natural terrestrial features rarely manifest themselves as curved geometries. Manmade structures and boundaries do have curves, but for many modeling cases, these structures can be adequately approximated with lines. Aeronautical charts are full of them because the sweep of radar is circular. Dams, dikes, and breakwaters are other curved macro structures that come to mind. Some highways segments come close to being curves, but linestrings are often more appropriate in modeling them, certainly when processing speed is more important than accuracy. Because of lack of support, we offer the following caveats before you decide to go down the path of using curved geometries:
Given all the drawbacks of curved geometries, you might be wondering why you’d ever want to use them. Here are a few reasons:
Let’s now take a closer look at the large variety of curved geometries. For simplicity, you can think of curved geometries in PostGIS as geometries with arcs. To build an arc, you must have exactly three distinct points. The first and last points denote the starting and end points of the arc, respectively. The point in the middle is called the control point because this point controls the degree of curvature of the arc.
A series of one or more arcs where the end point of one is the starting point of another makes up a geometry called a circularstring. Figure 2.12 is a diagram of a five-point circularstring.
This is a commonly asked question for people wanting to use curves. The short answer is no, but there has been talk of introducing such support in later versions once the basic SQL-MM circularly interpolated curved geometry support is complete. For complete support, PostGIS must be able to instantiate all the different types defined in SQL-MM Part 3 and most of the relation and processing functions that can work with them.
The circularstring is the simplest of all curved geometries and contains only arcs. The following listing contains more examples of circularstrings and how you’d register them in the database.
SELECT AddGeometryColumn ('public','my_geometries', 'my_circular_strings',-1,'CIRCULARSTRING',2); INSERT INTO my_geometries(name,my_circular_strings) VALUES ('Circle', ST_GeomFromText('CIRCULARSTRING(0 0,2 0, 2 2, 0 2, 0 0)')), ('Half Circle', ST_GeomFromText('CIRCULARSTRING(2.5 2.5,4.5 2.5, 4.5 4.5)')), ('Several Arcs', ST_GeomFromText('CIRCULARSTRING(5 5,6 6,4 8, 7 9, 9.5 9.5, 11 12, 12 12)'));
The output of listing 2.4 is shown in figure 2.13.
You’ll discover that not all rendering tools can handle curve geometries. In these instances, the ST_CurveToLine function comes in handy for approximating curved geometries with linestrings. As the following code and table 2.1 demonstrate, to achieve a reasonable degree of curvature, the linestring contains many segments:
Name |
cnpoints |
lnpoints |
---|---|---|
Circle | 5 | 129 |
Half Circle | 3 | 65 |
Several Arcs | 7 | 113 |
SELECT name,ST_NPoints(my_circular_strings) As cnpoints, ST_NPoints(ST_CurveToLine(my_circular_strings)) As lnpoints FROM my_geometries WHERE my_circular_strings IS NOT NULL;
Circularstrings and linestrings in series make up a collection geometry called compoundcurves. A polygon constructed from a compoundcurve is called a curvepolygon. A square with rounded corners is a nice representation of a closed compoundcurve with four circular strings and four straight linestrings. A compoundcurve is a geometry composed of both a circularstring and regular linestring segments, where the last point in the prior segment is the first point of the next segment. So, for example, if the last point of a circularstring is (10 12), then the first point of the linestring that follows would be (10 12). Following is an example of a compoundcurve composed of an arc sandwiched between two linestrings. The output is shown in figure 2.14.
SELECT AddGeometryColumn ('public','my_geometries','my_compound_curves', -1,'COMPOUNDCURVE',2); INSERT INTO my_geometries(name,my_compound_curves) VALUES ('Road with curve', ST_GeomFromText('COMPOUNDCURVE((2 2, 2.5 2.5), CIRCULARSTRING(2.5 2.5,4.5 2.5, 3.5 3.5), (3.5 3.5, 2.5 4.5, 3 5))'));
A curvepolygon is a polygon that has an exterior or inner ring with circularstrings. In pre-1.4 PostGIS versions, compondcurves can’t be used to form rings, even though SQL-MM specs allow for such curvepolygons. The following listing and figure 2.15 show some examples of curvepolygons.
SELECT AddGeometryColumn ('public','my_geometries', 'my_curve_polygons',-1,'CURVEPOLYGON',2); INSERT INTO my_geometries(name,my_curve_polygons) VALUES ('Solid Circle', ST_GeomFromText('CURVEPOLYGON( CIRCULARSTRING(0 0,2 0, 2 2, 0 2, 0 0))')), ('Circle t hole', ST_GeomFromText('CURVEPOLYGON(CIRCULARSTRING(2.5 2.5,4.5 2.5, 4.5 3.5, 2.5 4.5, 2.5 2.5), (3.5 3.5, 3.25 2.25, 4.25 3.25, 3.5 3.5) )') ), ('T arcish hole', ST_GeomFromText('CURVEPOLYGON((-0.5 7, -1 5, 3.5 5.25, -0.5 7), CIRCULARSTRING(0.25 5.5, -0.25 6.5, -0.5 5.75, 0 5.75, 0.25 5.5))'));
PostGIS 1.4 introduced support for compoundcurves as rings in a curvepolygon. Following is the WKT of a curvepolygon with a compoundcurve outer ring and a circular-string inner ring:
CURVEPOLYGON(COMPOUNDCURVE(CIRCULARSTRING(0 0,2 0, 2 1, 2 3, 4 3), (4 3, 4 5, 1 4, 0 0)), CIRCULARSTRING(1.7 1, 1.7 0.9, 1.6 0.5, 1.4 0.6, 1.7 1))
The output of the curve polygon is shown in figure 2.16.
PostGIS recognizes and stores 3D geometries, but full support is still spotty. As we’ve shown, you can easily create 3D points, linestrings, polygons, and curved geometries, but you must keep in mind that these lack the volumetric sense of 3D. They’re 2D objects sitting in 3D space (as such, they’re sometimes referred to as 2.5D). Be mindful of the following caveats when working with 3D geometries:
We began this chapter by introducing the geometry_columns metatable. Every PostGIS-enabled database has this metatable to catalog information about geometry columns in the database. We then introduced five functions that are commonly used to interact with the geometry_columns metatable, with AddGeometryColumn being the most prominent of these functions. We advised strongly against editing the geometry_columns table directly.
We then moved on to the single geometries of points, linestrings, and polygons, giving examples of their WKT representation and the INSERT SQL statements to add them to geometry_columns. Combining single geometries creates collection geometries. We covered multipoints, multilinestrings, multipolygons, and geometrycollections.
We finished the chapter with discussions of curved and 3D geometries. Because these two families of geometries are much more complex and less supported in real-world GIS modeling, they don’t fully enjoy the support of third-party tools and are only partially supported by PostGIS itself.
We hope that this chapter will give you the necessary know-how to set up your PostGIS table and to use metatables in PostGIS to centralize data type management. You should have a good understanding of single geometries and the purpose of geometry collections. In the next chapter, we’ll apply what you learned in this chapter by exploring the various approaches to organize your data in a PostGIS database.
Finally, we want to remind you not to think too much about rigorous definitions when modeling. If it looks, feels, and smells like a polygon, treat it like a polygon; don’t worry about inner rings and outer rings unless you need to. Although PostGIS tries to follow standards and the standards try to follow solid mathematical foundations, you’ll invariably run into definitional inconsistencies. Don’t be put off by these; instead, focus on what you’re trying to accomplish using PostGIS.
18.191.208.12