@turf/nearest-point-on-line
Advanced tools
Comparing version
@@ -5,5 +5,5 @@ import { LineString, MultiLineString, Feature, Point } from 'geojson'; | ||
/** | ||
* Takes a {@link Point} and a {@link LineString} and calculates the closest Point on the (Multi)LineString. | ||
* Returns the nearest point on a line to a given point. | ||
* | ||
* @name nearestPointOnLine | ||
* @function | ||
* @param {Geometry|Feature<LineString|MultiLineString>} lines lines to snap to | ||
@@ -10,0 +10,0 @@ * @param {Geometry|Feature<Point>|number[]} pt point to snap from |
@@ -22,9 +22,10 @@ var __defProp = Object.defineProperty; | ||
// index.ts | ||
import { bearing } from "@turf/bearing"; | ||
import { distance } from "@turf/distance"; | ||
import { destination } from "@turf/destination"; | ||
import { lineIntersect as lineIntersects } from "@turf/line-intersect"; | ||
import { flattenEach } from "@turf/meta"; | ||
import { point, lineString } from "@turf/helpers"; | ||
import { getCoords } from "@turf/invariant"; | ||
import { | ||
point, | ||
degreesToRadians, | ||
radiansToDegrees | ||
} from "@turf/helpers"; | ||
import { getCoord, getCoords } from "@turf/invariant"; | ||
function nearestPointOnLine(lines, pt, options = {}) { | ||
@@ -34,2 +35,3 @@ if (!lines || !pt) { | ||
} | ||
const ptPos = getCoord(pt); | ||
let closestPt = point([Infinity, Infinity], { | ||
@@ -49,60 +51,35 @@ dist: Infinity, | ||
start.properties.dist = distance(pt, start, options); | ||
const startPos = getCoord(start); | ||
const stop = point(coords[i + 1]); | ||
stop.properties.dist = distance(pt, stop, options); | ||
const stopPos = getCoord(stop); | ||
const sectionLength = distance(start, stop, options); | ||
const heightDistance = Math.max( | ||
start.properties.dist, | ||
stop.properties.dist | ||
); | ||
const direction = bearing(start, stop); | ||
const perpendicularPt1 = destination( | ||
pt, | ||
heightDistance, | ||
direction + 90, | ||
options | ||
); | ||
const perpendicularPt2 = destination( | ||
pt, | ||
heightDistance, | ||
direction - 90, | ||
options | ||
); | ||
const intersect = lineIntersects( | ||
lineString([ | ||
perpendicularPt1.geometry.coordinates, | ||
perpendicularPt2.geometry.coordinates | ||
]), | ||
lineString([start.geometry.coordinates, stop.geometry.coordinates]) | ||
); | ||
let intersectPos; | ||
let wasEnd; | ||
if (startPos[0] === ptPos[0] && startPos[1] === ptPos[1]) { | ||
[intersectPos, , wasEnd] = [startPos, void 0, false]; | ||
} else if (stopPos[0] === ptPos[0] && stopPos[1] === ptPos[1]) { | ||
[intersectPos, , wasEnd] = [stopPos, void 0, true]; | ||
} else { | ||
[intersectPos, , wasEnd] = nearestPointOnSegment( | ||
start.geometry.coordinates, | ||
stop.geometry.coordinates, | ||
getCoord(pt) | ||
); | ||
} | ||
let intersectPt; | ||
if (intersect.features.length > 0 && intersect.features[0]) { | ||
intersectPt = __spreadProps(__spreadValues({}, intersect.features[0]), { | ||
properties: { | ||
dist: distance(pt, intersect.features[0], options), | ||
multiFeatureIndex, | ||
location: length + distance(start, intersect.features[0], options) | ||
} | ||
if (intersectPos) { | ||
intersectPt = point(intersectPos, { | ||
dist: distance(pt, intersectPos, options), | ||
multiFeatureIndex, | ||
location: length + distance(start, intersectPos, options) | ||
}); | ||
} | ||
if (start.properties.dist < closestPt.properties.dist) { | ||
closestPt = __spreadProps(__spreadValues({}, start), { | ||
properties: __spreadProps(__spreadValues({}, start.properties), { | ||
index: i, | ||
multiFeatureIndex, | ||
location: length | ||
}) | ||
}); | ||
} | ||
if (stop.properties.dist < closestPt.properties.dist) { | ||
closestPt = __spreadProps(__spreadValues({}, stop), { | ||
properties: __spreadProps(__spreadValues({}, stop.properties), { | ||
index: i + 1, | ||
multiFeatureIndex, | ||
location: length + sectionLength | ||
}) | ||
}); | ||
} | ||
if (intersectPt && intersectPt.properties.dist < closestPt.properties.dist) { | ||
closestPt = __spreadProps(__spreadValues({}, intersectPt), { | ||
properties: __spreadProps(__spreadValues({}, intersectPt.properties), { index: i }) | ||
properties: __spreadProps(__spreadValues({}, intersectPt.properties), { | ||
// Legacy behaviour where index progresses to next segment # if we | ||
// went with the end point this iteration. | ||
index: wasEnd ? i + 1 : i | ||
}) | ||
}); | ||
@@ -116,2 +93,69 @@ } | ||
} | ||
function dot(v1, v2) { | ||
const [v1x, v1y, v1z] = v1; | ||
const [v2x, v2y, v2z] = v2; | ||
return v1x * v2x + v1y * v2y + v1z * v2z; | ||
} | ||
function cross(v1, v2) { | ||
const [v1x, v1y, v1z] = v1; | ||
const [v2x, v2y, v2z] = v2; | ||
return [v1y * v2z - v1z * v2y, v1z * v2x - v1x * v2z, v1x * v2y - v1y * v2x]; | ||
} | ||
function magnitude(v) { | ||
return Math.sqrt(Math.pow(v[0], 2) + Math.pow(v[1], 2) + Math.pow(v[2], 2)); | ||
} | ||
function angle(v1, v2) { | ||
const theta = dot(v1, v2) / (magnitude(v1) * magnitude(v2)); | ||
return Math.acos(Math.min(Math.max(theta, -1), 1)); | ||
} | ||
function lngLatToVector(a) { | ||
const lat = degreesToRadians(a[1]); | ||
const lng = degreesToRadians(a[0]); | ||
return [ | ||
Math.cos(lat) * Math.cos(lng), | ||
Math.cos(lat) * Math.sin(lng), | ||
Math.sin(lat) | ||
]; | ||
} | ||
function vectorToLngLat(v) { | ||
const [x, y, z] = v; | ||
const lat = radiansToDegrees(Math.asin(z)); | ||
const lng = radiansToDegrees(Math.atan2(y, x)); | ||
return [lng, lat]; | ||
} | ||
function nearestPointOnSegment(posA, posB, posC) { | ||
const A = lngLatToVector(posA); | ||
const B = lngLatToVector(posB); | ||
const C = lngLatToVector(posC); | ||
const [Cx, Cy, Cz] = C; | ||
const [D, E, F] = cross(A, B); | ||
const a = E * Cz - F * Cy; | ||
const b = F * Cx - D * Cz; | ||
const c = D * Cy - E * Cx; | ||
const f = c * E - b * F; | ||
const g = a * F - c * D; | ||
const h = b * D - a * E; | ||
const t = 1 / Math.sqrt(Math.pow(f, 2) + Math.pow(g, 2) + Math.pow(h, 2)); | ||
const I1 = [f * t, g * t, h * t]; | ||
const I2 = [-1 * f * t, -1 * g * t, -1 * h * t]; | ||
const angleAB = angle(A, B); | ||
const angleAI1 = angle(A, I1); | ||
const angleBI1 = angle(B, I1); | ||
const angleAI2 = angle(A, I2); | ||
const angleBI2 = angle(B, I2); | ||
let I; | ||
if (angleAI1 < angleAI2 && angleAI1 < angleBI2 || angleBI1 < angleAI2 && angleBI1 < angleBI2) { | ||
I = I1; | ||
} else { | ||
I = I2; | ||
} | ||
if (angle(A, I) > angleAB || angle(B, I) > angleAB) { | ||
if (distance(vectorToLngLat(I), vectorToLngLat(A)) <= distance(vectorToLngLat(I), vectorToLngLat(B))) { | ||
return [vectorToLngLat(A), true, false]; | ||
} else { | ||
return [vectorToLngLat(B), false, true]; | ||
} | ||
} | ||
return [vectorToLngLat(I), false, false]; | ||
} | ||
var turf_nearest_point_on_line_default = nearestPointOnLine; | ||
@@ -118,0 +162,0 @@ export { |
{ | ||
"name": "@turf/nearest-point-on-line", | ||
"version": "7.1.0", | ||
"version": "7.2.0", | ||
"description": "turf nearest-point-on-line module", | ||
@@ -49,28 +49,25 @@ "author": "Turf Authors", | ||
"devDependencies": { | ||
"@turf/along": "^7.1.0", | ||
"@turf/length": "^7.1.0", | ||
"@turf/truncate": "^7.1.0", | ||
"@turf/along": "^7.2.0", | ||
"@turf/length": "^7.2.0", | ||
"@turf/truncate": "^7.2.0", | ||
"@types/benchmark": "^2.1.5", | ||
"@types/tape": "^4.2.32", | ||
"@types/tape": "^4.13.4", | ||
"benchmark": "^2.1.4", | ||
"load-json-file": "^7.0.1", | ||
"npm-run-all": "^4.1.5", | ||
"tape": "^5.7.2", | ||
"tsup": "^8.0.1", | ||
"tsx": "^4.6.2", | ||
"typescript": "^5.2.2", | ||
"tape": "^5.9.0", | ||
"tsup": "^8.3.5", | ||
"tsx": "^4.19.2", | ||
"typescript": "^5.5.4", | ||
"write-json-file": "^5.0.0" | ||
}, | ||
"dependencies": { | ||
"@turf/bearing": "^7.1.0", | ||
"@turf/destination": "^7.1.0", | ||
"@turf/distance": "^7.1.0", | ||
"@turf/helpers": "^7.1.0", | ||
"@turf/invariant": "^7.1.0", | ||
"@turf/line-intersect": "^7.1.0", | ||
"@turf/meta": "^7.1.0", | ||
"@turf/distance": "^7.2.0", | ||
"@turf/helpers": "^7.2.0", | ||
"@turf/invariant": "^7.2.0", | ||
"@turf/meta": "^7.2.0", | ||
"@types/geojson": "^7946.0.10", | ||
"tslib": "^2.6.2" | ||
"tslib": "^2.8.1" | ||
}, | ||
"gitHead": "68915eeebc9278bb40dec3f1034499698a0561ef" | ||
"gitHead": "7b0f0374c4668cd569f8904c71e2ae7d941be867" | ||
} |
@@ -7,8 +7,8 @@ # @turf/nearest-point-on-line | ||
Takes a [Point][1] and a [LineString][2] and calculates the closest Point on the (Multi)LineString. | ||
Returns the nearest point on a line to a given point. | ||
### Parameters | ||
* `lines` **([Geometry][3] | [Feature][4]<([LineString][2] | [MultiLineString][5])>)** lines to snap to | ||
* `pt` **([Geometry][3] | [Feature][4]<[Point][1]> | [Array][6]<[number][7]>)** point to snap from | ||
* `lines` **([Geometry][1] | [Feature][2]<([LineString][3] | [MultiLineString][4])>)** lines to snap to | ||
* `pt` **([Geometry][1] | [Feature][2]<[Point][5]> | [Array][6]<[number][7]>)** point to snap from | ||
* `options` **[Object][8]** Optional parameters (optional, default `{}`) | ||
@@ -38,13 +38,13 @@ | ||
Returns **[Feature][4]<[Point][1]>** closest point on the `line` to `point`. The properties object will contain four values: `index`: closest point was found on nth line part, `multiFeatureIndex`: closest point was found on the nth line of the `MultiLineString`, `dist`: distance between pt and the closest point, `location`: distance along the line between start and the closest point. | ||
Returns **[Feature][2]<[Point][5]>** closest point on the `line` to `point`. The properties object will contain four values: `index`: closest point was found on nth line part, `multiFeatureIndex`: closest point was found on the nth line of the `MultiLineString`, `dist`: distance between pt and the closest point, `location`: distance along the line between start and the closest point. | ||
[1]: https://tools.ietf.org/html/rfc7946#section-3.1.2 | ||
[1]: https://tools.ietf.org/html/rfc7946#section-3.1 | ||
[2]: https://tools.ietf.org/html/rfc7946#section-3.1.4 | ||
[2]: https://tools.ietf.org/html/rfc7946#section-3.2 | ||
[3]: https://tools.ietf.org/html/rfc7946#section-3.1 | ||
[3]: https://tools.ietf.org/html/rfc7946#section-3.1.4 | ||
[4]: https://tools.ietf.org/html/rfc7946#section-3.2 | ||
[4]: https://tools.ietf.org/html/rfc7946#section-3.1.5 | ||
[5]: https://tools.ietf.org/html/rfc7946#section-3.1.5 | ||
[5]: https://tools.ietf.org/html/rfc7946#section-3.1.2 | ||
@@ -51,0 +51,0 @@ [6]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
49944
48.43%6
-33.33%352
31.34%- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
Updated
Updated
Updated
Updated
Updated