From 6989590a44f8215a3f6fac8ee2f05c728c6a64f1 Mon Sep 17 00:00:00 2001 From: Denis Date: Sun, 30 Apr 2017 17:25:32 -0400 Subject: [PATCH] Fix duplicate nodes from lineSegment #688 --- packages/turf-line-intersect/bench.js | 19 ++++++++++--------- packages/turf-line-intersect/index.js | 19 ++++++++++++++----- packages/turf-line-intersect/test.js | 18 +++++++++++++----- packages/turf-line-split/index.js | 16 +++++++++++----- packages/turf-line-split/test.js | 25 ++++++++++++++++++++++--- 5 files changed, 70 insertions(+), 27 deletions(-) diff --git a/packages/turf-line-intersect/bench.js b/packages/turf-line-intersect/bench.js index d0dbcc767a..e949b7137f 100644 --- a/packages/turf-line-intersect/bench.js +++ b/packages/turf-line-intersect/bench.js @@ -1,7 +1,7 @@ -const Benchmark = require('benchmark'); -const path = require('path'); const fs = require('fs'); +const path = require('path'); const load = require('load-json-file'); +const Benchmark = require('benchmark'); const lineIntersect = require('./'); const directory = path.join(__dirname, 'test', 'in') + path.sep; @@ -16,18 +16,19 @@ const fixtures = fs.readdirSync(directory).map(filename => { /** * Benchmark Results * - * 2-vertex-segment x 7,177,957 ops/sec ±0.83% (92 runs sampled) - * double-intersect x 88,165 ops/sec ±3.02% (78 runs sampled) - * multi-linestring x 19,554 ops/sec ±2.09% (77 runs sampled) - * multi-polygon x 11,149 ops/sec ±2.05% (74 runs sampled) - * same-coordinates x 67,439 ops/sec ±2.01% (73 runs sampled) + * 2-vertex-segment x 4,123,821 ops/sec ±12.92% (74 runs sampled) + * double-intersect x 53,118 ops/sec ±4.22% (72 runs sampled) + * multi-linestring x 16,417 ops/sec ±2.31% (77 runs sampled) + * polygons-with-holes x 9,739 ops/sec ±2.55% (85 runs sampled) + * same-coordinates x 51,303 ops/sec ±4.23% (71 runs sampled) */ const suite = new Benchmark.Suite('turf-line-intersect'); for (const {name, geojson} of fixtures) { - suite.add(name, () => lineIntersect(geojson.features[0], geojson.features[1])); + const [line1, line2] = geojson.features; + suite.add(name, () => lineIntersect(line1, line2)); } suite - .on('cycle', e => { console.log(String(e.target)); }) + .on('cycle', e => console.log(String(e.target))) .on('complete', () => {}) .run(); diff --git a/packages/turf-line-intersect/index.js b/packages/turf-line-intersect/index.js index c83dd50969..454c39e7f3 100644 --- a/packages/turf-line-intersect/index.js +++ b/packages/turf-line-intersect/index.js @@ -1,11 +1,11 @@ -var helpers = require('@turf/helpers'); var meta = require('@turf/meta'); -var lineSegment = require('@turf/line-segment'); -var getCoords = require('@turf/invariant').getCoords; var rbush = require('geojson-rbush'); +var helpers = require('@turf/helpers'); +var getCoords = require('@turf/invariant').getCoords; +var lineSegment = require('@turf/line-segment'); var point = helpers.point; -var featureCollection = helpers.featureCollection; var featureEach = meta.featureEach; +var featureCollection = helpers.featureCollection; /** * Takes any LineString or Polygon GeoJSON and returns the intersecting point(s). @@ -37,7 +37,9 @@ var featureEach = meta.featureEach; * var addToMap = [line, line2, intersects] */ module.exports = function (line1, line2) { + var unique = {}; var results = []; + // Handles simple 2-vertex segments if (line1.geometry.type === 'LineString' && line2.geometry.type === 'LineString' && @@ -47,13 +49,20 @@ module.exports = function (line1, line2) { if (intersect) results.push(intersect); return featureCollection(results); } + // Handles complex GeoJSON Geometries var tree = rbush(); tree.load(lineSegment(line2)); featureEach(lineSegment(line1), function (segment) { featureEach(tree.search(segment), function (match) { var intersect = intersects(segment, match); - if (intersect) results.push(intersect); + if (intersect) { + var key = getCoords(intersect).join(','); + if (!unique[key]) { + unique[key] = true; + results.push(intersect); + } + } }); }); return featureCollection(results); diff --git a/packages/turf-line-intersect/test.js b/packages/turf-line-intersect/test.js index 5f6f52f7f1..ba925295c2 100644 --- a/packages/turf-line-intersect/test.js +++ b/packages/turf-line-intersect/test.js @@ -1,11 +1,11 @@ -const test = require('tape'); const fs = require('fs'); +const test = require('tape'); const path = require('path'); const load = require('load-json-file'); const write = require('write-json-file'); -const featureEach = require('@turf/meta').featureEach; -const featureCollection = require('@turf/helpers').featureCollection; const truncate = require('@turf/truncate'); +const {featureEach} = require('@turf/meta'); +const {featureCollection, lineString} = require('@turf/helpers'); const lineIntersect = require('./'); const directories = { @@ -23,8 +23,7 @@ const fixtures = fs.readdirSync(directories.in).map(filename => { test('turf-line-intersect', t => { for (const {filename, name, geojson} of fixtures) { - const line1 = geojson.features[0]; - const line2 = geojson.features[1]; + const [line1, line2] = geojson.features; const points = truncate(lineIntersect(line1, line2)); const results = featureCollection([line1, line2]); featureEach(points, point => results.features.push(point)); @@ -34,3 +33,12 @@ test('turf-line-intersect', t => { } t.end(); }); + +test('turf-line-intersect - same point #688', t => { + const line1 = lineString([[7, 50], [8, 50], [9, 50]]); + const line2 = lineString([[8, 49], [8, 50], [8, 51]]); + + var results = lineIntersect(line1, line2); + t.equal(results.features.length, 1, 'should return single point'); + t.end(); +}); diff --git a/packages/turf-line-split/index.js b/packages/turf-line-split/index.js index 822cf949bc..3f75b00ef4 100644 --- a/packages/turf-line-split/index.js +++ b/packages/turf-line-split/index.js @@ -142,22 +142,28 @@ function splitLineWithPoint(line, splitter) { // Initial value is the first point of the first segments (begining of line) var initialValue = [getCoords(segments.features[0])[0]]; var lastCoords = featureReduce(segments, function (previous, current, index) { + var currentCoords = getCoords(current)[1]; + var splitterCoords = getCoords(splitter); // Location where segment intersects with line if (index === closestSegment.id) { - var coords = getCoords(splitter); - previous.push(coords); + previous.push(splitterCoords); results.push(lineString(previous)); - return [coords, getCoords(current)[1]]; + // Don't duplicate splitter coordinate (Issue #688) + if (splitterCoords[0] === currentCoords[0] && + splitterCoords[1] === currentCoords[1]) return [splitterCoords]; + return [splitterCoords, currentCoords]; // Keep iterating over coords until finished or intersection is found } else { - previous.push(getCoords(current)[1]); + previous.push(currentCoords); return previous; } }, initialValue); // Append last line to final split results - results.push(lineString(lastCoords)); + if (lastCoords.length > 1) { + results.push(lineString(lastCoords)); + } return featureCollection(results); } diff --git a/packages/turf-line-split/test.js b/packages/turf-line-split/test.js index a7ed7482ef..fc91ef4d0b 100644 --- a/packages/turf-line-split/test.js +++ b/packages/turf-line-split/test.js @@ -1,10 +1,10 @@ -const test = require('tape'); const fs = require('fs'); +const test = require('tape'); const path = require('path'); const load = require('load-json-file'); const write = require('write-json-file'); -const featureEach = require('@turf/meta').featureEach; -const featureCollection = require('@turf/helpers').featureCollection; +const {featureEach} = require('@turf/meta'); +const {point, lineString, featureCollection} = require('@turf/helpers'); const lineSplit = require('./'); const directories = { @@ -33,6 +33,25 @@ test('turf-line-split', t => { t.end(); }); +test('turf-line-split - lines should only contain 2 vertices #688', t => { + const pt = point([8, 50]); + const line = lineString([[7, 50], [8, 50], [9, 50]]); + const [line1, line2] = lineSplit(line, pt).features; + + t.deepEqual(line1, lineString([[7, 50], [8, 50]]), 'line1 should have 2 vertices'); + t.deepEqual(line2, lineString([[8, 50], [9, 50]]), 'line2 should have 2 vertices'); + t.end(); +}); + +test('turf-line-split - splitter exactly on end of line', t => { + const pt = point([9, 50]); + const line = lineString([[7, 50], [8, 50], [9, 50]]); + const features = lineSplit(line, pt).features; + + t.deepEqual(features, [line], 'should only contain 1 line of 3 vertices'); + t.end(); +}); + /** * Colorize FeatureCollection *