]> granicus.if.org Git - postgis/commitdiff
Add single-sided buffer option
authorDaniel Baston <dbaston@gmail.com>
Thu, 15 Feb 2018 03:03:49 +0000 (03:03 +0000)
committerDaniel Baston <dbaston@gmail.com>
Thu, 15 Feb 2018 03:03:49 +0000 (03:03 +0000)
Patch from Stephen Knox, modified by me
Resolves #3989

git-svn-id: http://svn.osgeo.org/postgis/trunk@16380 b70326c6-7e19-0410-871a-916f4a2858ee

NEWS
doc/html/image_src/Makefile.in
doc/html/image_src/st_buffer09.wkt [new file with mode: 0644]
doc/html/image_src/st_buffer10.wkt [new file with mode: 0644]
doc/html/image_src/st_buffer11.wkt [new file with mode: 0644]
doc/html/image_src/st_buffer12.wkt [new file with mode: 0644]
doc/html/image_src/st_buffer13.wkt [new file with mode: 0644]
doc/reference_processing.xml
postgis/lwgeom_geos.c
regress/regress_buffer_params.sql
regress/regress_buffer_params_expected

diff --git a/NEWS b/NEWS
index cc526e4da2ad132152e40f8e2703046553fc7260..9696f67732f0f1ad4d917077bd0a5cdfc6fd8089 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -2,6 +2,7 @@ PostGIS 2.5.0
 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()
index a895274e871c4fc0985ae870acce7865b7cc3573..2cc12c267f84a27505b1fd246268d03692287a88 100644 (file)
@@ -33,6 +33,11 @@ IMAGES= \
        ../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 \
diff --git a/doc/html/image_src/st_buffer09.wkt b/doc/html/image_src/st_buffer09.wkt
new file mode 100644 (file)
index 0000000..84daa3d
--- /dev/null
@@ -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
diff --git a/doc/html/image_src/st_buffer10.wkt b/doc/html/image_src/st_buffer10.wkt
new file mode 100644 (file)
index 0000000..f9d3179
--- /dev/null
@@ -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
diff --git a/doc/html/image_src/st_buffer11.wkt b/doc/html/image_src/st_buffer11.wkt
new file mode 100644 (file)
index 0000000..0d4f047
--- /dev/null
@@ -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
diff --git a/doc/html/image_src/st_buffer12.wkt b/doc/html/image_src/st_buffer12.wkt
new file mode 100644 (file)
index 0000000..25c22c3
--- /dev/null
@@ -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
diff --git a/doc/html/image_src/st_buffer13.wkt b/doc/html/image_src/st_buffer13.wkt
new file mode 100644 (file)
index 0000000..fa1e958
--- /dev/null
@@ -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
index 834e954ea4034e08c7493789e105f2ba100406f2..cdd28a2196e1373e086f60ff31d3c08baf5107cf 100644 (file)
@@ -88,6 +88,10 @@ The optional third parameter (currently only applies to geometry) can either spe
 <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 @@ SELECT ST_Buffer(
                                </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>
index 856bc3ab6a843d72c28f0b3b3d8f924bc9575966..c2a3420f84934018f29b79b44597426c2855e3b1 100644 (file)
@@ -811,10 +811,12 @@ Datum buffer(PG_FUNCTION_ARGS)
 {
        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 @@ Datum buffer(PG_FUNCTION_ARGS)
                                /* 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 @@ Datum buffer(PG_FUNCTION_ARGS)
 
        }
 
-       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");
index 6cdfaf0bc24979029ac02060de1247012bffa159..799d5391ac0ceafe69fc15657a42ac83e9b5e6ae 100644 (file)
@@ -10,9 +10,14 @@ SELECT 'line quadsegs=2', ST_AsText(ST_SnapToGrid(st_buffer('LINESTRING(0 0, 10
 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));
 
index 0a8866308781babc6165e456429b3e3cadb58161..e2e75d318337d26087002cec4a8e14be6b9d98b8 100644 (file)
@@ -3,8 +3,13 @@ line quadsegs=2|POLYGON((10 2,11.414214 1.414214,12 0,11.414214 -1.414214,10 -2,
 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))