From 7ccb5cc62bc9b9479b1c4d7304fc4a8a5e0d565e Mon Sep 17 00:00:00 2001 From: Paul Ramsey Date: Fri, 28 Sep 2012 17:03:46 +0000 Subject: [PATCH] Add casts from geometry::path, geometry::point, geometry::polygon, polygon::geometry, path::geometry, point::geometry to allow easier migration to PostGIS for folks who start with the Pg types. git-svn-id: http://svn.osgeo.org/postgis/trunk@10336 b70326c6-7e19-0410-871a-916f4a2858ee --- NEWS | 5 +- postgis/Makefile.in | 3 +- postgis/geometry_inout.c | 269 ++++++++++++++++++++++++++++++++++ postgis/postgis.sql.in.c | 43 ++++++ regress/out_geometry.sql | 11 ++ regress/out_geometry_expected | 7 + 6 files changed, 335 insertions(+), 3 deletions(-) diff --git a/NEWS b/NEWS index 3239521f3..55b42c4d6 100644 --- a/NEWS +++ b/NEWS @@ -24,10 +24,11 @@ PostGIS 2.1.0 (Bborie Park / UC Davis) - Added array variants of ST_SetValues() to set many pixel values of a band in one call (Bborie Park / UC Davis) - - #1643, Tiger Geocoder - Tiger 2011 loader (Regina Obe / Paragon Corporation) - Funded by Hunter Systems Group + - #1643, Tiger Geocoder - Tiger 2011 loader + (Regina Obe / Paragon Corporation) Funded by Hunter Systems Group - GEOMETRYCOLLECTION support for ST_MakeValid (Sandro Santilli / Vizzuality) - ST_PixelOfValue (Bborie Park / UC Davis) + - Casts to/from PostgreSQL geotypes (point/path/polygon). * Enhancements * - #823, tiger geocoder: Make loader_generate_script download portion less greedy diff --git a/postgis/Makefile.in b/postgis/Makefile.in index a0f4312b1..805a903f4 100644 --- a/postgis/Makefile.in +++ b/postgis/Makefile.in @@ -59,7 +59,8 @@ PG_OBJS= \ geography_estimate.o \ geography_measurement.o \ geography_measurement_trees.o \ - geometry_estimate.o + geometry_estimate.o \ + geometry_inout.o # Objects to build using PGXS OBJS=$(PG_OBJS) diff --git a/postgis/geometry_inout.c b/postgis/geometry_inout.c index e69de29bb..26a994fed 100644 --- a/postgis/geometry_inout.c +++ b/postgis/geometry_inout.c @@ -0,0 +1,269 @@ +#include "postgres.h" +#include "utils/geo_decls.h" + +#include "../postgis_config.h" + +#include "liblwgeom.h" /* For standard geometry types. */ +#include "lwgeom_pg.h" /* For debugging macros. */ + + +Datum geometry_to_point(PG_FUNCTION_ARGS); +Datum point_to_geometry(PG_FUNCTION_ARGS); +Datum geometry_to_path(PG_FUNCTION_ARGS); +Datum path_to_geometry(PG_FUNCTION_ARGS); +Datum geometry_to_polygon(PG_FUNCTION_ARGS); +Datum polygon_to_geometry(PG_FUNCTION_ARGS); + +/** +* Cast a PostgreSQL Point to a PostGIS geometry +*/ +PG_FUNCTION_INFO_V1(point_to_geometry); +Datum point_to_geometry(PG_FUNCTION_ARGS) +{ + Point *point; + LWPOINT *lwpoint; + GSERIALIZED *geom; + + POSTGIS_DEBUG(2, "point_to_geometry called"); + + if ( PG_ARGISNULL(0) ) + PG_RETURN_NULL(); + + point = PG_GETARG_POINT_P(0); + + if ( ! point ) + PG_RETURN_NULL(); + + lwpoint = lwpoint_make2d(SRID_UNKNOWN, point->x, point->y); + geom = geometry_serialize(lwpoint_as_lwgeom(lwpoint)); + lwpoint_free(lwpoint); + + PG_RETURN_POINTER(geom); +} + +/** +* Cast a PostGIS geometry to a PostgreSQL Point +*/ +PG_FUNCTION_INFO_V1(geometry_to_point); +Datum geometry_to_point(PG_FUNCTION_ARGS) +{ + Point *point; + LWGEOM *lwgeom; + LWPOINT *lwpoint; + GSERIALIZED *geom; + + POSTGIS_DEBUG(2, "geometry_to_point called"); + + if ( PG_ARGISNULL(0) ) + PG_RETURN_NULL(); + + geom = (GSERIALIZED *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0)); + + if ( gserialized_get_type(geom) != POINTTYPE ) + elog(ERROR, "geometry_to_point only accepts Points"); + + lwgeom = lwgeom_from_gserialized(geom); + + if ( lwgeom_is_empty(lwgeom) ) + PG_RETURN_NULL(); + + lwpoint = lwgeom_as_lwpoint(lwgeom); + + point = (Point*)palloc(sizeof(Point)); + point->x = lwpoint_get_x(lwpoint); + point->y = lwpoint_get_y(lwpoint); + + lwpoint_free(lwpoint); + PG_FREE_IF_COPY(geom,0); + + PG_RETURN_POINT_P(point); +} + +PG_FUNCTION_INFO_V1(geometry_to_path); +Datum geometry_to_path(PG_FUNCTION_ARGS) +{ + PATH *path; + LWLINE *lwline; + LWGEOM *lwgeom; + GSERIALIZED *geom; + POINTARRAY *pa; + int i; + POINT2D pt; + size_t size; + + POSTGIS_DEBUG(2, "geometry_to_path called"); + + if ( PG_ARGISNULL(0) ) + PG_RETURN_NULL(); + + geom = (GSERIALIZED *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0)); + + if ( gserialized_get_type(geom) != LINETYPE ) + elog(ERROR, "geometry_to_path only accepts LineStrings"); + + lwgeom = lwgeom_from_gserialized(geom); + if ( lwgeom_is_empty(lwgeom) ) + PG_RETURN_NULL(); + lwline = lwgeom_as_lwline(lwgeom); + + pa = lwline->points; + size = offsetof(PATH, p[0]) + sizeof(path->p[0]) * pa->npoints; + path = (PATH*)palloc(size); + SET_VARSIZE(path, size); + path->npts = pa->npoints; + path->closed = 0; + path->dummy = 0; + + for ( i = 0; i < pa->npoints; i++ ) + { + getPoint2d_p(pa, i, &pt); + (path->p[i]).x = pt.x; + (path->p[i]).y = pt.y; + } + + lwgeom_free(lwgeom); + PG_FREE_IF_COPY(geom,0); + + PG_RETURN_PATH_P(path); +} + + +PG_FUNCTION_INFO_V1(path_to_geometry); +Datum path_to_geometry(PG_FUNCTION_ARGS) +{ + PATH *path; + LWLINE *lwline; + POINTARRAY *pa; + GSERIALIZED *geom; + POINT4D pt; + Point p; + int i; + + POSTGIS_DEBUG(2, "path_to_geometry called"); + + if ( PG_ARGISNULL(0) ) + PG_RETURN_NULL(); + + path = PG_GETARG_PATH_P(0); + + if ( ! path ) + PG_RETURN_NULL(); + + pa = ptarray_construct_empty(0, 0, path->npts); + for ( i = 0; i < path->npts; i++ ) + { + p = path->p[i]; + pt.x = p.x; + pt.y = p.y; + ptarray_append_point(pa, &pt, LW_FALSE); + } + lwline = lwline_construct(SRID_UNKNOWN, NULL, pa); + geom = geometry_serialize(lwline_as_lwgeom(lwline)); + lwline_free(lwline); + + PG_RETURN_POINTER(geom); +} + +PG_FUNCTION_INFO_V1(geometry_to_polygon); +Datum geometry_to_polygon(PG_FUNCTION_ARGS) +{ + POLYGON *polygon; + LWPOLY *lwpoly; + LWGEOM *lwgeom; + GSERIALIZED *geom; + POINTARRAY *pa; + GBOX gbox; + int i; + size_t size; + + POSTGIS_DEBUG(2, "geometry_to_polygon called"); + + if ( PG_ARGISNULL(0) ) + PG_RETURN_NULL(); + + geom = (GSERIALIZED *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0)); + + if ( gserialized_get_type(geom) != POLYGONTYPE ) + elog(ERROR, "geometry_to_polygon only accepts Polygons"); + + lwgeom = lwgeom_from_gserialized(geom); + if ( lwgeom_is_empty(lwgeom) ) + PG_RETURN_NULL(); + lwpoly = lwgeom_as_lwpoly(lwgeom); + + pa = lwpoly->rings[0]; + + size = offsetof(POLYGON, p[0]) + sizeof(polygon->p[0]) * pa->npoints; + polygon = (POLYGON*)palloc0(size); /* zero any holes */ + SET_VARSIZE(polygon, size); + + polygon->npts = pa->npoints; + + lwgeom_calculate_gbox(lwgeom, &gbox); + polygon->boundbox.low.x = gbox.xmin; + polygon->boundbox.low.y = gbox.ymin; + polygon->boundbox.high.x = gbox.xmax; + polygon->boundbox.high.y = gbox.ymax; + + for ( i = 0; i < pa->npoints; i++ ) + { + POINT2D pt; + getPoint2d_p(pa, i, &pt); + (polygon->p[i]).x = pt.x; + (polygon->p[i]).y = pt.y; + } + + lwgeom_free(lwgeom); + PG_FREE_IF_COPY(geom,0); + + PG_RETURN_POLYGON_P(polygon); +} + + +PG_FUNCTION_INFO_V1(polygon_to_geometry); +Datum polygon_to_geometry(PG_FUNCTION_ARGS) +{ + POLYGON *polygon; + LWPOLY *lwpoly; + POINTARRAY *pa; + POINTARRAY **ppa; + GSERIALIZED *geom; + Point p; + int i = 0, unclosed = 0; + + POSTGIS_DEBUG(2, "polygon_to_geometry called"); + + if ( PG_ARGISNULL(0) ) + PG_RETURN_NULL(); + + polygon = PG_GETARG_POLYGON_P(0); + + if ( ! polygon ) + PG_RETURN_NULL(); + + /* Are first and last points different? If so we need to close this ring */ + if ( memcmp( polygon->p, polygon->p + polygon->npts - 1, sizeof(Point) ) ) + { + unclosed = 1; + } + + pa = ptarray_construct_empty(0, 0, polygon->npts + unclosed); + + for ( i = 0; i < (polygon->npts+unclosed); i++ ) + { + POINT4D pt; + p = polygon->p[i % polygon->npts]; + pt.x = p.x; + pt.y = p.y; + ptarray_append_point(pa, &pt, LW_FALSE); + } + + ppa = palloc(sizeof(POINTARRAY*)); + ppa[0] = pa; + lwpoly = lwpoly_construct(SRID_UNKNOWN, NULL, 1, ppa); + geom = geometry_serialize(lwpoly_as_lwgeom(lwpoly)); + lwpoly_free(lwpoly); + + PG_RETURN_POINTER(geom); +} + diff --git a/postgis/postgis.sql.in.c b/postgis/postgis.sql.in.c index 5494a01ed..788778074 100644 --- a/postgis/postgis.sql.in.c +++ b/postgis/postgis.sql.in.c @@ -113,6 +113,49 @@ CREATE OR REPLACE FUNCTION geometry(geometry, integer, boolean) CREATE CAST (geometry AS geometry) WITH FUNCTION geometry(geometry, integer, boolean) AS IMPLICIT; +-- Availability: 2.1.0 +CREATE OR REPLACE FUNCTION geometry(point) + RETURNS geometry + AS 'MODULE_PATHNAME','point_to_geometry' + LANGUAGE 'c' IMMUTABLE STRICT; + +-- Availability: 2.1.0 +CREATE OR REPLACE FUNCTION point(geometry) + RETURNS point + AS 'MODULE_PATHNAME','geometry_to_point' + LANGUAGE 'c' IMMUTABLE STRICT; + +-- Availability: 2.1.0 +CREATE OR REPLACE FUNCTION geometry(path) + RETURNS geometry + AS 'MODULE_PATHNAME','path_to_geometry' + LANGUAGE 'c' IMMUTABLE STRICT; + +-- Availability: 2.1.0 +CREATE OR REPLACE FUNCTION path(geometry) + RETURNS path + AS 'MODULE_PATHNAME','geometry_to_path' + LANGUAGE 'c' IMMUTABLE STRICT; + +-- Availability: 2.1.0 +CREATE OR REPLACE FUNCTION geometry(polygon) + RETURNS geometry + AS 'MODULE_PATHNAME','polygon_to_geometry' + LANGUAGE 'c' IMMUTABLE STRICT; + +-- Availability: 2.1.0 +CREATE OR REPLACE FUNCTION polygon(geometry) + RETURNS polygon + AS 'MODULE_PATHNAME','geometry_to_polygon' + LANGUAGE 'c' IMMUTABLE STRICT; + +CREATE CAST (geometry AS point) WITH FUNCTION point(geometry); +CREATE CAST (point AS geometry) WITH FUNCTION geometry(point); +CREATE CAST (geometry AS path) WITH FUNCTION path(geometry); +CREATE CAST (path AS geometry) WITH FUNCTION geometry(path); +CREATE CAST (geometry AS polygon) WITH FUNCTION polygon(geometry); +CREATE CAST (polygon AS geometry) WITH FUNCTION geometry(polygon); + ------------------------------------------------------------------- -- BOX3D TYPE -- Point coordinate data access diff --git a/regress/out_geometry.sql b/regress/out_geometry.sql index c9789ad62..f3fa4af66 100644 --- a/regress/out_geometry.sql +++ b/regress/out_geometry.sql @@ -145,6 +145,17 @@ SELECT 'geojson_options_14', ST_AsGeoJson(GeomFromEWKT('SRID=4326;LINESTRING(1 1 SELECT 'geojson_options_15', ST_AsGeoJson(GeomFromEWKT('SRID=0;LINESTRING(1 1, 2 2, 3 3, 4 4)'), 0, 7); SELECT 'geojson_options_16', ST_AsGeoJson(GeomFromEWKT('SRID=4326;LINESTRING(1 1, 2 2, 3 3, 4 4)'), 0, 7); +-- Out and in to PostgreSQL native geometric types +WITH p AS ( SELECT '((0,0),(0,1),(1,1),(1,0),(0,0))'::text AS p ) + SELECT 'pgcast_01', p = p::polygon::geometry::polygon::text FROM p; +WITH p AS ( SELECT '[(0,0),(1,1)]'::text AS p ) + SELECT 'pgcast_02', p = p::path::geometry::path::text FROM p; +WITH p AS ( SELECT '(1,1)'::text AS p ) + SELECT 'pgcast_03', p = p::point::geometry::point::text FROM p; +SELECT 'pgcast_03','POLYGON EMPTY'::geometry::polygon IS NULL; +SELECT 'pgcast_04','LINESTRING EMPTY'::geometry::path IS NULL; +SELECT 'pgcast_05','POINT EMPTY'::geometry::point IS NULL; +SELECT 'pgcast_06',ST_AsText('((0,0),(0,1),(1,1),(1,0))'::polygon::geometry); -- -- Delete inserted spatial data diff --git a/regress/out_geometry_expected b/regress/out_geometry_expected index 6e9d6ddf2..82b1a207c 100644 --- a/regress/out_geometry_expected +++ b/regress/out_geometry_expected @@ -72,3 +72,10 @@ geojson_options_13|{"type":"LineString","coordinates":[[1,1],[2,2],[3,3],[4,4]]} geojson_options_14|{"type":"LineString","crs":{"type":"name","properties":{"name":"urn:ogc:def:crs:EPSG::4326"}},"coordinates":[[1,1],[2,2],[3,3],[4,4]]} geojson_options_15|{"type":"LineString","bbox":[1,1,4,4],"coordinates":[[1,1],[2,2],[3,3],[4,4]]} geojson_options_16|{"type":"LineString","crs":{"type":"name","properties":{"name":"urn:ogc:def:crs:EPSG::4326"}},"bbox":[1,1,4,4],"coordinates":[[1,1],[2,2],[3,3],[4,4]]} +pgcast_01|t +pgcast_02|t +pgcast_03|t +pgcast_03|t +pgcast_04|t +pgcast_05|t +pgcast_06|POLYGON((0 0,0 1,1 1,1 0,0 0)) -- 2.40.0