[postgis-tickets] r15913 - Add ST_LineInterpolatePoints

Daniel Baston dbaston at gmail.com
Thu Oct 5 18:00:28 PDT 2017


Author: dbaston
Date: 2017-10-05 18:00:27 -0700 (Thu, 05 Oct 2017)
New Revision: 15913

Added:
   trunk/doc/html/image_src/st_line_interpolate_points01.wkt
Modified:
   trunk/NEWS
   trunk/doc/html/image_src/Makefile.in
   trunk/doc/reference_lrs.xml
   trunk/liblwgeom/cunit/cu_algorithm.c
   trunk/liblwgeom/cunit/cu_tester.h
   trunk/liblwgeom/liblwgeom.h.in
   trunk/liblwgeom/lwgeom_api.c
   trunk/liblwgeom/lwline.c
   trunk/postgis/lwgeom_functions_analytic.c
   trunk/postgis/postgis.sql.in
   trunk/regress/regress_lrs.sql
   trunk/regress/regress_lrs_expected
Log:
Add ST_LineInterpolatePoints

Closes #3564


Modified: trunk/NEWS
===================================================================
--- trunk/NEWS	2017-10-05 20:12:51 UTC (rev 15912)
+++ trunk/NEWS	2017-10-06 01:00:27 UTC (rev 15913)
@@ -2,6 +2,7 @@
 2018/xx/xx
 * New Features *
   - #3876, ST_Angle function (RĂ©mi Cura)
+  - #3564, ST_LineInterpolatePoints (Dan Baston)
 
 PostGIS 2.4.0
 2017/09/30

Modified: trunk/doc/html/image_src/Makefile.in
===================================================================
--- trunk/doc/html/image_src/Makefile.in	2017-10-05 20:12:51 UTC (rev 15912)
+++ trunk/doc/html/image_src/Makefile.in	2017-10-06 01:00:27 UTC (rev 15913)
@@ -91,6 +91,7 @@
 	../images/st_linecrossingdirection03.png \
 	../images/st_linecrossingdirection04.png \
 	../images/st_line_interpolate_point01.png \
+	../images/st_line_interpolate_points01.png \
 	../images/st_line_substring01.png \
 	../images/st_longestline01.png \
 	../images/st_longestline02.png \

Added: trunk/doc/html/image_src/st_line_interpolate_points01.wkt
===================================================================
--- trunk/doc/html/image_src/st_line_interpolate_points01.wkt	                        (rev 0)
+++ trunk/doc/html/image_src/st_line_interpolate_points01.wkt	2017-10-06 01:00:27 UTC (rev 15913)
@@ -0,0 +1,3 @@
+Style2;LINESTRING(25 50, 100 125, 150 190)
+Style1-thinline;MULTIPOINT(51.5974135047432 76.5974135047432,78.1948270094864 103.194827009486,104.132163186446 130.37181214238,127.066081593223 160.18590607119,150 190)
+

Modified: trunk/doc/reference_lrs.xml
===================================================================
--- trunk/doc/reference_lrs.xml	2017-10-05 20:12:51 UTC (rev 15912)
+++ trunk/doc/reference_lrs.xml	2017-10-06 01:00:27 UTC (rev 15913)
@@ -83,10 +83,85 @@
 		  <refsection>
 			<title>See Also</title>
 
-			<para><xref linkend="ST_AsText" />, <xref linkend="ST_AsEWKT" />, <xref linkend="ST_Length" />, <xref linkend="ST_LineLocatePoint" /></para>
+			<para>
+				<xref linkend="ST_AsText" />,
+				<xref linkend="ST_AsEWKT" />,
+				<xref linkend="ST_Length" />,
+				<xref linkend="ST_LineInterpolatePoints" />
+				<xref linkend="ST_LineLocatePoint" />
+			O</para>
 		  </refsection>
 		</refentry>
 
+		<refentry id="ST_LineInterpolatePoints">
+		  <refnamediv>
+			<refname>ST_LineInterpolatePoints</refname>
+
+			<refpurpose>
+				Returns one or more points interpolated along a line.
+			</refpurpose>
+		  </refnamediv>
+
+		  <refsynopsisdiv>
+			<funcsynopsis>
+			  <funcprototype>
+				<funcdef>geometry <function>ST_LineInterpolatePoints</function></funcdef>
+				<paramdef><type>geometry </type> <parameter>a_linestring</parameter></paramdef>
+				<paramdef><type>float8 </type> <parameter>a_fraction</parameter></paramdef>
+				<paramdef><type>boolean </type> <parameter>repeat</parameter></paramdef>
+			  </funcprototype>
+			</funcsynopsis>
+		  </refsynopsisdiv>
+
+		  <refsection>
+			<title>Description</title>
+
+			<para>Returns one or more points interpolated along a line. First argument
+			must be a LINESTRING. Second argument is a float8 between 0 and 1
+			representing the spacing between the points as a fraction of total
+			LineString length. If the third argument is false, at most one point
+			will be constructed (the function will be equivalent to <xref linkend="ST_LineInterpolatePoint" />.)
+		</para>
+
+		<para>
+			If the result has zero or one points, it will be returned as a POINT.
+			If it has two or more points, it will be returned as a MULTIPOINT.
+		</para>
+
+
+			<para>Availability: 2.5.0</para>
+			<para>&Z_support;</para>
+			<para>&M_support;</para>
+		  </refsection>
+
+		  <refsection>
+			<title>Examples</title>
+			<informalfigure>
+			  <mediaobject>
+				<imageobject>
+				  <imagedata fileref="images/st_line_interpolate_points01.png" />
+				</imageobject>
+				<caption><para>A linestring with the interpolated points every 20% </para></caption>
+			  </mediaobject>
+			</informalfigure>
+			<programlisting>--Return points each 20% along a 2D line
+SELECT ST_AsText(ST_LineInterpolatePoints('LINESTRING(25 50, 100 125, 150 190)', 0.20))
+   st_astext
+----------------
+ MULTIPOINT(51.5974135047432 76.5974135047432,78.1948270094864 103.194827009486,104.132163186446 130.37181214238,127.066081593223 160.18590607119,150 190)
+</programlisting>
+		  </refsection>
+
+		  <refsection>
+			<title>See Also</title>
+
+			<para>
+				<xref linkend="ST_LineInterpolatePoint" />
+				<xref linkend="ST_LineLocatePoint" />
+			</para>
+		  </refsection>
+		</refentry>
+
 		<refentry id="ST_LineLocatePoint">
 		  <refnamediv>
 			<refname>ST_LineLocatePoint</refname>

Modified: trunk/liblwgeom/cunit/cu_algorithm.c
===================================================================
--- trunk/liblwgeom/cunit/cu_algorithm.c	2017-10-05 20:12:51 UTC (rev 15912)
+++ trunk/liblwgeom/cunit/cu_algorithm.c	2017-10-06 01:00:27 UTC (rev 15913)
@@ -464,6 +464,84 @@
 
 }
 
+static void test_lwline_interpolate_points(void)
+{
+	LWLINE* line = lwgeom_as_lwline(lwgeom_from_wkt("LINESTRING ZM (0 0 3 1, 1 1 2 2, 10 10 4 3)", LW_PARSER_CHECK_NONE));
+	LWLINE* line2d = lwgeom_as_lwline(lwgeom_from_wkt("LINESTRING (1 1, 3 7, 9 12)", LW_PARSER_CHECK_NONE));
+	LWLINE* empty_line = lwgeom_as_lwline(lwgeom_from_wkt("LINESTRING EMPTY", LW_PARSER_CHECK_NONE));
+
+	POINTARRAY* rpa;
+	POINT4D pta;
+	POINT4D ptb;
+	double eps = 1e-10;
+
+	/* Empty line gives empty point */
+	rpa = lwline_interpolate_points(empty_line, 0.5, LW_TRUE);
+	ASSERT_INT_EQUAL(rpa->npoints, 0);
+	ptarray_free(rpa);
+
+	/* Get first endpoint when fraction = 0 */
+	rpa = lwline_interpolate_points(line, 0, LW_TRUE);
+	ASSERT_INT_EQUAL(rpa->npoints, 1);
+	pta = getPoint4d(line->points, 0);
+	ptb = getPoint4d(rpa, 0);
+	ASSERT_POINT4D_EQUAL(pta, ptb, eps);
+	ptarray_free(rpa);
+
+	/* Get last endpoint when fraction = 0 */
+	rpa = lwline_interpolate_points(line, 1, LW_TRUE);
+	ASSERT_INT_EQUAL(rpa->npoints, 1);
+	pta = getPoint4d(line->points, line->points->npoints - 1);
+	ptb = getPoint4d(rpa, 0);
+	ASSERT_POINT4D_EQUAL(pta, ptb, eps);
+	ptarray_free(rpa);
+
+    /* Interpolate a single point */
+    /* First vertex is at 10% */
+	rpa = lwline_interpolate_points(line, 0.1, LW_FALSE);
+	ASSERT_INT_EQUAL(rpa->npoints, 1);
+	pta = getPoint4d(line->points, 1);
+	ptb = getPoint4d(rpa, 0);
+	ASSERT_POINT4D_EQUAL(pta, ptb, eps);
+	ptarray_free(rpa);
+
+	/* 5% is halfway to first vertex */
+	rpa = lwline_interpolate_points(line, 0.05, LW_FALSE);
+	ASSERT_INT_EQUAL(rpa->npoints, 1);
+	pta.x = 0.5;
+	pta.y = 0.5;
+	pta.m = 1.5;
+	pta.z = 2.5;
+	ptb = getPoint4d(rpa, 0);
+	ASSERT_POINT4D_EQUAL(pta, ptb, eps);
+	ptarray_free(rpa);
+
+    /* Now repeat points */
+	rpa = lwline_interpolate_points(line, 0.4, LW_TRUE);
+	ASSERT_INT_EQUAL(rpa->npoints, 2);
+	pta.x = 4;
+	pta.y = 4;
+	ptb = getPoint4d(rpa, 0);
+	ASSERT_POINT2D_EQUAL(pta, ptb, eps);
+
+	pta.x = 8;
+	pta.y = 8;
+	ptb = getPoint4d(rpa, 1);
+	ASSERT_POINT2D_EQUAL(pta, ptb, eps);
+	ptarray_free(rpa);
+
+	/* Make sure it works on 2D lines */
+	rpa = lwline_interpolate_points(line2d, 0.4, LW_TRUE);
+	ASSERT_INT_EQUAL(rpa->npoints, 2);
+	CU_ASSERT_FALSE(ptarray_has_z(rpa));
+	CU_ASSERT_FALSE(ptarray_has_m(rpa));
+	ptarray_free(rpa);
+
+	lwgeom_free(lwline_as_lwgeom(line));
+	lwgeom_free(lwline_as_lwgeom(line2d));
+	lwgeom_free(lwline_as_lwgeom(empty_line));
+}
+
 static void test_lwline_clip(void)
 {
 	LWCOLLECTION *c;
@@ -1198,6 +1276,7 @@
 	PG_ADD_TEST(suite,test_lwpoint_set_ordinate);
 	PG_ADD_TEST(suite,test_lwpoint_get_ordinate);
 	PG_ADD_TEST(suite,test_point_interpolate);
+	PG_ADD_TEST(suite,test_lwline_interpolate_points);
 	PG_ADD_TEST(suite,test_lwline_clip);
 	PG_ADD_TEST(suite,test_lwline_clip_big);
 	PG_ADD_TEST(suite,test_lwmline_clip);

Modified: trunk/liblwgeom/cunit/cu_tester.h
===================================================================
--- trunk/liblwgeom/cunit/cu_tester.h	2017-10-05 20:12:51 UTC (rev 15912)
+++ trunk/liblwgeom/cunit/cu_tester.h	2017-10-06 01:00:27 UTC (rev 15913)
@@ -73,5 +73,17 @@
 	CU_PASS(); \
 } while(0);
 
+#define ASSERT_POINT2D_EQUAL(o, e, eps) do { \
+	CU_ASSERT_DOUBLE_EQUAL(o.x, e.x, eps); \
+	CU_ASSERT_DOUBLE_EQUAL(o.y, e.y, eps); \
+} while(0);
+
+#define ASSERT_POINT4D_EQUAL(o, e, eps) do { \
+	CU_ASSERT_DOUBLE_EQUAL(o.x, e.x, eps); \
+	CU_ASSERT_DOUBLE_EQUAL(o.y, e.y, eps); \
+	CU_ASSERT_DOUBLE_EQUAL(o.z, e.z, eps); \
+	CU_ASSERT_DOUBLE_EQUAL(o.m, e.m, eps); \
+} while(0);
+
 /* Utility functions */
 void do_fn_test(LWGEOM* (*transfn)(LWGEOM*), char *input_wkt, char *expected_wkt);

Modified: trunk/liblwgeom/liblwgeom.h.in
===================================================================
--- trunk/liblwgeom/liblwgeom.h.in	2017-10-05 20:12:51 UTC (rev 15912)
+++ trunk/liblwgeom/liblwgeom.h.in	2017-10-06 01:00:27 UTC (rev 15913)
@@ -1013,6 +1013,11 @@
  */
 extern int lwline_add_lwpoint(LWLINE *line, LWPOINT *point, int where);
 
+/**
+ * Interpolate one or more points along a line
+ */
+extern POINTARRAY* lwline_interpolate_points(const LWLINE *line, double length_fraction, char repeat);
+
 /******************************************************************
  * LWPOLY functions
  ******************************************************************/
@@ -1214,7 +1219,7 @@
 extern char* lwpoint_to_latlon(const LWPOINT *p, const char *format);
 extern int lwgeom_startpoint(const LWGEOM* lwgeom, POINT4D* pt);
 
-extern void interpolate_point4d(POINT4D *A, POINT4D *B, POINT4D *I, double F);
+extern void interpolate_point4d(const POINT4D *A, const POINT4D *B, POINT4D *I, double F);
 
 /**
 * Ensure the outer ring is clockwise oriented and all inner rings

Modified: trunk/liblwgeom/lwgeom_api.c
===================================================================
--- trunk/liblwgeom/lwgeom_api.c	2017-10-05 20:12:51 UTC (rev 15912)
+++ trunk/liblwgeom/lwgeom_api.c	2017-10-06 01:00:27 UTC (rev 15913)
@@ -703,7 +703,7 @@
  *   F=.2   :    A-I-------B
  */
 void
-interpolate_point4d(POINT4D *A, POINT4D *B, POINT4D *I, double F)
+interpolate_point4d(const POINT4D *A, const POINT4D *B, POINT4D *I, double F)
 {
 #if PARANOIA_LEVEL > 0
 	double absF=fabs(F);

Modified: trunk/liblwgeom/lwline.c
===================================================================
--- trunk/liblwgeom/lwline.c	2017-10-05 20:12:51 UTC (rev 15912)
+++ trunk/liblwgeom/lwline.c	2017-10-06 01:00:27 UTC (rev 15913)
@@ -547,3 +547,79 @@
 }
 
 
+POINTARRAY* lwline_interpolate_points(const LWLINE *line, double length_fraction, char repeat) {
+	POINT4D pt;
+	uint32_t i;
+	uint32_t points_to_interpolate;
+	uint32_t points_found = 0;
+	double length;
+	double length_fraction_increment = length_fraction;
+	double length_fraction_consumed = 0;
+	char has_z = (char) lwgeom_has_z(lwline_as_lwgeom(line));
+	char has_m = (char) lwgeom_has_m(lwline_as_lwgeom(line));
+	const POINTARRAY* ipa = line->points;
+	POINTARRAY* opa;
+
+	/* Empty.InterpolatePoint == Point Empty */
+	if ( lwline_is_empty(line) )
+	{
+		return ptarray_construct_empty(has_z, has_m, 0);
+	}
+
+	/* If distance is one of the two extremes, return the point on that
+	 * end rather than doing any computations
+	 */
+	if ( length_fraction == 0.0 || length_fraction == 1.0 )
+	{
+		if ( length_fraction == 0.0 )
+			getPoint4d_p(ipa, 0, &pt);
+		else
+			getPoint4d_p(ipa, ipa->npoints-1, &pt);
+
+		opa = ptarray_construct(has_z, has_m, 1);
+		ptarray_set_point4d(opa, 0, &pt);
+
+		return opa;
+	}
+
+	/* Interpolate points along the line */
+	length = ptarray_length_2d(ipa);
+	points_to_interpolate = repeat ? (uint32_t) floor(1 / length_fraction) : 1;
+	opa = ptarray_construct(has_z, has_m, points_to_interpolate);
+
+	const POINT2D* p1 = getPoint2d_cp(ipa, 0);
+	for ( i = 0; i < ipa->npoints - 1 && points_found < points_to_interpolate; i++ )
+	{
+		const POINT2D* p2 = getPoint2d_cp(ipa, i+1);
+		double segment_length_frac = distance2d_pt_pt(p1, p2) / length;
+
+		/* If our target distance is before the total length we've seen
+		 * so far. create a new point some distance down the current
+		 * segment.
+		 */
+		while ( length_fraction < length_fraction_consumed + segment_length_frac && points_found < points_to_interpolate )
+		{
+			POINT4D p1_4d = getPoint4d(ipa, i);
+			POINT4D p2_4d = getPoint4d(ipa, i+1);
+
+			double segment_fraction = (length_fraction - length_fraction_consumed) / segment_length_frac;
+			interpolate_point4d(&p1_4d, &p2_4d, &pt, segment_fraction);
+			ptarray_set_point4d(opa, points_found++, &pt);
+			length_fraction += length_fraction_increment;
+		}
+
+		length_fraction_consumed += segment_length_frac;
+
+		p1 = p2;
+	}
+
+	/* Return the last point on the line. This shouldn't happen, but
+	 * could if there's some floating point rounding errors. */
+	if (points_found < points_to_interpolate) {
+		getPoint4d_p(ipa, ipa->npoints - 1, &pt);
+		ptarray_set_point4d(opa, points_found, &pt);
+	}
+
+    return opa;
+}
+

Modified: trunk/postgis/lwgeom_functions_analytic.c
===================================================================
--- trunk/postgis/lwgeom_functions_analytic.c	2017-10-05 20:12:51 UTC (rev 15912)
+++ trunk/postgis/lwgeom_functions_analytic.c	2017-10-06 01:00:27 UTC (rev 15913)
@@ -36,18 +36,10 @@
 
 #include "access/htup_details.h"
 
-/***********************************************************************
- * Simple Douglas-Peucker line simplification.
- * No checks are done to avoid introduction of self-intersections.
- * No topology relations are considered.
- *
- * --strk at kbt.io;
- ***********************************************************************/
-
-
 /* Prototypes */
 Datum LWGEOM_simplify2d(PG_FUNCTION_ARGS);
 Datum LWGEOM_SetEffectiveArea(PG_FUNCTION_ARGS);
+Datum LWGEOM_line_interpolate_point(PG_FUNCTION_ARGS);
 Datum ST_LineCrossingDirection(PG_FUNCTION_ARGS);
 Datum ST_MinimumBoundingRadius(PG_FUNCTION_ARGS);
 Datum ST_MinimumBoundingCircle(PG_FUNCTION_ARGS);
@@ -61,6 +53,13 @@
 static int point_in_ring(POINTARRAY *pts, const POINT2D *point);
 static int point_in_ring_rtree(RTREE_NODE *root, const POINT2D *point);
 
+/***********************************************************************
+ * Simple Douglas-Peucker line simplification.
+ * No checks are done to avoid introduction of self-intersections.
+ * No topology relations are considered.
+ *
+ * --strk at kbt.io;
+ ***********************************************************************/
 
 PG_FUNCTION_INFO_V1(LWGEOM_simplify2d);
 Datum LWGEOM_simplify2d(PG_FUNCTION_ARGS)
@@ -137,111 +136,50 @@
  * Interpolate a point along a line, useful for Geocoding applications
  * SELECT line_interpolate_point( 'LINESTRING( 0 0, 2 2'::geometry, .5 )
  * returns POINT( 1 1 ).
- * Works in 2d space only.
- *
- * Initially written by: jsunday at rochgrp.com
- * Ported to LWGEOM by: strk at refractions.net
  ***********************************************************************/
-
-Datum LWGEOM_line_interpolate_point(PG_FUNCTION_ARGS);
-
 PG_FUNCTION_INFO_V1(LWGEOM_line_interpolate_point);
 Datum LWGEOM_line_interpolate_point(PG_FUNCTION_ARGS)
 {
 	GSERIALIZED *gser = PG_GETARG_GSERIALIZED_P(0);
 	GSERIALIZED *result;
-	double distance = PG_GETARG_FLOAT8(1);
-	LWLINE *line;
-	LWGEOM *geom;
-	LWPOINT *point;
-	POINTARRAY *ipa, *opa;
-	POINT4D pt;
-	int nsegs, i;
-	double length, slength, tlength;
+	double distance_fraction = PG_GETARG_FLOAT8(1);
+	int repeat = PG_NARGS() > 2 && PG_GETARG_BOOL(2);
+	int srid = gserialized_get_srid(gser);
+	LWLINE* lwline;
+	LWGEOM* lwresult;
+	POINTARRAY* opa;
 
-	if ( distance < 0 || distance > 1 )
+	if ( distance_fraction < 0 || distance_fraction > 1 )
 	{
 		elog(ERROR,"line_interpolate_point: 2nd arg isn't within [0,1]");
+		PG_FREE_IF_COPY(gser, 0);
 		PG_RETURN_NULL();
 	}
 
 	if ( gserialized_get_type(gser) != LINETYPE )
 	{
 		elog(ERROR,"line_interpolate_point: 1st arg isn't a line");
+		PG_FREE_IF_COPY(gser, 0);
 		PG_RETURN_NULL();
 	}
 
-	/* Empty.InterpolatePoint == Point Empty */
-	if ( gserialized_is_empty(gser) )
-	{
-		point = lwpoint_construct_empty(gserialized_get_srid(gser), gserialized_has_z(gser), gserialized_has_m(gser));
-		result = geometry_serialize(lwpoint_as_lwgeom(point));
-		lwpoint_free(point);
-		PG_RETURN_POINTER(result);
-	}
+	lwline = lwgeom_as_lwline(lwgeom_from_gserialized(gser));
+	opa = lwline_interpolate_points(lwline, distance_fraction, repeat);
 
-	geom = lwgeom_from_gserialized(gser);
-	line = lwgeom_as_lwline(geom);
-	ipa = line->points;
+	lwgeom_free(lwline_as_lwgeom(lwline));
+	PG_FREE_IF_COPY(gser, 0);
 
-	/* If distance is one of the two extremes, return the point on that
-	 * end rather than doing any expensive computations
-	 */
-	if ( distance == 0.0 || distance == 1.0 )
+	if (opa->npoints <= 1)
 	{
-		if ( distance == 0.0 )
-			getPoint4d_p(ipa, 0, &pt);
-		else
-			getPoint4d_p(ipa, ipa->npoints-1, &pt);
-
-		opa = ptarray_construct(lwgeom_has_z(geom), lwgeom_has_m(geom), 1);
-		ptarray_set_point4d(opa, 0, &pt);
-
-		point = lwpoint_construct(line->srid, NULL, opa);
-		PG_RETURN_POINTER(geometry_serialize(lwpoint_as_lwgeom(point)));
+		lwresult = lwpoint_as_lwgeom(lwpoint_construct(srid, NULL, opa));
+	} else {
+		lwresult = lwmpoint_as_lwgeom(lwmpoint_construct(srid, opa));
 	}
 
-	/* Interpolate a point on the line */
-	nsegs = ipa->npoints - 1;
-	length = ptarray_length_2d(ipa);
-	tlength = 0;
-	for ( i = 0; i < nsegs; i++ )
-	{
-		POINT4D p1, p2;
-		POINT4D *p1ptr=&p1, *p2ptr=&p2; /* don't break
-						                                 * strict-aliasing rules
-						                                 */
+	result = geometry_serialize(lwresult);
+	lwgeom_free(lwresult);
 
-		getPoint4d_p(ipa, i, &p1);
-		getPoint4d_p(ipa, i+1, &p2);
-
-		/* Find the relative length of this segment */
-		slength = distance2d_pt_pt((POINT2D*)p1ptr, (POINT2D*)p2ptr)/length;
-
-		/* If our target distance is before the total length we've seen
-		 * so far. create a new point some distance down the current
-		 * segment.
-		 */
-		if ( distance < tlength + slength )
-		{
-			double dseg = (distance - tlength) / slength;
-			interpolate_point4d(&p1, &p2, &pt, dseg);
-			opa = ptarray_construct(lwgeom_has_z(geom), lwgeom_has_m(geom), 1);
-			ptarray_set_point4d(opa, 0, &pt);
-			point = lwpoint_construct(line->srid, NULL, opa);
-			PG_RETURN_POINTER(geometry_serialize(lwpoint_as_lwgeom(point)));
-		}
-		tlength += slength;
-	}
-
-	/* Return the last point on the line. This shouldn't happen, but
-	 * could if there's some floating point rounding errors. */
-	getPoint4d_p(ipa, ipa->npoints-1, &pt);
-	opa = ptarray_construct(lwgeom_has_z(geom), lwgeom_has_m(geom), 1);
-	ptarray_set_point4d(opa, 0, &pt);
-	point = lwpoint_construct(line->srid, NULL, opa);
-	PG_FREE_IF_COPY(gser, 0);
-	PG_RETURN_POINTER(geometry_serialize(lwpoint_as_lwgeom(point)));
+	PG_RETURN_POINTER(result);
 }
 /***********************************************************************
  * --jsunday at rochgrp.com;

Modified: trunk/postgis/postgis.sql.in
===================================================================
--- trunk/postgis/postgis.sql.in	2017-10-05 20:12:51 UTC (rev 15912)
+++ trunk/postgis/postgis.sql.in	2017-10-06 01:00:27 UTC (rev 15913)
@@ -3128,6 +3128,12 @@
 	AS 'MODULE_PATHNAME', 'LWGEOM_line_interpolate_point'
 	LANGUAGE 'c' IMMUTABLE STRICT _PARALLEL;
 
+-- Availability: 2.5.0
+CREATE OR REPLACE FUNCTION ST_LineInterpolatePoints(geometry, float8, repeat boolean DEFAULT true)
+	RETURNS geometry
+	AS 'MODULE_PATHNAME', 'LWGEOM_line_interpolate_point'
+	LANGUAGE 'c' IMMUTABLE STRICT _PARALLEL;
+
 -- Availability: 1.2.2
 -- Deprecation in 2.1.0
 CREATE OR REPLACE FUNCTION ST_line_interpolate_point(geometry, float8)

Modified: trunk/regress/regress_lrs.sql
===================================================================
--- trunk/regress/regress_lrs.sql	2017-10-05 20:12:51 UTC (rev 15912)
+++ trunk/regress/regress_lrs.sql	2017-10-06 01:00:27 UTC (rev 15913)
@@ -69,7 +69,13 @@
 select 'line_interpolate_point', ST_AsText(ST_LineInterpolatePoint('LINESTRING(0 0, 1 1)', 0));
 select 'line_interpolate_point', ST_AsText(ST_LineInterpolatePoint('LINESTRING(0 0 10, 1 1 5)', 0.5));
 
+--
+--- ST_LineInterpolatePoints
+--
 
+select 'line_interpolate_points', ST_AsText(ST_LineInterpolatePoints('LINESTRING(0 0, 1 1)', 0.7));
+select 'line_interpolate_points', ST_AsText(ST_LineInterpolatePoints('LINESTRING(0 0, 1 1)', 0.3));
+
 --
 -- ST_AddMeasure
 --

Modified: trunk/regress/regress_lrs_expected
===================================================================
--- trunk/regress/regress_lrs_expected	2017-10-05 20:12:51 UTC (rev 15912)
+++ trunk/regress/regress_lrs_expected	2017-10-06 01:00:27 UTC (rev 15913)
@@ -36,6 +36,8 @@
 line_substring_12|POINT Z (0.5 0.5 7.5)
 line_interpolate_point|POINT(0 0)
 line_interpolate_point|POINT Z (0.5 0.5 7.5)
+line_interpolate_points|POINT(0.7 0.7)
+line_interpolate_points|MULTIPOINT(0.3 0.3,0.6 0.6,0.9 0.9)
 addMeasure1|LINESTRING M (0 0 10,2 0 15,4 0 20)
 addMeasure2|LINESTRING M (0 0 10,9 0 19,10 0 20)
 interpolatePoint1|2



More information about the postgis-tickets mailing list