diff --git a/packages/turf-line-split/index.d.ts b/packages/turf-line-split/index.d.ts index 5833a50b7f..e4263f9978 100644 --- a/packages/turf-line-split/index.d.ts +++ b/packages/turf-line-split/index.d.ts @@ -9,7 +9,7 @@ import { MultiPolygon, } from '@turf/helpers'; -export type Splitter = Feature +export type Splitter = Feature /** * http://turfjs.org/docs/#linesplit diff --git a/packages/turf-line-split/index.js b/packages/turf-line-split/index.js index 3fd4ab2e03..56215ff3ae 100644 --- a/packages/turf-line-split/index.js +++ b/packages/turf-line-split/index.js @@ -1,11 +1,12 @@ import rbush from 'geojson-rbush'; -import flatten from '@turf/flatten'; +import square from '@turf/square'; +import bbox from '@turf/bbox'; import truncate from '@turf/truncate'; import lineSegment from '@turf/line-segment'; -import nearestPointOnLine from '@turf/nearest-point-on-line'; import lineIntersect from '@turf/line-intersect'; +import nearestPointOnLine from '@turf/nearest-point-on-line'; import { getCoords, getType } from '@turf/invariant'; -import { featureEach, featureReduce} from '@turf/meta'; +import { featureEach, featureReduce, flattenEach} from '@turf/meta'; import { lineString, featureCollection } from '@turf/helpers'; /** @@ -43,7 +44,7 @@ function lineSplit(line, splitter) { case 'Point': return splitLineWithPoint(line, truncatedSplitter); case 'MultiPoint': - return splitLineWithPoints(line, flatten(truncatedSplitter)); + return splitLineWithPoints(line, truncatedSplitter); case 'LineString': case 'MultiLineString': case 'Polygon': @@ -64,7 +65,7 @@ function splitLineWithPoints(line, splitter) { var results = []; var tree = rbush(); - featureEach(splitter, function (point) { + flattenEach(splitter, function (point) { // Add index/id to features (needed for filter) results.forEach(function (feature, index) { feature.id = index; @@ -72,25 +73,32 @@ function splitLineWithPoints(line, splitter) { // First Point - doesn't need to handle any previous line results if (!results.length) { results = splitLineWithPoint(line, point).features; + + // Add Square BBox to each feature for GeoJSON-RBush + results.forEach(function (feature) { + if (!feature.bbox) feature.bbox = square(bbox(feature)); + }); tree.load(featureCollection(results)); // Split with remaining points - lines might needed to be split multiple times } else { // Find all lines that are within the splitter's bbox var search = tree.search(point); - // RBush might return multiple lines - only process the closest line to splitter - var closestLine = findClosestFeature(point, search); - - // Remove closest line from results since this will be split into two lines - // This removes any duplicates inside the results & index - results = results.filter(function (feature) { return feature.id !== closestLine.id; }); - tree.remove(closestLine); - - // Append the two newly split lines into the results - featureEach(splitLineWithPoint(closestLine, point), function (line) { - results.push(line); - tree.insert(line); - }); + if (search.features.length) { + // RBush might return multiple lines - only process the closest line to splitter + var closestLine = findClosestFeature(point, search); + + // Remove closest line from results since this will be split into two lines + // This removes any duplicates inside the results & index + results = results.filter(function (feature) { return feature.id !== closestLine.id; }); + tree.remove(closestLine); + + // Append the two newly split lines into the results + featureEach(splitLineWithPoint(closestLine, point), function (line) { + results.push(line); + tree.insert(line); + }); + } } }); return featureCollection(results); @@ -164,7 +172,7 @@ function splitLineWithPoint(line, splitter) { * @returns {Feature} closest LineString */ function findClosestFeature(point, lines) { - if (!lines.features) throw new Error(' must contain features'); + if (!lines.features.length) throw new Error('lines must contain features'); // Filter to one segment that is the closest to the line if (lines.features.length === 1) return lines.features[0]; diff --git a/packages/turf-line-split/package.json b/packages/turf-line-split/package.json index d6e56c073b..6a16a11af7 100644 --- a/packages/turf-line-split/package.json +++ b/packages/turf-line-split/package.json @@ -37,21 +37,22 @@ }, "homepage": "https://github.com/Turfjs/turf", "devDependencies": { + "@std/esm": "*", "benchmark": "*", "load-json-file": "*", - "tape": "*", - "write-json-file": "*", "rollup": "*", - "@std/esm": "*" + "tape": "*", + "write-json-file": "*" }, "dependencies": { - "@turf/flatten": "*", + "@turf/bbox": "*", "@turf/helpers": "5.x", "@turf/invariant": "5.x", "@turf/line-intersect": "5.x", "@turf/line-segment": "5.x", "@turf/meta": "5.x", "@turf/nearest-point-on-line": "5.x", + "@turf/square": "*", "@turf/truncate": "5.x", "geojson-rbush": "2.1.0" }, diff --git a/packages/turf-line-split/test.js b/packages/turf-line-split/test.js index 885364ec15..2b319432a0 100644 --- a/packages/turf-line-split/test.js +++ b/packages/turf-line-split/test.js @@ -4,7 +4,7 @@ import path from 'path'; import load from 'load-json-file'; import write from 'write-json-file'; import { featureEach } from '@turf/meta'; -import { point, lineString, featureCollection, round } from '@turf/helpers'; +import { point, lineString, multiPoint, featureCollection, round } from '@turf/helpers'; import { getCoords } from '@turf/invariant'; import lineSplit from '.'; @@ -13,13 +13,14 @@ const directories = { out: path.join(__dirname, 'test', 'out') + path.sep }; -const fixtures = fs.readdirSync(directories.in).map(filename => { +let fixtures = fs.readdirSync(directories.in).map(filename => { return { filename, name: path.parse(filename).name, geojson: load.sync(directories.in + filename) }; }); +// fixtures = fixtures.filter(name => name === 'issue-#1075') test('turf-line-split', t => { for (const {filename, name, geojson} of fixtures) { @@ -107,6 +108,13 @@ test('turf-line-split -- prevent input mutation', t => { t.end(); }); +test('turf-line-split -- issue #1075', t => { + const line = lineString([[-87.168433, 37.946093], [-87.168510, 37.960085]]); + const splitter = multiPoint([[-87.168446, 37.947929], [-87.168445, 37.948301]]); + const split = lineSplit(line, splitter); + t.assert(split); + t.end(); +}) /** * Colorize FeatureCollection @@ -128,3 +136,4 @@ function colorize(geojson) { }); return featureCollection(results); } + diff --git a/packages/turf-line-split/test/in/issue-#1075-1.geojson b/packages/turf-line-split/test/in/issue-#1075-1.geojson new file mode 100644 index 0000000000..704da3cadf --- /dev/null +++ b/packages/turf-line-split/test/in/issue-#1075-1.geojson @@ -0,0 +1,24 @@ +{ + "type": "FeatureCollection", + "features": [ + { + "type": "Feature", + "properties": {}, + "geometry": { + "type": "LineString", + "coordinates": [[-87.168433, 37.946093], [-87.168510, 37.960085]] + } + }, + { + "type": "Feature", + "properties": {}, + "geometry": { + "type": "MultiPoint", + "coordinates": [ + [-87.168446, 37.947929], + [-87.168445, 37.948301] + ] + } + } + ] +} \ No newline at end of file diff --git a/packages/turf-line-split/test/in/issue-#1075-2.geojson b/packages/turf-line-split/test/in/issue-#1075-2.geojson new file mode 100644 index 0000000000..fa74ce5d47 --- /dev/null +++ b/packages/turf-line-split/test/in/issue-#1075-2.geojson @@ -0,0 +1,24 @@ +{ + "type": "FeatureCollection", + "features": [ + { + "type": "Feature", + "properties": {}, + "geometry": { + "type": "LineString", + "coordinates": [[-87.168433, 37.946093], [-87.168510, 37.960085]] + } + }, + { + "type": "Feature", + "properties": {}, + "geometry": { + "type": "MultiPoint", + "coordinates": [ + [-87.1684431695,37.9479287543], + [-87.1684451512,37.9483010091] + ] + } + } + ] +} \ No newline at end of file diff --git a/packages/turf-line-split/test/in/issue-#1075-3.geojson b/packages/turf-line-split/test/in/issue-#1075-3.geojson new file mode 100644 index 0000000000..8166f4deef --- /dev/null +++ b/packages/turf-line-split/test/in/issue-#1075-3.geojson @@ -0,0 +1,24 @@ +{ + "type": "FeatureCollection", + "features": [ + { + "type": "Feature", + "properties": {}, + "geometry": { + "type": "LineString", + "coordinates": [[-87.168433, 37.946093], [-87.168510, 37.960085]] + } + }, + { + "type": "Feature", + "properties": {}, + "geometry": { + "type": "MultiPoint", + "coordinates": [ + [-87.1684431695,37.9479287543], + [-87.161, 37.948] + ] + } + } + ] +} \ No newline at end of file diff --git a/packages/turf-line-split/test/out/issue-#1075-1.geojson b/packages/turf-line-split/test/out/issue-#1075-1.geojson new file mode 100644 index 0000000000..c57026ff49 --- /dev/null +++ b/packages/turf-line-split/test/out/issue-#1075-1.geojson @@ -0,0 +1,93 @@ +{ + "type": "FeatureCollection", + "features": [ + { + "type": "Feature", + "properties": { + "stroke": "#F00", + "stroke-width": 10 + }, + "geometry": { + "type": "LineString", + "coordinates": [ + [ + -87.168433, + 37.946093 + ], + [ + -87.168446, + 37.947929 + ] + ] + }, + "bbox": [ + -87.1693575, + 37.946093, + -87.1675215, + 37.947929 + ], + "id": 0 + }, + { + "type": "Feature", + "properties": { + "stroke": "#00F", + "stroke-width": 10 + }, + "geometry": { + "type": "LineString", + "coordinates": [ + [ + -87.168446, + 37.947929 + ], + [ + -87.16851, + 37.960085 + ] + ] + }, + "bbox": [ + -87.174556, + 37.947929, + -87.16239999999999, + 37.960085 + ], + "id": 1 + }, + { + "type": "Feature", + "properties": {}, + "geometry": { + "type": "LineString", + "coordinates": [ + [ + -87.168433, + 37.946093 + ], + [ + -87.16851, + 37.960085 + ] + ] + } + }, + { + "type": "Feature", + "properties": {}, + "geometry": { + "type": "MultiPoint", + "coordinates": [ + [ + -87.168446, + 37.947929 + ], + [ + -87.168445, + 37.948301 + ] + ] + } + } + ] +} diff --git a/packages/turf-line-split/test/out/issue-#1075-2.geojson b/packages/turf-line-split/test/out/issue-#1075-2.geojson new file mode 100644 index 0000000000..e26e0fd368 --- /dev/null +++ b/packages/turf-line-split/test/out/issue-#1075-2.geojson @@ -0,0 +1,118 @@ +{ + "type": "FeatureCollection", + "features": [ + { + "type": "Feature", + "properties": { + "stroke": "#F00", + "stroke-width": 10 + }, + "geometry": { + "type": "LineString", + "coordinates": [ + [ + -87.168433, + 37.946093 + ], + [ + -87.1684432, + 37.9479288 + ] + ] + }, + "bbox": [ + -87.16935600000001, + 37.946093, + -87.1675202, + 37.9479288 + ], + "id": 0 + }, + { + "type": "Feature", + "properties": { + "stroke": "#00F", + "stroke-width": 10 + }, + "geometry": { + "type": "LineString", + "coordinates": [ + [ + -87.1684432, + 37.9479288 + ], + [ + -87.1684452, + 37.948301 + ] + ] + }, + "bbox": [ + -87.1684452, + 37.9479288, + -87.1684432, + 37.948301 + ] + }, + { + "type": "Feature", + "properties": { + "stroke": "#F00", + "stroke-width": 10 + }, + "geometry": { + "type": "LineString", + "coordinates": [ + [ + -87.1684452, + 37.948301 + ], + [ + -87.16851, + 37.960085 + ] + ] + }, + "bbox": [ + -87.16851, + 37.948301, + -87.1684452, + 37.960085 + ] + }, + { + "type": "Feature", + "properties": {}, + "geometry": { + "type": "LineString", + "coordinates": [ + [ + -87.168433, + 37.946093 + ], + [ + -87.16851, + 37.960085 + ] + ] + } + }, + { + "type": "Feature", + "properties": {}, + "geometry": { + "type": "MultiPoint", + "coordinates": [ + [ + -87.1684431695, + 37.9479287543 + ], + [ + -87.1684451512, + 37.9483010091 + ] + ] + } + } + ] +} diff --git a/packages/turf-line-split/test/out/issue-#1075-3.geojson b/packages/turf-line-split/test/out/issue-#1075-3.geojson new file mode 100644 index 0000000000..005bd24095 --- /dev/null +++ b/packages/turf-line-split/test/out/issue-#1075-3.geojson @@ -0,0 +1,93 @@ +{ + "type": "FeatureCollection", + "features": [ + { + "type": "Feature", + "properties": { + "stroke": "#F00", + "stroke-width": 10 + }, + "geometry": { + "type": "LineString", + "coordinates": [ + [ + -87.168433, + 37.946093 + ], + [ + -87.1684432, + 37.9479288 + ] + ] + }, + "bbox": [ + -87.16935600000001, + 37.946093, + -87.1675202, + 37.9479288 + ], + "id": 0 + }, + { + "type": "Feature", + "properties": { + "stroke": "#00F", + "stroke-width": 10 + }, + "geometry": { + "type": "LineString", + "coordinates": [ + [ + -87.1684432, + 37.9479288 + ], + [ + -87.16851, + 37.960085 + ] + ] + }, + "bbox": [ + -87.17455469999999, + 37.9479288, + -87.1623985, + 37.960085 + ], + "id": 1 + }, + { + "type": "Feature", + "properties": {}, + "geometry": { + "type": "LineString", + "coordinates": [ + [ + -87.168433, + 37.946093 + ], + [ + -87.16851, + 37.960085 + ] + ] + } + }, + { + "type": "Feature", + "properties": {}, + "geometry": { + "type": "MultiPoint", + "coordinates": [ + [ + -87.1684431695, + 37.9479287543 + ], + [ + -87.161, + 37.948 + ] + ] + } + } + ] +} diff --git a/packages/turf-line-split/test/out/linestrings.geojson b/packages/turf-line-split/test/out/linestrings.geojson index 33ce52ce98..2fe146c1e7 100644 --- a/packages/turf-line-split/test/out/linestrings.geojson +++ b/packages/turf-line-split/test/out/linestrings.geojson @@ -22,9 +22,9 @@ }, "bbox": [ 118.6090049242457, - -32.99023555965107, + -34.00655617152344, 126.5625, - -27.06938170764152 + -26.05306109576914 ], "id": 0 }, diff --git a/packages/turf-line-split/test/out/multi-linestring.geojson b/packages/turf-line-split/test/out/multi-linestring.geojson index 0ac751ea8a..d40e4f0e06 100644 --- a/packages/turf-line-split/test/out/multi-linestring.geojson +++ b/packages/turf-line-split/test/out/multi-linestring.geojson @@ -26,9 +26,9 @@ }, "bbox": [ 115.400390625, - -27.215556209029675, + -29.81495016405105, 124.99610755509275, - -22.81862718897968 + -20.219233233958303 ], "id": 0 }, diff --git a/packages/turf-line-split/test/out/multi-polygon.geojson b/packages/turf-line-split/test/out/multi-polygon.geojson index 6a99ff9b1f..da91cbf685 100644 --- a/packages/turf-line-split/test/out/multi-polygon.geojson +++ b/packages/turf-line-split/test/out/multi-polygon.geojson @@ -22,9 +22,9 @@ }, "bbox": [ 115.400390625, - -27.215556209029675, + -28.623293314794815, 119.2236328, - -26.207788245559954 + -24.80005113979481 ], "id": 0 }, diff --git a/packages/turf-line-split/test/out/multiPoint-on-line-2.geojson b/packages/turf-line-split/test/out/multiPoint-on-line-2.geojson index ec43968cd5..6b2eaa906e 100644 --- a/packages/turf-line-split/test/out/multiPoint-on-line-2.geojson +++ b/packages/turf-line-split/test/out/multiPoint-on-line-2.geojson @@ -26,9 +26,9 @@ }, "bbox": [ 116.98242187499999, - -32.249974455863295, + -35.54005060293165, 127.35351562499999, - -28.459033 + -25.16895685293165 ], "id": 0 }, diff --git a/packages/turf-line-split/test/out/polygon-with-holes.geojson b/packages/turf-line-split/test/out/polygon-with-holes.geojson index 8fb26283fb..3b0d828e47 100644 --- a/packages/turf-line-split/test/out/polygon-with-holes.geojson +++ b/packages/turf-line-split/test/out/polygon-with-holes.geojson @@ -30,9 +30,9 @@ }, "bbox": [ 108.10546875, - -24.607069137709694, + -27.50066352226305, 123.80984529019764, - -14.689881366618762 + -11.796286982065407 ], "id": 0 }, diff --git a/packages/turf-line-split/test/out/polygon.geojson b/packages/turf-line-split/test/out/polygon.geojson index 76734a2506..6b671b9626 100644 --- a/packages/turf-line-split/test/out/polygon.geojson +++ b/packages/turf-line-split/test/out/polygon.geojson @@ -21,9 +21,9 @@ ] }, "bbox": [ - 133.38557423512307, + 129.37945207795372, -13.837223632933568, - 133.76953125, + 137.77565340716933, -5.44102230371796 ], "id": 0 diff --git a/packages/turf-line-split/yarn.lock b/packages/turf-line-split/yarn.lock index 1e12619bfa..87ad4a31c2 100644 --- a/packages/turf-line-split/yarn.lock +++ b/packages/turf-line-split/yarn.lock @@ -6,16 +6,113 @@ version "0.12.1" resolved "https://registry.yarnpkg.com/@std/esm/-/esm-0.12.1.tgz#d07b7c5e2701beade1e7db74b95484f5d91c46bf" -"@turf/helpers@*", "@turf/helpers@^5.0.0": +"@turf/bbox@*": + version "5.0.0" + resolved "https://registry.yarnpkg.com/@turf/bbox/-/bbox-5.0.0.tgz#c179e533328120ca29b5dbd215a5f54e5c7940d3" + dependencies: + "@turf/helpers" "5.0.0" + "@turf/meta" "5.0.0" + +"@turf/bearing@5.x": + version "5.0.0" + resolved "https://registry.yarnpkg.com/@turf/bearing/-/bearing-5.0.0.tgz#448a2d1c7c6fde2b777c79886bf706256758e309" + dependencies: + "@turf/invariant" "5.0.0" + +"@turf/destination@5.x": + version "5.0.0" + resolved "https://registry.yarnpkg.com/@turf/destination/-/destination-5.0.0.tgz#9b1c3fc3bcb4fe631a5cb9e14dbc00baeb788770" + dependencies: + "@turf/helpers" "5.0.0" + "@turf/invariant" "5.0.0" + +"@turf/distance@5.x": + version "5.0.0" + resolved "https://registry.yarnpkg.com/@turf/distance/-/distance-5.0.0.tgz#f4db3dcfa160213fdaaf82b2153e1a479efaba6b" + dependencies: + "@turf/helpers" "5.0.0" + "@turf/invariant" "5.0.0" + +"@turf/distance@^4.7.3": + version "4.7.3" + resolved "https://registry.yarnpkg.com/@turf/distance/-/distance-4.7.3.tgz#b5ab48a09a642706d65c39b919433d5d2cc571b1" + dependencies: + "@turf/helpers" "^4.7.3" + "@turf/invariant" "^4.7.3" + +"@turf/helpers@*", "@turf/helpers@5.0.0", "@turf/helpers@^5.0.0": version "5.0.0" resolved "https://registry.yarnpkg.com/@turf/helpers/-/helpers-5.0.0.tgz#732cb570e6aebca9ef7375e8e42fafa8e16e5d47" -"@turf/meta@*": +"@turf/helpers@5.x": + version "5.0.1" + resolved "https://registry.yarnpkg.com/@turf/helpers/-/helpers-5.0.1.tgz#d950df25b10c9ed347f7811927ec14e72ccc58b2" + +"@turf/helpers@^4.7.3": + version "4.7.3" + resolved "https://registry.yarnpkg.com/@turf/helpers/-/helpers-4.7.3.tgz#bc312ac43cab3c532a483151c4c382c5649429e9" + +"@turf/invariant@5.0.0", "@turf/invariant@5.x": + version "5.0.0" + resolved "https://registry.yarnpkg.com/@turf/invariant/-/invariant-5.0.0.tgz#da9b4d7b044cffa300d654b3e0444969fc19d850" + +"@turf/invariant@^4.7.3": + version "4.7.3" + resolved "https://registry.yarnpkg.com/@turf/invariant/-/invariant-4.7.3.tgz#538f367d23c113fc849d70c9a524b8563874601d" + +"@turf/line-intersect@5.x": + version "5.0.3" + resolved "https://registry.yarnpkg.com/@turf/line-intersect/-/line-intersect-5.0.3.tgz#a15df3bc99a9cae028532349a4e3a011c1789044" + dependencies: + "@turf/helpers" "5.0.0" + "@turf/invariant" "5.0.0" + "@turf/line-segment" "5.0.0" + "@turf/meta" "5.0.0" + geojson-rbush "^2.0.1" + +"@turf/line-segment@5.0.0", "@turf/line-segment@5.x": + version "5.0.0" + resolved "https://registry.yarnpkg.com/@turf/line-segment/-/line-segment-5.0.0.tgz#983569e93eda49b76442def4e5249cd9d0dd79ce" + dependencies: + "@turf/helpers" "5.0.0" + "@turf/invariant" "5.0.0" + "@turf/meta" "5.0.0" + +"@turf/meta@*", "@turf/meta@5.x": version "5.0.2" resolved "https://registry.yarnpkg.com/@turf/meta/-/meta-5.0.2.tgz#712ff485cdaa6024d1f97d6fbed69d3f8e31e686" dependencies: "@turf/helpers" "^5.0.0" +"@turf/meta@5.0.0": + version "5.0.0" + resolved "https://registry.yarnpkg.com/@turf/meta/-/meta-5.0.0.tgz#75b100e9854cd7b2fa63e68561e1298dac09d7b4" + +"@turf/nearest-point-on-line@5.x": + version "5.0.0" + resolved "https://registry.yarnpkg.com/@turf/nearest-point-on-line/-/nearest-point-on-line-5.0.0.tgz#b5f58329c010bf76f9f27f8c0358cb2e8503b358" + dependencies: + "@turf/bearing" "5.x" + "@turf/destination" "5.x" + "@turf/distance" "5.x" + "@turf/helpers" "5.x" + "@turf/invariant" "5.x" + "@turf/line-intersect" "5.x" + "@turf/meta" "5.x" + +"@turf/square@*": + version "4.7.3" + resolved "https://registry.yarnpkg.com/@turf/square/-/square-4.7.3.tgz#90391f7c1520ed62925488f466314426a5e4d33c" + dependencies: + "@turf/distance" "^4.7.3" + +"@turf/truncate@5.x": + version "5.0.0" + resolved "https://registry.yarnpkg.com/@turf/truncate/-/truncate-5.0.0.tgz#f11a2bd3b05f43600a985937cd1d7baf3f57db52" + dependencies: + "@turf/helpers" "5.x" + "@turf/meta" "5.x" + balanced-match@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" @@ -99,7 +196,7 @@ function-bind@^1.0.2, function-bind@^1.1.1, function-bind@~1.1.0: version "1.1.1" resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" -geojson-rbush@2.1.0: +geojson-rbush@2.1.0, geojson-rbush@^2.0.1: version "2.1.0" resolved "https://registry.yarnpkg.com/geojson-rbush/-/geojson-rbush-2.1.0.tgz#3bd73be391fc10b0ae693d9b8acea2aae0b83a8d" dependencies: