Turfjs / turf

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

Self-intersection linestring's buffer is not exactly? #2075

Open wtusmchen opened 3 years ago

wtusmchen commented 3 years ago

Please provide the following when reporting an issue:

turf 6.3.0 let buffered = turf.buffer(fea, 100, {units:'kilometers'});

0001

chrispahm commented 3 years ago

Hey @wtusmchen,

TLDR; use one of the solutions outlined in this notebook https://observablehq.com/@chrispahm/turf-issue-2075

I'm just guessing here, but since Turf >v.6.2.0, GeoJSON input objects are projected into Azimuthal Equidistant projection instead of Transverse Mercator which has been used in previous versions < 5.1.6 (see #1956). While this fixes a number of issues related to the buffer operation (e.g. #1246, #1470, #1484, #1547), it may be responsible for the somewhat unexpected result you're seeing.

It looks as if the intersection of the west-east facing line and the south-north facing line in your example are internally corrected for their actual geodesic position (see the mapbox-gl-draw-plugin-geodesic to draw geodesic lines) image Wich explains why the buffer intersection is further north than where it would be expected given the straight line.

This theory is (somewhat) confirmed by the fact that if you move your shape towards the equator, it's following the straight line accordingly image

Anyways, there are (at least) four possible solutions for your problem: 1) You can add a point to your lineString at the intersection of the east-west/south-north line prior to applying the buffer operation 2) You can apply the buffer operation for each segment of the lineString, and concatenate the lineStrings to a single one using turf.union

const arrayOfLineStrings = turf.segmentReduce(data[ls], (buffers, currentSegment, i) => {
    const segmentBuffer = turf.buffer(currentSegment, 100);
    buffers.push(segmentBuffer);
    return buffers;
  }, [])

const linestringBuffer = arrayOfLineStrings.reduce((u, c) => turf.union(u, c), arrayOfLineStrings[0])

3) You may want to display your lineString using a software/service that supports geodesic lines (e.g. mapbox-gl-draw-plugin-geodesic) 4) You can downgrade to Turf <v.5.1.6, which does not incorporate the fixes mentioned above. Note that in this case the size of your buffer will not be truly equal to 100km (or whichever size you need).

Hope this helps!