Turfjs / turf

A modular geospatial engine written in JavaScript and TypeScript
https://turfjs.org/
MIT License
9.26k stars 937 forks source link

can provide options to control the behavior of buffer like 'miter' #2434

Open touchpillow opened 1 year ago

touchpillow commented 1 year ago

SeaTalk_IMG_1683367257

I use buffer to do some improvement now , and I found that buffer use 'bevel' ,and we can pass steps parameters , but it will change the shape of polygon.

can buffer provide a more options to controls the shape of intersection, just like the pictures.

JerryWang24 commented 1 year ago

Have you found a solution yet?

touchpillow commented 1 year ago

Have you found a solution yet?

not yet, didn't find a perfect solution yet.

JamesLMilner commented 1 year ago

So my understanding is @turf/buffer does not support this - but you may be able to have some luck with underlying JSTS module: https://www.npmjs.com/package/@turf/jsts :

We do this internally like:

  var buffered = BufferOp.bufferOp(geom, distance, steps);

You'll have to look at the original JTS documentation to figure out the correct options to pass: http://locationtech.github.io/jts/javadoc/

But i think the 4th option is the join type:

https://github.com/bjornharrtell/jsts/blob/ad5af47313caad5e28d897a6c20ce7c9fb2b496f/src/org/locationtech/jts/operation/buffer/BufferOp.js#L52

Would assume these are integers like 0, 1, 2 etc but you'd have to experiment as I can't see the actual values anywhere.

moosetraveller commented 4 months ago

The issue #639 (buffer endcap and join strategies) also addresses this limitation. Based on a @JamesLMilner's comment and from a patch file that @axekan a comment posted in issue 639, I created an example using JSTS.

It is using JSTS to create the buffer and was original written for a StackOverflow answer but ended up not matching with the OP's requirement. However, I would like to share the code as it could be useful for others. For the example, you'll need JSTS and Proj4js along with Turfjs:

<script src="https://cdn.jsdelivr.net/npm/@turf/turf@6/turf.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/jsts@2.6.1/dist/jsts.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/proj4js/2.7.5/proj4.js"></script>

Here is a JavaScript function buffer(...) that takes a (GeoJSON) feature (with WGS84 coordinates) and creates a buffer (using metres; coordinates are temporarly projected to Web Mercator, replace to a more appropriate coordinate system if needed):

// to shorten line width in code example below
const { GeoJSONReader, GeoJSONWriter, WKTReader } = jsts.io;
const { BufferOp, BufferParameters } = jsts.operation.buffer;

/**
 * Creates buffer from given polygon feature with lat/lon coordinates using 
 * a buffer distance in metres, temporarly projects the feature to an 
 * appropriate coordinate system. Returns a turf.polygon object.
 */
function buffer(polygonFeature, distance, parameters, tempProjEPSG='EPSG:3857') {

    const [ coordinates ] = polygon.coordinates;

    const inProj = proj4.defs('EPSG:4326');
    const tempProj = proj4.defs(tempProjEPSG);

    const projectedPolygon = turf.polygon([
        project(coordinates, inProj, tempProj)
    ]);

    // transform to a JSTS geometry object
    const geometry = new GeoJSONReader()
        .read(JSON.stringify(projectedPolygon.geometry));

    const bufferOp = new BufferOp(geometry, parameters);
    const bufferedGeom = bufferOp.getResultGeometry(distance);

    // retrieve coordinates from JSTS geometry object
    const [ bufferCoordinates ] = new GeoJSONWriter()
        .write(bufferedGeom)
        .coordinates;

    return turf.polygon([
        project(bufferCoordinates, tempProj, inProj)
    ]);

}

/**
 * Projects the given coordinates from one coordinate system to another one.
 */
function project(coordinates, inProj, outProj) {
    return coordinates.map(coord => proj4(inProj, outProj, coord));
}

In your code, you then use the function buffer:

// example polygon
const wkt = 'POLYGON((8.504 47.362, 8.508 47.363, 8.511 47.361, 8.504 47.362))'; 
const geometry = new WKTReader().read(wkt);
const geojsonPolygon = new GeoJSONWriter().write(geometry);

// buffer parameters
const bufferParameters = new BufferParameters(
    8,
    BufferParameters.CAP_FLAT,
    BufferParameters.JOIN_MITRE,
    10,
);

// assuming you want a buffer of 500 metre
const bufferPolygon = buffer(geojsonPolygon, 500, bufferParameters);

console.log(bufferPolygon)