Skip to content

Commit

Permalink
Merge pull request #439 from ebocher/st_bufferside
Browse files Browse the repository at this point in the history
Add two functions : ST_SideBuffer and ST_RingSideBuffer


ST_SideBuffer permits to create a buffer on one side of the geometry (negative or positive offset).

ST_RingSideBuffer offers a way to compute, as ST_RingBuffer does, a set of rings but on one side of the geometry.

By the way, ST_RingBuffer has been improved to support negative offset. The user can now create a ring buffer inside a polygon.

Note : this 3 functions support only single geometry (point, line or polygon). If a polygon contains a hole, the buffer will be based only on the external ring of it.
  • Loading branch information
nicolas-f committed Jan 7, 2015
2 parents c4b8237 + cba6235 commit 3452083
Show file tree
Hide file tree
Showing 5 changed files with 563 additions and 44 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,9 @@ public static Function[] getBuiltInsFunctions() throws SQLException {
new ST_ProjectPoint(),
new ST_CollectExtract(),
new DoubleRange(),
new IntegerRange()};
new IntegerRange(),
new ST_SideBuffer(),
new ST_RingSideBuffer()};
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,7 @@
package org.h2gis.h2spatialext.function.spatial.create;

import com.vividsolutions.jts.geom.Geometry;
import com.vividsolutions.jts.geom.GeometryFactory;
import com.vividsolutions.jts.geom.MultiPolygon;
import com.vividsolutions.jts.geom.Point;
import com.vividsolutions.jts.geom.Polygon;
import com.vividsolutions.jts.operation.buffer.BufferOp;
import com.vividsolutions.jts.operation.buffer.BufferParameters;
Expand All @@ -39,17 +38,16 @@
* @author Erwan Bocher
*/
public class ST_RingBuffer extends AbstractFunction implements ScalarFunction {

private static final String CAP_STYLE_SQUARE = "square";
private static final String CAP_STYLE_ROUND = "round";
private static final GeometryFactory GF = new GeometryFactory();


public ST_RingBuffer() {
addProperty(PROP_REMARKS, "Compute a ring buffer around a geometry.\n"
+ "Avalaible arguments are :\n"
+ " (1) the geometry, (2) the size of each ring, "
+ " (3) the number of rings, (4) optional - the end cap style (square, round) Default is round\n"+
" (5) optional - createHole True if you want to keep only difference between buffers Default is true");
+ " (3) the number of rings, (4) optional - the end cap style (square, round) Default is round\n"
+ "a list of blank-separated key=value pairs (string case) iso used t manage line style parameters.\n "
+ "Please read the ST_Buffer documention.\n"
+ " (5) optional - createHole True if you want to keep only difference between buffers Default is true.\n"
+ "Note : Holes are not supported by this function.");
}

@Override
Expand All @@ -63,79 +61,171 @@ public String getJavaStaticMethod() {
* @param bufferSize
* @param numBuffer
* @return
* @throws java.sql.SQLException
*/
public static Geometry ringBuffer(Geometry geom, double bufferSize, int numBuffer) throws SQLException {
return ringBuffer(geom, bufferSize, numBuffer, CAP_STYLE_ROUND);
return ringBuffer(geom, bufferSize, numBuffer, "endcap=round");
}

/**
*
* @param geom
* @param bufferDistance
* @param numBuffer
* @param endCapStyle
* @param parameters
* @return
* @throws java.sql.SQLException
*/
public static Geometry ringBuffer(Geometry geom, double bufferDistance,
int numBuffer, String endCapStyle) throws SQLException {
return ringBuffer(geom, bufferDistance, numBuffer, endCapStyle, true);
int numBuffer, String parameters) throws SQLException {
return ringBuffer(geom, bufferDistance, numBuffer, parameters, true);
}

/**
* Compute a ring buffer around a geometry
* @param geom
* @param bufferDistance
* @param numBuffer
* @param endCapStyle
* @param parameters
* @param doDifference
* @throws SQLException
* @return
*/
public static Geometry ringBuffer(Geometry geom, double bufferDistance,
int numBuffer, String endCapStyle, boolean doDifference) throws SQLException {
if(!(bufferDistance > 0)) {
// If buffer distance is not superior than zero return the same geometry.
return geom;
int numBuffer, String parameters, boolean doDifference) throws SQLException {
if(geom==null){
return null;
}
if (geom.getNumGeometries() > 1) {
throw new SQLException("This function supports only single geometry : point, linestring or polygon.");
} else {
String[] buffParemeters = parameters.split("\\s+");
BufferParameters bufferParameters = new BufferParameters();
for (String params : buffParemeters) {
String[] keyValue = params.split("=");
if (keyValue[0].equalsIgnoreCase("endcap")) {
String param = keyValue[1];
if (param.equalsIgnoreCase("round")) {
bufferParameters.setEndCapStyle(BufferParameters.CAP_ROUND);
} else if (param.equalsIgnoreCase("square")) {
bufferParameters.setEndCapStyle(BufferParameters.CAP_SQUARE);
} else {
throw new IllegalArgumentException("Supported join values are round or square.");
}
} else if (keyValue[0].equalsIgnoreCase("join")) {
String param = keyValue[1];
if (param.equalsIgnoreCase("bevel")) {
bufferParameters.setJoinStyle(BufferParameters.JOIN_BEVEL);
} else if (param.equalsIgnoreCase("mitre") || param.equalsIgnoreCase("miter")) {
bufferParameters.setJoinStyle(BufferParameters.JOIN_MITRE);
} else if (param.equalsIgnoreCase("round")) {
bufferParameters.setJoinStyle(BufferParameters.JOIN_ROUND);
} else {
throw new IllegalArgumentException("Supported join values are bevel, mitre, miter or round.");
}
} else if (keyValue[0].equalsIgnoreCase("mitre_limit") || keyValue[0].equalsIgnoreCase("miter_limit")) {
bufferParameters.setMitreLimit(Double.valueOf(keyValue[1]));
} else if (keyValue[0].equalsIgnoreCase("quad_segs")) {
bufferParameters.setQuadrantSegments(Integer.valueOf(keyValue[1]));
} else {
throw new IllegalArgumentException("Unknown parameters. Please read the documentation.");
}
}
if (bufferDistance > 0) {
return computePositiveRingBuffer(geom, bufferDistance, numBuffer, bufferParameters, doDifference);
} else if (bufferDistance < 0) {
if (geom instanceof Point) {
throw new SQLException("Cannot compute a negative ring buffer on a point.");
} else {
return computeNegativeRingBuffer(geom, bufferDistance, numBuffer, bufferParameters, doDifference);
}
} else {
return geom;
}
}
}

/**
* Compute a ring buffer with a positive offset
*
* @param geom
* @param bufferDistance
* @param numBuffer
* @param bufferParameters
* @param doDifference
* @return
* @throws SQLException
*/
public static Geometry computePositiveRingBuffer(Geometry geom, double bufferDistance,
int numBuffer, BufferParameters bufferParameters, boolean doDifference) throws SQLException {
Polygon[] buffers = new Polygon[numBuffer];
if (geom instanceof Polygon) {
//Work arround to manage polygon with hole
geom = geom.getFactory().createPolygon(((Polygon) geom).getExteriorRing().getCoordinateSequence());
}
Polygon[] buffers = new Polygon[numBuffer];
Geometry previous = geom;
double distance = 0;
for (int i = 0; i < numBuffer; i++) {
distance += bufferDistance;
Geometry newBuffer = runBuffer(geom, distance, endCapStyle);
if(doDifference) {
Geometry newBuffer = runBuffer(geom, distance, bufferParameters);
if (doDifference) {
buffers[i] = (Polygon) newBuffer.difference(previous);
} else {
buffers[i] = (Polygon) newBuffer;
}
previous = newBuffer;
}
return GF.createMultiPolygon(buffers);
return geom.getFactory().createMultiPolygon(buffers);
}

/**
* Compute a ring buffer with a negative offset
*
* @param geom
* @param bufferDistance
* @param numBuffer
* @param bufferParameters
* @param doDifference
* @return
* @throws SQLException
*/
public static Geometry computeNegativeRingBuffer(Geometry geom, double bufferDistance,
int numBuffer, BufferParameters bufferParameters, boolean doDifference) throws SQLException {
Polygon[] buffers = new Polygon[numBuffer];
Geometry previous = geom;
double distance = 0;
if (geom instanceof Polygon) {
geom = ((Polygon) geom).getExteriorRing();
bufferParameters.setSingleSided(true);
}
for (int i = 0; i < numBuffer; i++) {
distance += bufferDistance;
Geometry newBuffer = runBuffer(geom, distance, bufferParameters);
if (i == 0) {
buffers[i] = (Polygon) newBuffer;
} else {
if (doDifference) {
buffers[i] = (Polygon) newBuffer.difference(previous);
} else {
buffers[i] = (Polygon) newBuffer;
}
}
previous = newBuffer;
}
return geom.getFactory().createMultiPolygon(buffers);
}

/**
* Calculate the ring buffer
*
* @param geom
* @param bufferSize
* @param endCapStyle
* @param bufferParameters
* @return
* @throws SQLException
*/
private static Geometry runBuffer(final Geometry geom, final double bufferSize,
final String endCapStyle) throws SQLException {
BufferOp bufOp;
if (endCapStyle.equalsIgnoreCase(CAP_STYLE_SQUARE)) {
bufOp = new BufferOp(geom, new BufferParameters(
BufferParameters.DEFAULT_QUADRANT_SEGMENTS,
BufferParameters.CAP_SQUARE));
return bufOp.getResultGeometry(bufferSize);
} else if (endCapStyle.equalsIgnoreCase(CAP_STYLE_ROUND)){
bufOp = new BufferOp(geom, new BufferParameters(
BufferParameters.DEFAULT_QUADRANT_SEGMENTS,
BufferParameters.CAP_ROUND));
return bufOp.getResultGeometry(bufferSize);
}
throw new SQLException("Invalid cap style value. Please use "+ CAP_STYLE_ROUND + " or "
+ CAP_STYLE_SQUARE);
public static Geometry runBuffer(final Geometry geom, final double bufferSize,
final BufferParameters bufferParameters) throws SQLException {
return BufferOp.bufferOp(geom, bufferSize, bufferParameters);
}
}
Loading

0 comments on commit 3452083

Please sign in to comment.