Top 5 Recent Articles
ARTICLES CATEGORIES
- Algorithms (22)
- All (399)
- Biography (1)
- Blog (44)
- Business Requirements (1)
- Commentary (1)
- Conversion (2)
- Customers (2)
- Data Models (1)
- Education (2)
- GeoRaptor (13)
- GPS (1)
- Image Processing (2)
- Import Export (8)
- Licensing (2)
- LiDAR (1)
- Linear Referencing (4)
- Manifold GIS (3)
- Mapping (1)
- MySQL Spatial (7)
- Networking and Routing (including Optimization) (5)
- Open Source (18)
- Oracle Spatial and Locator (194)
- Partitioning (1)
- PostGIS (36)
- Projections (1)
- Published Articles (1)
- qGIS (1)
- Recommendations (1)
- Services (1)
- Software Change Log (1)
- Source Code (37)
- Space Curves (9)
- Spatial Database Functions (109)
- Spatial DB comparison (1)
- Spatial XML Processing (11)
- SQL Server Spatial (92)
- Standards (3)
- Stored Procedure (17)
- Tessellation or Gridding (10)
- Tools (2)
- Topological Relationships (1)
- Training (2)
- Triangulation (2)
Implementing Oracle’s GetVertices function in PostGIS – ST_DumpPoints
The GetVertices function in the MDSYS.SDO_UTIL package in Oracle Spatial is an exceedingly useful function.
From the Oracle documentation of this function:
This function returns an object of VERTEX_SET_TYPE, which consists of a table of objects of VERTEX_TYPE. Oracle Spatial defines the type VERTEX_SET_TYPE as:
CREATE TYPE vertex_set_type as TABLE OF vertex_type;
Oracle Spatial defines the object type VERTEX_TYPE as:
CREATE TYPE vertex_type AS OBJECT
(x NUMBER,
y NUMBER,
z NUMBER,
w NUMBER,
id NUMBER);
The GetVertices function, because it returns a ‘table of objects’, must be used in a SQL SELECT statement via a TABLE function which will present the elemnts of the set to SQL as a table of records. A simple example:
SELECT v.*
FROM TABLE(mdsys.sdo_util.GetVertices(mdsys.sdo_geometry('POLYGON ((1 1,5 2,5 7,2 7,1 1))',NULL))) v;
X Y Z W ID
---------------------- ---------------------- ---------------------- ---------------------- ----------------------
1 1 1
5 2 2
5 7 3
2 7 4
1 1 5
5 rows selected
Now, as at PostgreSQL 8.3, there is no equivalent function in PostGIS, though there are some who are pushing for it to be implemented. In the meantime, as an exercise, I thought I would create a custom plpgsql function that does what GetVertices does.
Here is my coding of the function:
-- First create the required type
CREATE TYPE coordtype AS
(x double precision,
y double precision,
z double precision,
m double precision);
-- Now define the function
CREATE OR REPLACE FUNCTION ST_DumpPoints(p_geometry geometry)
RETURNS SETOF CoordType IMMUTABLE
AS $$
DECLARE
v_GeometryType varchar(1000);
v_rec RECORD;
v_point CoordType;
c_points CURSOR ( p_geom geometry,
p_Geometrytype text )
IS
SELECT ST_X(p_geom),ST_Y(p_geom),ST_Z(p_geom),ST_M(p_geom)
WHERE p_geometrytype = 'ST_Point'
UNION ALL
SELECT ST_X(p),ST_Y(p),ST_Z(p),ST_M(p)
FROM ( SELECT ST_GeometryN(p_geom,generate_series(1,ST_NumGeometries(p_geom))) as p
WHERE p_geometrytype = 'ST_MultiPoint'
) AS multipoint
UNION ALL
SELECT ST_X(sp),ST_Y(sp),ST_Z(sp),ST_M(sp)
FROM ( SELECT ST_PointN(p_geom, generate_series(1, ST_NPoints(p_geom))) as sp
WHERE ( p_GeometryType = 'ST_LineString' )
) AS linestring
UNION ALL
SELECT ST_X(sp),ST_Y(sp),ST_Z(sp),ST_M(sp)
FROM ( SELECT ST_PointN(b.geom, generate_series(1, ST_NPoints(b.geom))) as sp
FROM (SELECT ST_GeometryN(p_geom,generate_series(1,ST_NumGeometries(p_geom))) as geom
WHERE ( p_GeometryType = 'ST_MultiLineString' )
) as b
) AS multiline
UNION ALL
SELECT ST_X(sp),ST_Y(sp),ST_Z(sp),ST_M(sp)
FROM ( SELECT ST_PointN(a.geom, generate_series(1, ST_NPoints(a.geom))) as sp
FROM ( SELECT ST_ExteriorRing(p_geom) as geom
WHERE ( p_GeometryType = 'ST_Polygon' )
UNION ALL
SELECT ST_InteriorRingN(p_geom,generate_series(1,ST_NumInteriorRings(p_geom))) as geom
WHERE ( p_GeometryType = 'ST_Polygon' )
) a
) as polygon
UNION ALL
SELECT ST_X(sp),ST_Y(sp),ST_Z(sp),ST_M(sp)
FROM ( SELECT ST_PointN(a.geom, generate_series(1, ST_NPoints(a.geom))) as sp
FROM ( SELECT ST_ExteriorRing(b.geom) as geom
FROM (SELECT ST_GeometryN(p_geom,generate_series(1,ST_NumGeometries(p_geom))) as geom
WHERE ( p_GeometryType = 'ST_MultiPolygon' )
) b
UNION ALL
SELECT ST_interiorringn(c.geom,generate_series(1,ST_numinteriorrings(c.geom))) as geom
FROM (SELECT ST_GeometryN(p_geom,generate_series(1,ST_NumGeometries(p_geom))) as geom
WHERE ( p_GeometryType = 'ST_MultiPolygon' )
) c
) a
) as multipoly;
c_refcursor refcursor;
BEGIN
IF ( p_geometry is NULL ) THEN
return;
END IF;
v_GeometryType := ST_GeometryType(p_geometry);
IF ( v_GeometryType = 'ST_Geometry' ) THEN
-- Could be anything... use native PostGIS function
v_GeometryType = GeometryType(p_geometry);
IF ( v_geometryType = 'GEOMETRYCOLLECTION' ) THEN
FOR v_geom IN 1..ST_NumGeometries(p_geometry) LOOP
FOR v_rec IN SELECT * FROM ST_DumpPoints(ST_GeometryN(p_geometry,v_geom)) LOOP
RETURN NEXT v_rec;
END LOOP;
END LOOP;
ELSE
-- Probably CURVED something...
RETURN;
END IF;
END IF;
IF ( v_geometryType NOT IN ('ST_Geometry','GEOMETRYCOLLECTION') ) THEN
OPEN c_points(p_geometry,v_Geometrytype);
c_refcursor := c_points;
LOOP
FETCH c_refcursor INTO
v_point.x, v_point.y, v_point.z, v_point.m;
EXIT WHEN NOT FOUND;
RETURN NEXT v_point;
END LOOP;
CLOSE c_refcursor;
END IF;
END;
$$ LANGUAGE 'plpgsql';
Firstly, I create a special coordinate type for use in the function (note that I have not added an ID field). I could have had the function return all its data as ST_Point geometries but I elected not to do so. One could create a simple overloaded function that did this if that is what is required.
Note that I do all the “heavy lifting” in a single SQL statement that will only execute one of the many SELECT statements if the geometry object is of the right type. This allows me to not have a whole series of IF statements in the body of the function that execute specific, separate, SQL statements depending on geometry type. This is my particular coding “style” – I do not claim it to be the best way, just my way!
Finally, I haven’t supported circular curves in geometries as the PostGIS API for accessing them is still very immature. At some stage I will do so.
Here are some tests:
select * from ST_DumpPoints('POINT(1 2 3 4)'::geometry) as p;
| x | y | z | m |
| 1 | 2 | 3 | 4 |
select * from ST_DumpPoints('MULTIPOINT(0 0 0, 1 1 1, 2 2 2, 3 3 3)') as p;
| x | y | z | m |
| 0 | 0 | 0 | (null) |
| 1 | 1 | 1 | (null) |
| 2 | 2 | 2 | (null) |
| 3 | 3 | 3 | (null) |
select * from ST_DumpPoints('LINESTRING(0 0, 1 1, 2 2, 3 3)'::geometry) as p;
| x | y | z | m |
| 0 | 0 | (null) | (null) |
| 1 | 1 | (null) | (null) |
| 2 | 2 | (null) | (null) |
| 3 | 3 | (null) | (null) |
select * from ST_DumpPoints('MULTILINESTRING((0 0,1 1,1 2),(2 3,3 2,5 4))'::geometry) as p;
| x | y | z | m |
| 0 | 0 | (null) | (null) |
| 1 | 1 | (null) | (null) |
| 1 | 2 | (null) | (null) |
| 2 | 3 | (null) | (null) |
| 3 | 2 | (null) | (null) |
| 5 | 4 | (null) | (null) |
select * from ST_DumpPoints('POLYGON((326454.7 5455793.7,326621.3 5455813.7,326455.4 5455796.6,326454.7 5455793.7))'::geometry) as p;
| x | y | z | m |
| 326454.7 | 5455793.7 | (null) | (null) |
| 326621.3 | 5455813.7 | (null) | (null) |
| 326455.4 | 5455796.6 | (null) | (null) |
| 326454.7 | 5455793.7 | (null) | (null) |
select * from ST_DumpPoints('MULTIPOLYGON(((326454.7 5455793.7,326621.3 5455813.7,326455.4 5455796.6,326454.7 5455793.7)),((326771.6 5455831.6,326924.1 5455849.9,326901.9 5455874.2,326900.7 5455875.8,326888.9 5455867.3,326866 5455853.1,326862 5455851.2,326847.4 5455845.8,326827.7 5455841.2,326771.6 5455831.6)))'::geometry) as p;
| x | y | z | m |
| 326454.7 | 5455793.7 | (null) | (null) |
| 326621.3 | 5455813.7 | (null) | (null) |
| 326455.4 | 5455796.6 | (null) | (null) |
| 326454.7 | 5455793.7 | (null) | (null) |
| 326771.6 | 5455831.6 | (null) | (null) |
| 326924.1 | 5455849.9 | (null) | (null) |
| 326901.9 | 5455874.2 | (null) | (null) |
| 326900.7 | 5455875.8 | (null) | (null) |
| 326888.9 | 5455867.3 | (null) | (null) |
| 326866 | 5455853.1 | (null) | (null) |
| 326862 | 5455851.2 | (null) | (null) |
| 326847.4 | 5455845.8 | (null) | (null) |
| 326827.7 | 5455841.2 | (null) | (null) |
| 326771.6 | 5455831.6 | (null) | (null) |
select * from ST_DumpPoints('GEOMETRYCOLLECTION(POINT(2 3 4),LINESTRING(2 3 4,3 4 5))'::geometry) as p;
| x | y | z | m |
| 2 | 3 | 4 | (null) |
| 2 | 3 | 4 | (null) |
| 3 | 4 | 5 | (null) |
I hope this function is of use to someone.
Documentation
- GeoRaptor Documentation
- MySQL Spatial General Functions
- Oracle LRS Objects
- Oracle Spatial Exporter (Java + pl/SQL)
- Oracle Spatial Object Functions
- Oracle Spatial Object Functions (Multi Page)
- PostGIS pl/pgSQL Functions
- SC4O Oracle Java Topology Suite (Java + pl/SQL)
- SQL Server Spatial General TSQL Functions
- SQL Server Spatial LRS TSQL Functions