[postgis-tickets] r16380 - Add single-sided buffer option

Daniel Baston dbaston at gmail.com
Wed Feb 14 07:03:49 PST 2018


Author: dbaston
Date: 2018-02-14 19:03:49 -0800 (Wed, 14 Feb 2018)
New Revision: 16380

Added:
   trunk/doc/html/image_src/st_buffer09.wkt
   trunk/doc/html/image_src/st_buffer10.wkt
   trunk/doc/html/image_src/st_buffer11.wkt
   trunk/doc/html/image_src/st_buffer12.wkt
   trunk/doc/html/image_src/st_buffer13.wkt
Modified:
   trunk/NEWS
   trunk/doc/html/image_src/Makefile.in
   trunk/doc/reference_processing.xml
   trunk/postgis/lwgeom_geos.c
   trunk/regress/regress_buffer_params.sql
   trunk/regress/regress_buffer_params_expected
Log:
Add single-sided buffer option

Patch from Stephen Knox, modified by me
Resolves #3989


Modified: trunk/NEWS
===================================================================
--- trunk/NEWS	2018-02-13 17:52:29 UTC (rev 16379)
+++ trunk/NEWS	2018-02-15 03:03:49 UTC (rev 16380)
@@ -2,6 +2,7 @@
 2018/xx/xx
 
 * New Features *
+  - #3989, ST_Buffer single sided option (Stephen Knox)
   - #3876, ST_Angle function (RĂ©mi Cura)
   - #3564, ST_LineInterpolatePoints (Dan Baston)
   - #3896, PostGIS_Extensions_Upgrade()

Modified: trunk/doc/html/image_src/Makefile.in
===================================================================
--- trunk/doc/html/image_src/Makefile.in	2018-02-13 17:52:29 UTC (rev 16379)
+++ trunk/doc/html/image_src/Makefile.in	2018-02-15 03:03:49 UTC (rev 16380)
@@ -33,6 +33,11 @@
 	../images/st_buffer06.png \
 	../images/st_buffer07.png \
 	../images/st_buffer08.png \
+	../images/st_buffer09.png \
+	../images/st_buffer10.png \
+	../images/st_buffer11.png \
+	../images/st_buffer12.png \
+	../images/st_buffer13.png \
 	../images/st_buildarea01.png \
 	../images/st_buildarea02.png \
 	../images/st_closestpoint01.png \

Added: trunk/doc/html/image_src/st_buffer09.wkt
===================================================================
--- trunk/doc/html/image_src/st_buffer09.wkt	                        (rev 0)
+++ trunk/doc/html/image_src/st_buffer09.wkt	2018-02-15 03:03:49 UTC (rev 16380)
@@ -0,0 +1,2 @@
+Style2;POLYGON((150 50,150 150,50 50,42.9289321881345 57.0710678118655,142.928932188135 157.071067811865,144.444297669804 158.314696123025,146.173165676349 159.238795325113,148.049096779839 159.807852804032,150 160,151.950903220161 159.807852804032,153.826834323651 159.238795325113,155.555702330196 158.314696123025,157.071067811865 157.071067811865,158.314696123025 155.555702330196,159.238795325113 153.826834323651,159.807852804032 151.950903220161,160 150,160 50,150 50))
+Style1-thinline;LINESTRING(50 50,150 150,150 50)
\ No newline at end of file

Added: trunk/doc/html/image_src/st_buffer10.wkt
===================================================================
--- trunk/doc/html/image_src/st_buffer10.wkt	                        (rev 0)
+++ trunk/doc/html/image_src/st_buffer10.wkt	2018-02-15 03:03:49 UTC (rev 16380)
@@ -0,0 +1,2 @@
+Style2;POLYGON((50 50,150 150,150 50,140 50,140 125.857864376269,57.0710678118655 42.9289321881345,50 50))
+Style1-thinline;LINESTRING(50 50,150 150,150 50)
\ No newline at end of file

Added: trunk/doc/html/image_src/st_buffer11.wkt
===================================================================
--- trunk/doc/html/image_src/st_buffer11.wkt	                        (rev 0)
+++ trunk/doc/html/image_src/st_buffer11.wkt	2018-02-15 03:03:49 UTC (rev 16380)
@@ -0,0 +1,2 @@
+Style2;POLYGON((150 50,150 150,50 50,42.9289321881345 57.0710678118655,160 174.142135623731,160 50,150 50))
+Style1-thinline;LINESTRING(50 50,150 150,150 50)
\ No newline at end of file

Added: trunk/doc/html/image_src/st_buffer12.wkt
===================================================================
--- trunk/doc/html/image_src/st_buffer12.wkt	                        (rev 0)
+++ trunk/doc/html/image_src/st_buffer12.wkt	2018-02-15 03:03:49 UTC (rev 16380)
@@ -0,0 +1,2 @@
+Style2;POLYGON((50 50,30 50,30 170,170 170,170 30,50 30,50 50),(50 50,150 50,150 150,50 150,50 50))
+Style1-thinline;LINESTRING(50 50, 50 150, 150 150, 150 50, 50 50)
\ No newline at end of file

Added: trunk/doc/html/image_src/st_buffer13.wkt
===================================================================
--- trunk/doc/html/image_src/st_buffer13.wkt	                        (rev 0)
+++ trunk/doc/html/image_src/st_buffer13.wkt	2018-02-15 03:03:49 UTC (rev 16380)
@@ -0,0 +1,2 @@
+Style2;POLYGON((50 50,50 70,50 150,150 150,150 50,70 50,50 50),(70 70,130 70,130 130,70 130,70 70))
+Style1-thinline;LINESTRING(50 50, 50 150, 150 150, 150 50, 50 50)
\ No newline at end of file

Modified: trunk/doc/reference_processing.xml
===================================================================
--- trunk/doc/reference_processing.xml	2018-02-13 17:52:29 UTC (rev 16379)
+++ trunk/doc/reference_processing.xml	2018-02-15 03:03:49 UTC (rev 16380)
@@ -88,6 +88,10 @@
 <listitem>
 <para>'mitre_limit=#.#' : mitre ratio limit (only affects mitered join style). 'miter_limit' is also accepted as a synonym for 'mitre_limit'.</para>
 </listitem>
+<listitem>
+<para>'side=both|left|right' : 'left' or 'right' performs a single-sided buffer on the geometry, with the buffered side relative to the direction of the line.
+This is only really relevant to LINESTRING geometry and does not affect POINT or POLYGON geometries. By default end caps are square.</para>
+</listitem>
 </itemizedlist>
 				</para>
 
@@ -238,6 +242,91 @@
 				</programlisting>
 						  </para></entry>
 					  </row>
+						<row>
+						<entry><para><informalfigure>
+							<mediaobject>
+							  <imageobject>
+								<imagedata fileref="images/st_buffer09.png" />
+							  </imageobject>
+							  <caption><para>side=left</para></caption>
+							</mediaobject>
+						  </informalfigure>
+				<programlisting>
+SELECT ST_Buffer(
+ ST_GeomFromText(
+  'LINESTRING(50 50,150 150,150 50)'
+ ), 10, 'side=left');
+				</programlisting>
+						  </para></entry>
+
+						<entry><para><informalfigure>
+							<mediaobject>
+							  <imageobject>
+								<imagedata fileref="images/st_buffer10.png" />
+							  </imageobject>
+							  <caption><para>side=right</para></caption>
+							</mediaobject>
+						  </informalfigure>
+				<programlisting>
+SELECT ST_Buffer(
+ ST_GeomFromText(
+  'LINESTRING(50 50,150 150,150 50)'
+ ), 10, 'side=right');
+				</programlisting>
+						  </para></entry>
+
+						<entry><para><informalfigure>
+							<mediaobject>
+							  <imageobject>
+								<imagedata fileref="images/st_buffer11.png" />
+							  </imageobject>
+							  <caption><para>side=left join=mitre</para></caption>
+							</mediaobject>
+						  </informalfigure>
+				<programlisting>
+SELECT ST_Buffer(
+ ST_GeomFromText(
+  'LINESTRING(50 50,150 150,150 50)'
+ ), 10, 'side=left join=mitre');
+				</programlisting>
+						  </para></entry>
+					  </row>
+						<row>
+						<entry><para><informalfigure>
+							<mediaobject>
+							  <imageobject>
+								<imagedata fileref="images/st_buffer12.png" />
+							  </imageobject>
+							  <caption><para>right-hand-winding, polygon boundary side=left</para></caption>
+							</mediaobject>
+						  </informalfigure>
+				<programlisting>
+SELECT ST_Buffer(
+ST_ForceRHR(
+ST_Boundary(
+ ST_GeomFromText(
+'POLYGON ((50 50, 50 150, 150 150, 150 50, 50 50))'))),
+ ), 20, 'side=left');
+				</programlisting>
+						  </para></entry>
+						<entry><para><informalfigure>
+							<mediaobject>
+							  <imageobject>
+								<imagedata fileref="images/st_buffer13.png" />
+							  </imageobject>
+							  <caption><para>right-hand-winding, polygon boundary side=right</para></caption>
+							</mediaobject>
+						  </informalfigure>
+				<programlisting>
+SELECT ST_Buffer(
+ST_ForceRHR(
+ST_Boundary(
+ ST_GeomFromText(
+'POLYGON ((50 50, 50 150, 150 150, 150 50, 50 50))'))
+), 20,'side=right')
+				</programlisting>
+				</para></entry>
+				</row>
 					</tbody>
 				  </tgroup>
 			</informaltable>

Modified: trunk/postgis/lwgeom_geos.c
===================================================================
--- trunk/postgis/lwgeom_geos.c	2018-02-13 17:52:29 UTC (rev 16379)
+++ trunk/postgis/lwgeom_geos.c	2018-02-15 03:03:49 UTC (rev 16380)
@@ -811,10 +811,12 @@
 {
 	GSERIALIZED	*geom1;
 	double	size;
-	GEOSGeometry *g1, *g3;
+	GEOSBufferParams *bufferparams;
+	GEOSGeometry *g1, *g3 = NULL;
 	GSERIALIZED *result;
 	int quadsegs = 8; /* the default */
 	int nargs;
+	int singleside = 0; /* the default */
 	enum
 	{
 	    ENDCAP_ROUND = 1,
@@ -950,12 +952,37 @@
 				/* quadrant segments is an int */
 				quadsegs = atoi(val);
 			}
+			else if ( !strcmp(key, "side") ||
+					  !strcmp(key, "side") )
+			{
+				if ( !strcmp(val, "both") )
+				{
+					singleside = 0;
+				}
+				else if ( !strcmp(val, "left") )
+				{
+					singleside = 1;
+				}
+				else if ( !strcmp(val, "right") )
+				{
+					singleside = 1;
+					size *= -1;
+				}
+				else
+				{
+					lwpgerror("Invalid side "
+					        "parameter: %s (accept: "
+					        "'right', 'left', 'both' "
+					        ")", val);
+					break;
+				}
+			}
 			else
 			{
 				lwpgerror("Invalid buffer parameter: %s (accept: "
 				        "'endcap', 'join', 'mitre_limit', "
-				        "'miter_limit and "
-				        "'quad_segs')", key);
+				        "'miter_limit', 'quad_segs' and "
+				        "'side')", key);
 				break;
 			}
 		}
@@ -967,7 +994,28 @@
 
 	}
 
-	g3 = GEOSBufferWithStyle(g1, size, quadsegs, endCapStyle, joinStyle, mitreLimit);
+	bufferparams = GEOSBufferParams_create();
+	if (bufferparams)
+	{
+		if (GEOSBufferParams_setEndCapStyle(bufferparams, endCapStyle) &&
+			GEOSBufferParams_setJoinStyle(bufferparams, joinStyle) &&
+			GEOSBufferParams_setMitreLimit(bufferparams, mitreLimit) &&
+			GEOSBufferParams_setQuadrantSegments(bufferparams, quadsegs) &&
+			GEOSBufferParams_setSingleSided(bufferparams, singleside))
+		{
+			g3 = GEOSBufferWithParams(g1, bufferparams, size);
+		}
+		else
+		{
+			lwpgerror("Error setting buffer parameters.");
+		}
+		GEOSBufferParams_destroy(bufferparams);
+	}
+	else
+	{
+		lwpgerror("Error setting buffer parameters.");
+	}
+
 	GEOSGeom_destroy(g1);
 
 	if (!g3) HANDLE_GEOS_ERROR("GEOSBuffer");

Modified: trunk/regress/regress_buffer_params.sql
===================================================================
--- trunk/regress/regress_buffer_params.sql	2018-02-13 17:52:29 UTC (rev 16379)
+++ trunk/regress/regress_buffer_params.sql	2018-02-15 03:03:49 UTC (rev 16380)
@@ -10,9 +10,14 @@
 SELECT 'line quadsegs=2 endcap=flat', ST_AsText(ST_SnapToGrid(st_buffer('LINESTRING(0 0, 10 0)', 2, 'quad_segs=2 endcap=flat'), 1.0e-6));
 SELECT 'line quadsegs=2 endcap=butt', ST_AsText(ST_SnapToGrid(st_buffer('LINESTRING(0 0, 10 0)', 2, 'quad_segs=2 endcap=butt'), 1.0e-6));
 SELECT 'line quadsegs=2 endcap=square', ST_AsText(ST_SnapToGrid(st_buffer('LINESTRING(0 0, 10 0)', 2, 'quad_segs=2 endcap=square'), 1.0e-6));
+SELECT 'line join=mitre mitre_limit=1.0 side=both', ST_AsText(ST_SnapToGrid(ST_Buffer('LINESTRING(50 50,150 150,150 50)',10,'join=mitre mitre_limit=1.0 side=both'), 1.0e-6));
+SELECT 'line side=left',ST_AsText(ST_SnapToGrid(ST_Buffer('LINESTRING(50 50,150 150,150 50)',10,'side=left'),1.0e-6));
+SELECT 'line side=right',ST_AsText(ST_SnapToGrid(ST_Buffer('LINESTRING(50 50,150 150,150 50)',10,'side=right'),1.0e-6));
+SELECT 'line side=left join=mitre',ST_AsText(ST_SnapToGrid(ST_Buffer('LINESTRING(50 50,150 150,150 50)',10,'side=left join=mitre'),1.0e-6));
 SELECT 'poly quadsegs=2 join=round', ST_AsText(ST_SnapToGrid(st_buffer('POLYGON((0 0, 10 0, 10 10, 0 10, 0 0))', 2, 'quad_segs=2 join=round'), 1.0e-6));
 SELECT 'poly quadsegs=2 join=bevel', ST_AsText(ST_SnapToGrid(st_buffer('POLYGON((0 0, 10 0, 10 10, 0 10, 0 0))', 2, 'quad_segs=2 join=bevel'), 1.0e-6));
 SELECT 'poly quadsegs=2 join=mitre', ST_AsText(ST_SnapToGrid(st_buffer('POLYGON((0 0, 10 0, 10 10, 0 10, 0 0))', 2, 'quad_segs=2 join=mitre'), 1.0e-6));
 SELECT 'poly quadsegs=2 join=mitre mitre_limit=1', ST_AsText(ST_SnapToGrid(st_buffer('POLYGON((0 0, 10 0, 10 10, 0 10, 0 0))', 2, 'quad_segs=2 join=mitre mitre_limit=1'), 1.0e-6));
 SELECT 'poly quadsegs=2 join=miter miter_limit=1', ST_AsText(ST_SnapToGrid(st_buffer('POLYGON((0 0, 10 0, 10 10, 0 10, 0 0))', 2, 'quad_segs=2 join=miter miter_limit=1'), 1.0e-6));
+SELECT 'poly boundary rhr side=left', ST_AsText(ST_SnapToGrid(ST_Buffer(ST_ForceRHR(ST_Boundary('POLYGON ((20 20, 20 40, 40 40, 40 40, 40 20, 20 20))')),10,'join=mitre side=left'),1.0e-6));
 

Modified: trunk/regress/regress_buffer_params_expected
===================================================================
--- trunk/regress/regress_buffer_params_expected	2018-02-13 17:52:29 UTC (rev 16379)
+++ trunk/regress/regress_buffer_params_expected	2018-02-15 03:03:49 UTC (rev 16380)
@@ -3,8 +3,13 @@
 line quadsegs=2 endcap=flat|POLYGON((10 2,10 -2,0 -2,0 2,10 2))
 line quadsegs=2 endcap=butt|POLYGON((10 2,10 -2,0 -2,0 2,10 2))
 line quadsegs=2 endcap=square|POLYGON((10 2,12 2,12 -2,0 -2,-2 -2,-2 2,10 2))
+line join=mitre mitre_limit=1.0 side=both|POLYGON((148.123573 161.601164,159.530096 156.876427,160 50,159.807853 48.049097,159.238795 46.173166,158.314696 44.444298,157.071068 42.928932,155.555702 41.685304,153.826834 40.761205,151.950903 40.192147,150 40,148.049097 40.192147,146.173166 40.761205,144.444298 41.685304,142.928932 42.928932,141.685304 44.444298,140.761205 46.173166,140.192147 48.049097,140 50,140 125.857864,57.071068 42.928932,55.555702 41.685304,53.826834 40.761205,51.950903 40.192147,50 40,48.049097 40.192147,46.173166 40.761205,44.444298 41.685304,42.928932 42.928932,41.685304 44.444298,40.761205 46.173166,40.192147 48.049097,40 50,40.192147 51.950903,40.761205 53.826834,41.685304 55.555702,42.928932 57.071068,148.123573 161.601164))
+line side=left|POLYGON((150 50,150 150,50 50,42.928932 57.071068,142.928932 157.071068,144.444298 158.314696,146.173166 159.238795,148.049097 159.807853,150 160,151.950903 159.807853,153.826834 159.238795,155.555702 158.314696,157.071068 157.071068,158.314696 155.555702,159.238795 153.826834,159.807853 151.950903,160 150,160 50,150 50))
+line side=right|POLYGON((50 50,150 150,150 50,140 50,140 125.857864,57.071068 42.928932,50 50))
+line side=left join=mitre|POLYGON((150 50,150 150,50 50,42.928932 57.071068,160 174.142136,160 50,150 50))
 poly quadsegs=2 join=round|POLYGON((-2 0,-2 10,-1.414214 11.414214,0 12,10 12,11.414214 11.414214,12 10,12 0,11.414214 -1.414214,10 -2,0 -2,-1.414214 -1.414214,-2 0))
 poly quadsegs=2 join=bevel|POLYGON((-2 0,-2 10,0 12,10 12,12 10,12 0,10 -2,0 -2,-2 0))
 poly quadsegs=2 join=mitre|POLYGON((-2 -2,-2 12,12 12,12 -2,-2 -2))
 poly quadsegs=2 join=mitre mitre_limit=1|POLYGON((-1.828427 -1,-1.828427 11,-1 11.828427,11 11.828427,11.828427 11,11.828427 -1,11 -1.828427,-1 -1.828427,-1.828427 -1))
 poly quadsegs=2 join=miter miter_limit=1|POLYGON((-1.828427 -1,-1.828427 11,-1 11.828427,11 11.828427,11.828427 11,11.828427 -1,11 -1.828427,-1 -1.828427,-1.828427 -1))
+poly boundary rhr side=left|POLYGON((20 20,10 20,10 50,50 50,50 10,20 10,20 20),(20 20,40 20,40 40,20 40,20 20))



More information about the postgis-tickets mailing list