From f0894275df4f7b2f7dbda5e180bb083ed54d6c73 Mon Sep 17 00:00:00 2001 From: Paul Ramsey Date: Tue, 26 Jan 2010 18:56:22 +0000 Subject: [PATCH] Make ST_AddMeasure handle multilinestrings as well as linestrings. git-svn-id: http://svn.osgeo.org/postgis/trunk@5168 b70326c6-7e19-0410-871a-916f4a2858ee --- liblwgeom/cunit/cu_geodetic.c | 2 +- liblwgeom/liblwgeom.h | 1 + liblwgeom/lwline.c | 2 +- liblwgeom/lwmline.c | 61 ++++++++++++++++++++++++++++++++++ postgis/lwgeom_functions_lrs.c | 13 +++++--- 5 files changed, 73 insertions(+), 6 deletions(-) diff --git a/liblwgeom/cunit/cu_geodetic.c b/liblwgeom/cunit/cu_geodetic.c index f0dbd7cc9..e4ecc7414 100644 --- a/liblwgeom/cunit/cu_geodetic.c +++ b/liblwgeom/cunit/cu_geodetic.c @@ -30,7 +30,7 @@ CU_pSuite register_geodetic_suite(void) if ( (NULL == CU_add_test(pSuite, "test_signum()", test_signum)) || (NULL == CU_add_test(pSuite, "test_gbox_from_spherical_coordinates()", test_gbox_from_spherical_coordinates)) || -/* (NULL == CU_add_test(pSuite, "test_gserialized_get_gbox_geocentric()", test_gserialized_get_gbox_geocentric)) || */ + (NULL == CU_add_test(pSuite, "test_gserialized_get_gbox_geocentric()", test_gserialized_get_gbox_geocentric)) || (NULL == CU_add_test(pSuite, "test_clairaut()", test_clairaut)) || (NULL == CU_add_test(pSuite, "test_edge_intersection()", test_edge_intersection)) || (NULL == CU_add_test(pSuite, "test_edge_distance_to_point()", test_edge_distance_to_point)) || diff --git a/liblwgeom/liblwgeom.h b/liblwgeom/liblwgeom.h index f3ce1ef35..8d33e4954 100644 --- a/liblwgeom/liblwgeom.h +++ b/liblwgeom/liblwgeom.h @@ -1380,6 +1380,7 @@ extern POINTARRAY *ptarray_substring(POINTARRAY *, double, double); extern double ptarray_locate_point(POINTARRAY *, POINT2D *); extern void closest_point_on_segment(POINT2D *p, POINT2D *A, POINT2D *B, POINT2D *ret); extern LWLINE *lwline_measured_from_lwline(const LWLINE *lwline, double m_start, double m_end); +extern LWMLINE* lwmline_measured_from_lwmline(const LWMLINE *lwmline, double m_start, double m_end); /* * Ensure every segment is at most 'dist' long. diff --git a/liblwgeom/lwline.c b/liblwgeom/lwline.c index 614496cdb..d9adfb483 100644 --- a/liblwgeom/lwline.c +++ b/liblwgeom/lwline.c @@ -544,7 +544,7 @@ lwline_measured_from_lwline(const LWLINE *lwline, double m_start, double m_end) if( TYPE_GETTYPE(lwline->type) != LINETYPE ) { - lwerror("lwmline_construct_from_lwline: only line types supported"); + lwerror("lwline_construct_from_lwline: only line types supported"); return NULL; } diff --git a/liblwgeom/lwmline.c b/liblwgeom/lwmline.c index f3e82b189..969d76f1e 100644 --- a/liblwgeom/lwmline.c +++ b/liblwgeom/lwmline.c @@ -123,6 +123,67 @@ lwmline_add(const LWMLINE *to, uint32 where, const LWGEOM *what) } +/** +* Re-write the measure ordinate (or add one, if it isn't already there) interpolating +* the measure between the supplied start and end values. +*/ +LWMLINE* +lwmline_measured_from_lwmline(const LWMLINE *lwmline, double m_start, double m_end) +{ + int i = 0; + int hasm = 0, hasz = 0; + double length = 0.0, length_so_far = 0.0; + double m_range = m_end - m_start; + LWGEOM **geoms = NULL; + + if( TYPE_GETTYPE(lwmline->type) != MULTILINETYPE ) + { + lwerror("lwmline_measured_from_lmwline: only multiline types supported"); + return NULL; + } + + hasz = TYPE_HASZ(lwmline->type); + hasm = 1; + + /* Calculate the total length of the mline */ + for( i = 0; i < lwmline->ngeoms; i++ ) + { + LWLINE *lwline = (LWLINE*)lwmline->geoms[i]; + if( lwline->points && lwline->points->npoints > 1 ) + { + length += lwgeom_pointarray_length2d(lwline->points); + } + } + + if( lwgeom_is_empty((LWGEOM*)lwmline) ) + { + return (LWMLINE*)lwcollection_construct_empty(lwmline->SRID, hasz, hasm); + } + + geoms = lwalloc(sizeof(LWGEOM*) * lwmline->ngeoms); + + for( i = 0; i < lwmline->ngeoms; i++ ) + { + double sub_m_start, sub_m_end; + double sub_length = 0.0; + LWLINE *lwline = (LWLINE*)lwmline->geoms[i]; + + if( lwline->points && lwline->points->npoints > 1 ) + { + sub_length = lwgeom_pointarray_length2d(lwline->points); + } + + sub_m_start = (m_start + m_range * length_so_far / length); + sub_m_end = (m_start + m_range * (length_so_far + sub_length) / length); + + geoms[i] = (LWGEOM*)lwline_measured_from_lwline(lwline, sub_m_start, sub_m_end); + + length_so_far += sub_length; + } + + return (LWMLINE*)lwcollection_construct(lwmline->type, lwmline->SRID, NULL, lwmline->ngeoms, geoms); +} + void lwmline_free(LWMLINE *mline) { int i; diff --git a/postgis/lwgeom_functions_lrs.c b/postgis/lwgeom_functions_lrs.c index c9ba726ab..22d46e7b5 100644 --- a/postgis/lwgeom_functions_lrs.c +++ b/postgis/lwgeom_functions_lrs.c @@ -551,16 +551,21 @@ Datum ST_AddMeasure(PG_FUNCTION_ARGS) double start_measure = PG_GETARG_FLOAT8(1); double end_measure = PG_GETARG_FLOAT8(2); LWGEOM *lwin, *lwout; + int type = TYPE_GETTYPE(gin->type); - /* Raise an error if input is not a linestring */ - if ( TYPE_GETTYPE(gin->type) != LINETYPE ) + /* Raise an error if input is not a linestring or multilinestring */ + if ( type != LINETYPE && type != MULTILINETYPE ) { - lwerror("Only LINESTRING is supported"); + lwerror("Only LINESTRING and MULTILINESTRING are supported"); PG_RETURN_NULL(); } lwin = pglwgeom_deserialize(gin); - lwout = (LWGEOM*)lwline_measured_from_lwline((LWLINE*)lwin, start_measure, end_measure); + if( type == LINETYPE ) + lwout = (LWGEOM*)lwline_measured_from_lwline((LWLINE*)lwin, start_measure, end_measure); + else + lwout = (LWGEOM*)lwmline_measured_from_lwmline((LWMLINE*)lwin, start_measure, end_measure); + lwgeom_release(lwin); if ( lwout == NULL ) -- 2.40.0