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