Commit d884ff8f authored by Dan Baston's avatar Dan Baston

Add single-sided buffer option

Patch from Stephen Knox, modified by me
Resolves #3989


git-svn-id: http://svn.osgeo.org/postgis/trunk@16380 b70326c6-7e19-0410-871a-916f4a2858ee
parent 79c879e0
Pipeline #17555201 passed with stage
in 16 minutes and 4 seconds
......@@ -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()
......
......@@ -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 \
......
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
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
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
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
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
......@@ -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>
......
......@@ -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");
......
......@@ -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));
......@@ -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))
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment