Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rewrite line-intersect module #2033

Merged
merged 28 commits into from
Apr 2, 2022
Merged
Show file tree
Hide file tree
Changes from 27 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
6bc6fa3
Rewrite line-intersect module
rowanwins Feb 13, 2021
1ed5100
fix jsdoc
rowanwins Feb 17, 2021
f8e39fd
update use of sweepline-intersections to not detect self-intersections
rowanwins Jun 16, 2021
0b08f63
Merge branch 'master' into line-intersect-update
rowanwins Jun 16, 2021
d3bffd4
Merge branch 'master' into line-intersect-update
rowanwins Jun 17, 2021
f7fe715
inline sweepline-intersections as es5
rowanwins Jun 20, 2021
01217c9
Add some ts config
rowanwins Jun 20, 2021
2e4c38c
remove unused import
rowanwins Jun 20, 2021
dd02540
add allowJs
rowanwins Jun 20, 2021
0790a03
adjust tsconfig
rowanwins Jun 20, 2021
5cb1352
address some eslint issues
rowanwins Jun 20, 2021
2d1da5c
Merge branch 'master' into line-intersect-update
rowanwins Aug 1, 2021
a09c60c
Some minor typing updates to get the build to pass
mfedderly Aug 4, 2021
d5713e4
Merge remote-tracking branch 'origin/master' into line-intersect-update
mfedderly Aug 4, 2021
ed0a1b1
Merge branch 'master' into line-intersect-update
rowanwins Jan 11, 2022
cf497ef
Merge branch 'master' into line-intersect-update
rowanwins Jan 17, 2022
9a5c3bf
Fix imports after merge
rowanwins Jan 17, 2022
eb6939d
rejig ts config
rowanwins Jan 17, 2022
c5f3dde
update yarn lock
rowanwins Jan 18, 2022
8493699
Merge branch 'master' into line-intersect-update
rowanwins Jan 18, 2022
7e78442
Merge branch 'master' into line-intersect-update
rowanwins Mar 17, 2022
4005391
add extra option to line-intersect re self-intersections
rowanwins Mar 17, 2022
5bf8641
run prettier
rowanwins Mar 17, 2022
8b6821b
update the turf-line-split outputs
rowanwins Mar 17, 2022
bb1ab42
fix prettier
rowanwins Mar 18, 2022
472f471
line-intersect additional type cleanup (#2273)
twelch Mar 20, 2022
c1af16d
line-intersect-typescript-cleanup (#2274)
mfedderly Mar 20, 2022
e7c5c03
bump sweepline-intersections to 1.4.0
rowanwins Apr 2, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions packages/turf-line-intersect/bench.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ const fs = require("fs");
const path = require("path");
const load = require("load-json-file");
const Benchmark = require("benchmark");
const lineIntersect = require("./index");
const lineIntersect = require("./index").default;

const directory = path.join(__dirname, "test", "in") + path.sep;
const fixtures = fs.readdirSync(directory).map((filename) => {
Expand All @@ -16,11 +16,11 @@ const fixtures = fs.readdirSync(directory).map((filename) => {
/**
* Benchmark Results
*
* 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)
* 2-vertex-segment x 1,467,485 ops/sec ±1.74% (89 runs sampled)
* double-intersect x 307,665 ops/sec ±1.70% (91 runs sampled)
* multi-linestring x 81,337 ops/sec ±0.67% (94 runs sampled)
* polygons-with-holes x 27,711 ops/sec ±0.70% (92 runs sampled)
* same-coordinates x 521,277 ops/sec ±0.75% (92 runs sampled)
*/
const suite = new Benchmark.Suite("turf-line-intersect");
for (const { name, geojson } of fixtures) {
Expand Down
139 changes: 48 additions & 91 deletions packages/turf-line-intersect/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { feature, featureCollection, point } from "@turf/helpers";
import {
Feature,
FeatureCollection,
Expand All @@ -7,18 +8,17 @@ import {
Point,
Polygon,
} from "geojson";
import { feature, featureCollection, point } from "@turf/helpers";
import { getCoords } from "@turf/invariant";
import lineSegment from "@turf/line-segment";
import { featureEach } from "@turf/meta";
import rbush from "@turf/geojson-rbush";
import findIntersections, { Intersection } from "sweepline-intersections";

/**
* Takes any LineString or Polygon GeoJSON and returns the intersecting point(s).
*
* @name lineIntersect
* @param {GeoJSON} line1 any LineString or Polygon
* @param {GeoJSON} line2 any LineString or Polygon
* @param {Object} [options={}] Optional parameters
* @param {boolean} [options.removeDuplicates=true] remove duplicate intersections
* @param {boolean} [options.ignoreSelfIntersections=false] ignores self-intersections on input features
* @returns {FeatureCollection<Point>} point(s) that intersect both
* @example
* var line1 = turf.lineString([[126, -11], [129, -21]]);
Expand All @@ -33,100 +33,57 @@ function lineIntersect<
G2 extends LineString | MultiLineString | Polygon | MultiPolygon
>(
line1: FeatureCollection<G1> | Feature<G1> | G1,
line2: FeatureCollection<G2> | Feature<G2> | G2
line2: FeatureCollection<G2> | Feature<G2> | G2,
options: {
removeDuplicates?: boolean;
ignoreSelfIntersections?: boolean;
} = {}
): FeatureCollection<Point> {
const unique: any = {};
const results: any[] = [];

// First, normalize geometries to features
// Then, handle simple 2-vertex segments
if (line1.type === "LineString") {
line1 = feature(line1);
}
if (line2.type === "LineString") {
line2 = feature(line2);
}
if (
line1.type === "Feature" &&
line2.type === "Feature" &&
line1.geometry !== null &&
line2.geometry !== null &&
line1.geometry.type === "LineString" &&
line2.geometry.type === "LineString" &&
line1.geometry.coordinates.length === 2 &&
line2.geometry.coordinates.length === 2
const { removeDuplicates = true, ignoreSelfIntersections = false } = options;
let features: Feature<G1 | G2>[] = [];
if (line1.type === "FeatureCollection")
features = features.concat(line1.features);
else if (line1.type === "Feature") features.push(line1);
else if (
line1.type === "LineString" ||
line1.type === "Polygon" ||
line1.type === "MultiLineString" ||
line1.type === "MultiPolygon"
) {
const intersect = intersects(line1, line2);
if (intersect) {
results.push(intersect);
}
return featureCollection(results);
}

// Handles complex GeoJSON Geometries
const tree = rbush();
tree.load(lineSegment(line2));
featureEach(lineSegment(line1), (segment) => {
featureEach(tree.search(segment), (match) => {
const intersect = intersects(segment, match);
if (intersect) {
// prevent duplicate points https://github.com/Turfjs/turf/issues/688
const key = getCoords(intersect).join(",");
if (!unique[key]) {
unique[key] = true;
results.push(intersect);
}
}
});
});
return featureCollection(results);
}

/**
* Find a point that intersects LineStrings with two coordinates each
*
* @private
* @param {Feature<LineString>} line1 GeoJSON LineString (Must only contain 2 coordinates)
* @param {Feature<LineString>} line2 GeoJSON LineString (Must only contain 2 coordinates)
* @returns {Feature<Point>} intersecting GeoJSON Point
*/
function intersects(line1: Feature<any>, line2: Feature<any>) {
const coords1: any = getCoords(line1);
const coords2: any = getCoords(line2);
if (coords1.length !== 2) {
throw new Error("<intersects> line1 must only contain 2 coordinates");
features.push(feature(line1));
}
if (coords2.length !== 2) {
throw new Error("<intersects> line2 must only contain 2 coordinates");
}
const x1 = coords1[0][0];
const y1 = coords1[0][1];
const x2 = coords1[1][0];
const y2 = coords1[1][1];
const x3 = coords2[0][0];
const y3 = coords2[0][1];
const x4 = coords2[1][0];
const y4 = coords2[1][1];
const denom = (y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1);
const numeA = (x4 - x3) * (y1 - y3) - (y4 - y3) * (x1 - x3);
const numeB = (x2 - x1) * (y1 - y3) - (y2 - y1) * (x1 - x3);

if (denom === 0) {
if (numeA === 0 && numeB === 0) {
return null;
}
return null;
if (line2.type === "FeatureCollection")
features = features.concat(line2.features);
else if (line2.type === "Feature") features.push(line2);
else if (
line2.type === "LineString" ||
line2.type === "Polygon" ||
line2.type === "MultiLineString" ||
line2.type === "MultiPolygon"
) {
features.push(feature(line2));
}

const uA = numeA / denom;
const uB = numeB / denom;
const intersections = findIntersections(
featureCollection(features),
ignoreSelfIntersections
);

if (uA >= 0 && uA <= 1 && uB >= 0 && uB <= 1) {
const x = x1 + uA * (x2 - x1);
const y = y1 + uA * (y2 - y1);
return point([x, y]);
let results: Intersection[] = [];
if (removeDuplicates) {
const unique: Record<string, boolean> = {};
intersections.forEach((intersection) => {
const key = intersection.join(",");
if (!unique[key]) {
unique[key] = true;
results.push(intersection);
}
});
} else {
results = intersections;
}
return null;
return featureCollection(results.map((r) => point(r)));
}

export default lineIntersect;
5 changes: 1 addition & 4 deletions packages/turf-line-intersect/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -63,11 +63,8 @@
"write-json-file": "*"
},
"dependencies": {
"@turf/geojson-rbush": "^3.2.0",
"@turf/helpers": "^6.5.0",
"@turf/invariant": "^6.5.0",
"@turf/line-segment": "^6.5.0",
"@turf/meta": "^6.5.0",
"sweepline-intersections": "^1.3.1",
rowanwins marked this conversation as resolved.
Show resolved Hide resolved
rowanwins marked this conversation as resolved.
Show resolved Hide resolved
"tslib": "^2.3.0"
}
}
24 changes: 14 additions & 10 deletions packages/turf-line-intersect/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ const write = require("write-json-file");
const truncate = require("@turf/truncate").default;
const {
featureCollection,
geometryCollection,
// geometryCollection,
lineString,
polygon,
} = require("@turf/helpers");
Expand Down Expand Up @@ -61,12 +61,10 @@ test("turf-line-intersect - prevent input mutation", (t) => {
test("turf-line-intersect - Geometry Objects", (t) => {
const line1 = lineString([
[7, 50],
[8, 50],
[9, 50],
]);
const line2 = lineString([
[8, 49],
[8, 50],
[8, 51],
]);
t.ok(
Expand All @@ -78,13 +76,13 @@ test("turf-line-intersect - Geometry Objects", (t) => {
.features.length,
"support Feature Collection"
);
t.ok(
lineIntersect(
geometryCollection([line1.geometry]),
geometryCollection([line2.geometry])
).features.length,
"support Geometry Collection"
);
// t.ok(
rowanwins marked this conversation as resolved.
Show resolved Hide resolved
// lineIntersect(
// geometryCollection([line1.geometry]),
// geometryCollection([line2.geometry])
// ).features.length,
// "support Geometry Collection"
// );
t.end();
});

Expand All @@ -102,6 +100,12 @@ test("turf-line-intersect - same point #688", (t) => {

const results = lineIntersect(line1, line2);
t.equal(results.features.length, 1, "should return single point");

const results2 = lineIntersect(line1, line2, {
removeDuplicates: false,
});
t.equal(results2.features.length, 3, "should return three points");

t.end();
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,15 @@
"properties": {},
"geometry": {
"type": "Point",
"coordinates": [132.808697, -11.630938]
"coordinates": [119.832884, -19.58857]
}
},
{
"type": "Feature",
"properties": {},
"geometry": {
"type": "Point",
"coordinates": [119.832884, -19.58857]
"coordinates": [132.808697, -11.630938]
}
},
{
Expand Down
20 changes: 10 additions & 10 deletions packages/turf-line-intersect/test/out/multi-linestring.geojson
Original file line number Diff line number Diff line change
Expand Up @@ -6,79 +6,79 @@
"properties": {},
"geometry": {
"type": "Point",
"coordinates": [136.479474, -14.675333]
"coordinates": [117.006519, -20.917359]
}
},
{
"type": "Feature",
"properties": {},
"geometry": {
"type": "Point",
"coordinates": [136.389417, -14.506578]
"coordinates": [118.554586, -25.732946]
}
},
{
"type": "Feature",
"properties": {},
"geometry": {
"type": "Point",
"coordinates": [132.808697, -11.630938]
"coordinates": [119.832884, -19.58857]
}
},
{
"type": "Feature",
"properties": {},
"geometry": {
"type": "Point",
"coordinates": [135.006479, -11.91514]
"coordinates": [121.656735, -23.805914]
}
},
{
"type": "Feature",
"properties": {},
"geometry": {
"type": "Point",
"coordinates": [119.832884, -19.58857]
"coordinates": [132.658557, -18.734814]
}
},
{
"type": "Feature",
"properties": {},
"geometry": {
"type": "Point",
"coordinates": [117.006519, -20.917359]
"coordinates": [132.808697, -11.630938]
}
},
{
"type": "Feature",
"properties": {},
"geometry": {
"type": "Point",
"coordinates": [118.554586, -25.732946]
"coordinates": [135.006479, -11.91514]
}
},
{
"type": "Feature",
"properties": {},
"geometry": {
"type": "Point",
"coordinates": [121.656735, -23.805914]
"coordinates": [135.197772, -18.403004]
}
},
{
"type": "Feature",
"properties": {},
"geometry": {
"type": "Point",
"coordinates": [132.658557, -18.734814]
"coordinates": [136.389417, -14.506578]
}
},
{
"type": "Feature",
"properties": {},
"geometry": {
"type": "Point",
"coordinates": [135.197772, -18.403004]
"coordinates": [136.479474, -14.675333]
}
},
{
Expand Down
Loading