From 564f011ec9da9e981bad787f5b8f1e4674f04d92 Mon Sep 17 00:00:00 2001 From: Daniel Baston Date: Sat, 16 Jan 2016 15:45:37 +0000 Subject: [PATCH] #3428, ST_Points git-svn-id: http://svn.osgeo.org/postgis/trunk@14604 b70326c6-7e19-0410-871a-916f4a2858ee --- NEWS | 1 + doc/reference_accessor.xml | 54 ++++++++++++++++++++++++++++++++ liblwgeom/cunit/cu_misc.c | 15 +++++++++ liblwgeom/cunit/cu_tester.c | 22 +++++++++++++ liblwgeom/cunit/cu_tester.h | 15 +++++++++ liblwgeom/liblwgeom.h.in | 2 +- liblwgeom/lwmpoint.c | 17 ++++++++++ postgis/lwgeom_functions_basic.c | 23 ++++++++++++++ postgis/postgis.sql.in | 6 ++++ regress/regress.sql | 3 ++ regress/regress_expected | 3 ++ 11 files changed, 160 insertions(+), 1 deletion(-) diff --git a/NEWS b/NEWS index 55e5b210d..56b7c0cda 100644 --- a/NEWS +++ b/NEWS @@ -11,6 +11,7 @@ PostGIS 2.3.0 - populate_topology_layer (Sandro Santilli) - #2259 ST_Voronoi (Dan Baston) - #3339 ST_GeneratePoints (Paul Ramsey) + - #3428 ST_Points (Dan Baston) PostGIS 2.2.1 2016/01/06 diff --git a/doc/reference_accessor.xml b/doc/reference_accessor.xml index 13f0fd6a1..f2ce6d6b6 100644 --- a/doc/reference_accessor.xml +++ b/doc/reference_accessor.xml @@ -2081,6 +2081,60 @@ POINT(3 2) + + + ST_Points + Returns a MultiPoint containing all of the coordinates of a geometry. + + + + + + + geometry ST_Points + + geometry + geom + + + + + + + Description + + + Returns a MultiPoint containing all of the coordinates of a + geometry. Does not remove points that are duplicated in + the input geometry, including start and end points of ring geometries. + (If this behavior is undesired, duplicates may be removed using + ). + + + + M and Z ordinates will be preserved if present. + + + &curve_support; + &Z_support; + + + + Examples + + SELECT ST_AsText(ST_Points('POLYGON Z ((30 10 4,10 30 5,40 40 6, 30 10))')); + +--result +MULTIPOINT Z (30 10 4,10 30 5,40 40 6, 30 10 4) + + + + + See Also + + + + ST_SRID diff --git a/liblwgeom/cunit/cu_misc.c b/liblwgeom/cunit/cu_misc.c index 11554c39c..3d6026e72 100644 --- a/liblwgeom/cunit/cu_misc.c +++ b/liblwgeom/cunit/cu_misc.c @@ -156,6 +156,20 @@ static void test_clone(void) lwgeom_free(geom1); } +static void test_lwmpoint_from_lwgeom(void) +{ + /* This cast is so ugly, we only want to do it once. And not even that. */ + LWGEOM* (*to_points)(LWGEOM*) = (LWGEOM* (*)(LWGEOM*)) &lwmpoint_from_lwgeom; + + do_fn_test(to_points, "MULTIPOLYGON (EMPTY)", "MULTIPOINT EMPTY"); + do_fn_test(to_points, "POINT (30 10)", "MULTIPOINT ((30 10))"); + do_fn_test(to_points, "LINESTRING Z (30 10 4,10 30 5,40 40 6)", "MULTIPOINT Z (30 10 4,10 30 5, 40 40 6)"); + do_fn_test(to_points, "POLYGON((35 10,45 45,15 40,10 20,35 10),(20 30,35 35,30 20,20 30))", "MULTIPOINT(35 10,45 45,15 40,10 20,35 10,20 30,35 35,30 20,20 30)"); + do_fn_test(to_points, "MULTIPOINT M (10 40 1,40 30 2,20 20 3,30 10 4)", "MULTIPOINT M (10 40 1,40 30 2,20 20 3,30 10 4)"); + do_fn_test(to_points, "COMPOUNDCURVE(CIRCULARSTRING(0 0,2 0, 2 1, 2 3, 4 3),(4 3, 4 5, 1 4, 0 0))", "MULTIPOINT(0 0, 2 0, 2 1, 2 3, 4 3, 4 3, 4 5, 1 4, 0 0)"); + do_fn_test(to_points, "TIN(((80 130,50 160,80 70,80 130)),((50 160,10 190,10 70,50 160)))", "MULTIPOINT (80 130, 50 160, 80 70, 80 130, 50 160, 10 190, 10 70, 50 160)"); +} + /* ** Used by the test harness to register the tests in this file. */ @@ -170,4 +184,5 @@ void misc_suite_setup(void) PG_ADD_TEST(suite, test_misc_wkb); PG_ADD_TEST(suite, test_grid); PG_ADD_TEST(suite, test_clone); + PG_ADD_TEST(suite, test_lwmpoint_from_lwgeom); } diff --git a/liblwgeom/cunit/cu_tester.c b/liblwgeom/cunit/cu_tester.c index b760e479a..d1098c9c9 100644 --- a/liblwgeom/cunit/cu_tester.c +++ b/liblwgeom/cunit/cu_tester.c @@ -278,3 +278,25 @@ cu_error_msg_reset() { memset(cu_error_msg, '\0', MAX_CUNIT_ERROR_LENGTH); } + +/* Utility functions for testing */ + +/* do_transformation_test + * - reads input_wkt and expected_wkt + * - asserts output of transfn(input) = expected + * - cleans up + */ +void +do_fn_test(LWGEOM* (*transfn)(LWGEOM*), char *input_wkt, char *expected_wkt) +{ + LWGEOM* input = lwgeom_from_wkt(input_wkt, LW_PARSER_CHECK_NONE); + LWGEOM* expected = lwgeom_from_wkt(expected_wkt, LW_PARSER_CHECK_NONE); + LWGEOM* observed = transfn(input); + + ASSERT_LWGEOM_EQUAL(observed, expected); + + lwgeom_free(input); + lwgeom_free(expected); + lwgeom_free(observed); +} + diff --git a/liblwgeom/cunit/cu_tester.h b/liblwgeom/cunit/cu_tester.h index 918a0d153..ea606d7d3 100644 --- a/liblwgeom/cunit/cu_tester.h +++ b/liblwgeom/cunit/cu_tester.h @@ -10,6 +10,8 @@ * **********************************************************************/ +#include "liblwgeom.h" + #define MAX_CUNIT_ERROR_LENGTH 512 #define PG_ADD_TEST(suite, testfunc) CU_add_test(suite, #testfunc, testfunc) @@ -41,3 +43,16 @@ typedef void (*PG_SuiteSetup)(void); CU_ASSERT_STRING_EQUAL(o,e); \ } while (0); +#define ASSERT_LWGEOM_EQUAL(o, e) do { \ + if ( !lwgeom_same(o, e) ) { \ + char* wkt_o = lwgeom_to_ewkt(o); \ + char* wkt_e = lwgeom_to_ewkt(e); \ + fprintf(stderr, "[%s:%d]\n Expected: %s\n Obtained: %s\n", __FILE__, __LINE__, (wkt_o), (wkt_e)); \ + lwfree(wkt_o); \ + lwfree(wkt_e); \ + } \ + CU_ASSERT_TRUE(lwgeom_same(o, e)); \ +} while(0); + +/* Utility functions */ +void do_fn_test(LWGEOM* (*transfn)(LWGEOM*), char *input_wkt, char *expected_wkt); diff --git a/liblwgeom/liblwgeom.h.in b/liblwgeom/liblwgeom.h.in index f2c7081c8..59bfd1125 100644 --- a/liblwgeom/liblwgeom.h.in +++ b/liblwgeom/liblwgeom.h.in @@ -1337,7 +1337,7 @@ extern LWLINE *lwline_removepoint(LWLINE *line, uint32_t which); extern void lwline_setPoint4d(LWLINE *line, uint32_t which, POINT4D *newpoint); extern LWPOLY *lwpoly_from_lwlines(const LWLINE *shell, uint32_t nholes, const LWLINE **holes); extern LWTRIANGLE *lwtriangle_from_lwline(const LWLINE *shell); - +extern LWMPOINT *lwmpoint_from_lwgeom(const LWGEOM *g); /* Extract the coordinates of an LWGEOM into an LWMPOINT */ /* Some point accessors */ extern double lwpoint_get_x(const LWPOINT *point); diff --git a/liblwgeom/lwmpoint.c b/liblwgeom/lwmpoint.c index d486d6ac0..5e6887564 100644 --- a/liblwgeom/lwmpoint.c +++ b/liblwgeom/lwmpoint.c @@ -120,3 +120,20 @@ lwmpoint_remove_repeated_points(const LWMPOINT *mpoint, double tolerance) } +LWMPOINT* +lwmpoint_from_lwgeom(const LWGEOM *g) +{ + LWPOINTITERATOR* it = lwpointiterator_create(g); + int has_z = lwgeom_has_z(g); + int has_m = lwgeom_has_m(g); + LWMPOINT* result = lwmpoint_construct_empty(g->srid, has_z, has_m); + POINT4D p; + + while(lwpointiterator_next(it, &p)) { + LWPOINT* lwp = lwpoint_make(g->srid, has_z, has_m, &p); + lwmpoint_add_lwpoint(result, lwp); + } + + lwpointiterator_destroy(it); + return result; +} diff --git a/postgis/lwgeom_functions_basic.c b/postgis/lwgeom_functions_basic.c index b4634c747..051e52ee4 100644 --- a/postgis/lwgeom_functions_basic.c +++ b/postgis/lwgeom_functions_basic.c @@ -2806,3 +2806,26 @@ Datum ST_Scale(PG_FUNCTION_ARGS) PG_RETURN_POINTER(ret); } + +Datum ST_Points(PG_FUNCTION_ARGS); +PG_FUNCTION_INFO_V1(ST_Points); +Datum ST_Points(PG_FUNCTION_ARGS) +{ + if (PG_ARGISNULL(0)) + { + PG_RETURN_NULL(); + } + else + { + GSERIALIZED* geom = PG_GETARG_GSERIALIZED_P(0); + GSERIALIZED* ret; + LWGEOM* lwgeom = lwgeom_from_gserialized(geom); + LWMPOINT* result = lwmpoint_from_lwgeom(lwgeom); + + lwgeom_free(lwgeom); + + ret = geometry_serialize(lwmpoint_as_lwgeom(result)); + lwmpoint_free(result); + PG_RETURN_POINTER(ret); + } +} diff --git a/postgis/postgis.sql.in b/postgis/postgis.sql.in index b470c6d21..fb18e0557 100644 --- a/postgis/postgis.sql.in +++ b/postgis/postgis.sql.in @@ -3313,6 +3313,12 @@ CREATE OR REPLACE FUNCTION ST_Boundary(geometry) AS 'MODULE_PATHNAME','boundary' LANGUAGE 'c' IMMUTABLE STRICT; +-- Availability: 2.3.0 +CREATE OR REPLACE FUNCTION ST_Points(geometry) + RETURNS geometry + AS 'MODULE_PATHNAME', 'ST_Points' + LANGUAGE 'c' IMMUTABLE STRICT; + -- PostGIS equivalent function: symdifference(geom1 geometry, geom2 geometry) CREATE OR REPLACE FUNCTION ST_SymDifference(geom1 geometry, geom2 geometry) RETURNS geometry diff --git a/regress/regress.sql b/regress/regress.sql index d2034dee9..1023118cd 100644 --- a/regress/regress.sql +++ b/regress/regress.sql @@ -238,6 +238,9 @@ select '179', ST_AsText('MULTICURVE EMPTY'); select '180', ST_AsText('GEOMETRYCOLLECTION EMPTY'); select '181', ST_AsText('GEOMETRYCOLLECTION(TRIANGLE EMPTY,TIN EMPTY)'); +select '190', ST_Points(NULL) IS NULL; +select '191', ST_AsText(ST_Points('MULTICURVE EMPTY')); +select '192', ST_AsText(ST_Points('POLYGON((35 10,45 45,15 40,10 20,35 10),(20 30,35 35,30 20,20 30))')); -- Drop test table DROP table test; diff --git a/regress/regress_expected b/regress/regress_expected index 41672fb6b..157e1cf6f 100644 --- a/regress/regress_expected +++ b/regress/regress_expected @@ -165,3 +165,6 @@ ERROR: geometry contains non-closed rings 179|MULTICURVE EMPTY 180|GEOMETRYCOLLECTION EMPTY 181|GEOMETRYCOLLECTION(TRIANGLE EMPTY,TIN EMPTY) +190|t +191|MULTIPOINT EMPTY +192|MULTIPOINT(35 10,45 45,15 40,10 20,35 10,20 30,35 35,30 20,20 30) -- 2.40.0