@turf/nearest-point-on-line 7.3.1 → 7.3.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -10,10 +10,23 @@ If any of the segments in the input line string are antipodal and therefore
10
10
  have an undefined arc, this function will instead return that the point lies
11
11
  on the line.
12
12
 
13
+ ⚠️ We have begun the process of migrating to different return properties for
14
+ this function. The new properties we recommend using as of v7.4 are:
15
+
16
+ * lineStringIndex - point was found on the nth LineString of an input MultiLineString. Previously `multiFeatureIndex`
17
+ * segmentIndex - point was found on the nth segment of the above LineString. Previously `index`
18
+ * totalDistance - distance from the start of the overall MultiLineString. Previously `location`
19
+ * lineDistance - distance from the start of the relevant LineString
20
+ * segmentDistance - distance from the start of the relevant segment
21
+ * pointDistance - distance between found point is from input reference point. Previously `dist`
22
+
23
+ multiFeatureIndex, index, location, and dist continue to work as previously
24
+ until at least the next major release.
25
+
13
26
  ### Parameters
14
27
 
15
- * `lines` **([Geometry][1] | [Feature][2]<([LineString][3] | [MultiLineString][4])>)** lines to snap to
16
- * `pt` **([Geometry][1] | [Feature][2]<[Point][5]> | [Array][6]<[number][7]>)** point to snap from
28
+ * `lines` **([Geometry][1] | [Feature][2]<([LineString][3] | [MultiLineString][4])>)** Lines to snap to
29
+ * `inputPoint` **([Geometry][1] | [Feature][2]<[Point][5]> | [Array][6]<[number][7]>)** Point to snap from
17
30
  * `options` **[Object][8]** Optional parameters (optional, default `{}`)
18
31
 
19
32
  * `options.units` **Units** Supports all valid Turf [Units][9] (optional, default `'kilometers'`)
@@ -29,16 +42,16 @@ var line = turf.lineString([
29
42
  [-77.021884, 38.889563],
30
43
  [-77.019824, 38.892368]
31
44
  ]);
32
- var pt = turf.point([-77.037076, 38.884017]);
45
+ var inputPoint = turf.point([-77.037076, 38.884017]);
33
46
 
34
- var snapped = turf.nearestPointOnLine(line, pt, {units: 'miles'});
47
+ var snapped = turf.nearestPointOnLine(line, inputPoint, {units: 'miles'});
35
48
 
36
49
  //addToMap
37
- var addToMap = [line, pt, snapped];
50
+ var addToMap = [line, inputPoint, snapped];
38
51
  snapped.properties['marker-color'] = '#00f';
39
52
  ```
40
53
 
41
- 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.
54
+ Returns **[Feature][2]<[Point][5]>** closest point on the `lines` to the `inputPoint`. The point will have the following properties: `lineStringIndex`: closest point was found on the nth LineString (only relevant if input is MultiLineString), `segmentIndex`: closest point was found on nth line segment of the LineString, `totalDistance`: distance along the line from the absolute start of the MultiLineString, `lineDistance`: distance along the line from the start of the LineString where the closest point was found, `segmentDistance`: distance along the line from the start of the line segment where the closest point was found, `pointDistance`: distance to the input point.
42
55
 
43
56
  [1]: https://tools.ietf.org/html/rfc7946#section-3.1
44
57
 
@@ -27,56 +27,87 @@ var _meta = require('@turf/meta');
27
27
 
28
28
  var _helpers = require('@turf/helpers');
29
29
  var _invariant = require('@turf/invariant');
30
- function nearestPointOnLine(lines, pt, options = {}) {
31
- if (!lines || !pt) {
32
- throw new Error("lines and pt are required arguments");
30
+ function nearestPointOnLine(lines, inputPoint, options = {}) {
31
+ if (!lines || !inputPoint) {
32
+ throw new Error("lines and inputPoint are required arguments");
33
33
  }
34
- const ptPos = _invariant.getCoord.call(void 0, pt);
34
+ const inputPos = _invariant.getCoord.call(void 0, inputPoint);
35
35
  let closestPt = _helpers.point.call(void 0, [Infinity, Infinity], {
36
- dist: Infinity,
37
- index: -1,
36
+ lineStringIndex: -1,
37
+ segmentIndex: -1,
38
+ totalDistance: -1,
39
+ lineDistance: -1,
40
+ segmentDistance: -1,
41
+ pointDistance: Infinity,
42
+ // deprecated properties START
38
43
  multiFeatureIndex: -1,
39
- location: -1
44
+ index: -1,
45
+ location: -1,
46
+ dist: Infinity
47
+ // deprecated properties END
40
48
  });
41
- let length = 0;
49
+ let totalDistance = 0;
50
+ let lineDistance = 0;
51
+ let currentLineStringIndex = -1;
42
52
  _meta.flattenEach.call(void 0,
43
53
  lines,
44
- function(line, _featureIndex, multiFeatureIndex) {
54
+ function(line, _featureIndex, lineStringIndex) {
55
+ if (currentLineStringIndex !== lineStringIndex) {
56
+ currentLineStringIndex = lineStringIndex;
57
+ lineDistance = 0;
58
+ }
45
59
  const coords = _invariant.getCoords.call(void 0, line);
60
+ const maxSegmentIndex = coords.length - 2;
46
61
  for (let i = 0; i < coords.length - 1; i++) {
47
62
  const start = _helpers.point.call(void 0, coords[i]);
48
63
  const startPos = _invariant.getCoord.call(void 0, start);
49
64
  const stop = _helpers.point.call(void 0, coords[i + 1]);
50
65
  const stopPos = _invariant.getCoord.call(void 0, stop);
51
- const sectionLength = _distance.distance.call(void 0, start, stop, options);
66
+ const segmentLength = _distance.distance.call(void 0, start, stop, options);
52
67
  let intersectPos;
53
68
  let wasEnd;
54
- if (stopPos[0] === ptPos[0] && stopPos[1] === ptPos[1]) {
69
+ if (stopPos[0] === inputPos[0] && stopPos[1] === inputPos[1]) {
55
70
  [intersectPos, wasEnd] = [stopPos, true];
56
- } else if (startPos[0] === ptPos[0] && startPos[1] === ptPos[1]) {
71
+ } else if (startPos[0] === inputPos[0] && startPos[1] === inputPos[1]) {
57
72
  [intersectPos, wasEnd] = [startPos, false];
58
73
  } else {
59
74
  [intersectPos, wasEnd] = nearestPointOnSegment(
60
75
  startPos,
61
76
  stopPos,
62
- ptPos
77
+ inputPos
63
78
  );
64
79
  }
65
- const intersectPt = _helpers.point.call(void 0, intersectPos, {
66
- dist: _distance.distance.call(void 0, pt, intersectPos, options),
67
- multiFeatureIndex,
68
- location: length + _distance.distance.call(void 0, start, intersectPos, options)
69
- });
70
- if (intersectPt.properties.dist < closestPt.properties.dist) {
71
- closestPt = __spreadProps(__spreadValues({}, intersectPt), {
72
- properties: __spreadProps(__spreadValues({}, intersectPt.properties), {
73
- // Legacy behaviour where index progresses to next segment # if we
74
- // went with the end point this iteration.
75
- index: wasEnd ? i + 1 : i
76
- })
80
+ const pointDistance = _distance.distance.call(void 0, inputPoint, intersectPos, options);
81
+ if (pointDistance < closestPt.properties.pointDistance) {
82
+ const segmentDistance = _distance.distance.call(void 0, start, intersectPos, options);
83
+ closestPt = _helpers.point.call(void 0, intersectPos, {
84
+ lineStringIndex,
85
+ // Legacy behaviour where index progresses to next segment if we
86
+ // went with the end point this iteration. Though make sure we
87
+ // only progress to the beginning of the next segment if one
88
+ // actually exists.
89
+ segmentIndex: wasEnd && i + 1 <= maxSegmentIndex ? i + 1 : i,
90
+ totalDistance: totalDistance + segmentDistance,
91
+ lineDistance: lineDistance + segmentDistance,
92
+ segmentDistance,
93
+ pointDistance,
94
+ // deprecated properties START
95
+ multiFeatureIndex: -1,
96
+ index: -1,
97
+ location: -1,
98
+ dist: Infinity
99
+ // deprecated properties END
100
+ });
101
+ closestPt.properties = __spreadProps(__spreadValues({}, closestPt.properties), {
102
+ multiFeatureIndex: closestPt.properties.lineStringIndex,
103
+ index: closestPt.properties.segmentIndex,
104
+ location: closestPt.properties.totalDistance,
105
+ dist: closestPt.properties.pointDistance
106
+ // deprecated properties END
77
107
  });
78
108
  }
79
- length += sectionLength;
109
+ totalDistance += segmentLength;
110
+ lineDistance += segmentLength;
80
111
  }
81
112
  }
82
113
  );
@@ -1 +1 @@
1
- {"version":3,"sources":["/home/runner/work/turf/turf/packages/turf-nearest-point-on-line/dist/cjs/index.cjs","../../index.ts"],"names":[],"mappings":"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","file":"/home/runner/work/turf/turf/packages/turf-nearest-point-on-line/dist/cjs/index.cjs","sourcesContent":[null,"import { Feature, Point, Position, LineString, MultiLineString } from \"geojson\";\nimport { distance } from \"@turf/distance\";\nimport { flattenEach } from \"@turf/meta\";\nimport {\n point,\n degreesToRadians,\n radiansToDegrees,\n Coord,\n Units,\n} from \"@turf/helpers\";\nimport { getCoord, getCoords } from \"@turf/invariant\";\n\n/**\n * Returns the nearest point on a line to a given point.\n *\n * If any of the segments in the input line string are antipodal and therefore\n * have an undefined arc, this function will instead return that the point lies\n * on the line.\n *\n * @function\n * @param {Geometry|Feature<LineString|MultiLineString>} lines lines to snap to\n * @param {Geometry|Feature<Point>|number[]} pt point to snap from\n * @param {Object} [options={}] Optional parameters\n * @param {Units} [options.units='kilometers'] Supports all valid Turf {@link https://turfjs.org/docs/api/types/Units Units}\n * @returns {Feature<Point>} 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.\n * @example\n * var line = turf.lineString([\n * [-77.031669, 38.878605],\n * [-77.029609, 38.881946],\n * [-77.020339, 38.884084],\n * [-77.025661, 38.885821],\n * [-77.021884, 38.889563],\n * [-77.019824, 38.892368]\n * ]);\n * var pt = turf.point([-77.037076, 38.884017]);\n *\n * var snapped = turf.nearestPointOnLine(line, pt, {units: 'miles'});\n *\n * //addToMap\n * var addToMap = [line, pt, snapped];\n * snapped.properties['marker-color'] = '#00f';\n */\nfunction nearestPointOnLine<G extends LineString | MultiLineString>(\n lines: Feature<G> | G,\n pt: Coord,\n options: { units?: Units } = {}\n): Feature<\n Point,\n {\n dist: number;\n index: number;\n multiFeatureIndex: number;\n location: number;\n [key: string]: any;\n }\n> {\n if (!lines || !pt) {\n throw new Error(\"lines and pt are required arguments\");\n }\n\n const ptPos = getCoord(pt);\n\n let closestPt: Feature<\n Point,\n { dist: number; index: number; multiFeatureIndex: number; location: number }\n > = point([Infinity, Infinity], {\n dist: Infinity,\n index: -1,\n multiFeatureIndex: -1,\n location: -1,\n });\n\n let length = 0.0;\n flattenEach(\n lines,\n function (line: any, _featureIndex: number, multiFeatureIndex: number) {\n const coords: any = getCoords(line);\n\n for (let i = 0; i < coords.length - 1; i++) {\n //start - start of current line section\n const start: Feature<Point, { dist: number }> = point(coords[i]);\n const startPos = getCoord(start);\n\n //stop - end of current line section\n const stop: Feature<Point, { dist: number }> = point(coords[i + 1]);\n const stopPos = getCoord(stop);\n\n // sectionLength\n const sectionLength = distance(start, stop, options);\n let intersectPos: Position;\n let wasEnd: boolean;\n\n // Short circuit if snap point is start or end position of the line\n // Test the end position first for consistency in case they are\n // coincident\n if (stopPos[0] === ptPos[0] && stopPos[1] === ptPos[1]) {\n [intersectPos, wasEnd] = [stopPos, true];\n } else if (startPos[0] === ptPos[0] && startPos[1] === ptPos[1]) {\n [intersectPos, wasEnd] = [startPos, false];\n } else {\n // Otherwise, find the nearest point the hard way.\n [intersectPos, wasEnd] = nearestPointOnSegment(\n startPos,\n stopPos,\n ptPos\n );\n }\n\n const intersectPt = point(intersectPos, {\n dist: distance(pt, intersectPos, options),\n multiFeatureIndex: multiFeatureIndex,\n location: length + distance(start, intersectPos, options),\n });\n\n if (intersectPt.properties.dist < closestPt.properties.dist) {\n closestPt = {\n ...intersectPt,\n properties: {\n ...intersectPt.properties,\n // Legacy behaviour where index progresses to next segment # if we\n // went with the end point this iteration.\n index: wasEnd ? i + 1 : i,\n },\n };\n }\n\n // update length\n length += sectionLength;\n }\n }\n );\n\n return closestPt;\n}\n\n// A simple Vector3 type for cartesian operations.\ntype Vector = [number, number, number];\n\nfunction dot(v1: Vector, v2: Vector): number {\n const [v1x, v1y, v1z] = v1;\n const [v2x, v2y, v2z] = v2;\n return v1x * v2x + v1y * v2y + v1z * v2z;\n}\n\n// https://en.wikipedia.org/wiki/Cross_product\nfunction cross(v1: Vector, v2: Vector): Vector {\n const [v1x, v1y, v1z] = v1;\n const [v2x, v2y, v2z] = v2;\n return [v1y * v2z - v1z * v2y, v1z * v2x - v1x * v2z, v1x * v2y - v1y * v2x];\n}\n\nfunction magnitude(v: Vector): number {\n return Math.sqrt(Math.pow(v[0], 2) + Math.pow(v[1], 2) + Math.pow(v[2], 2));\n}\n\nfunction normalize(v: Vector): Vector {\n const mag = magnitude(v);\n return [v[0] / mag, v[1] / mag, v[2] / mag];\n}\n\nfunction lngLatToVector(a: Position): Vector {\n const lat = degreesToRadians(a[1]);\n const lng = degreesToRadians(a[0]);\n return [\n Math.cos(lat) * Math.cos(lng),\n Math.cos(lat) * Math.sin(lng),\n Math.sin(lat),\n ];\n}\n\nfunction vectorToLngLat(v: Vector): Position {\n const [x, y, z] = v;\n // Clamp the z-value to ensure that is inside the [-1, 1] domain as required\n // by asin. Note therefore that this function should only be applied to unit\n // vectors so z > 1 should not exist\n const zClamp = Math.min(Math.max(z, -1), 1);\n const lat = radiansToDegrees(Math.asin(zClamp));\n const lng = radiansToDegrees(Math.atan2(y, x));\n\n return [lng, lat];\n}\n\nfunction nearestPointOnSegment(\n posA: Position, // start point of segment to measure to\n posB: Position, // end point of segment to measure to\n posC: Position // point to measure from\n): [Position, boolean] {\n // Based heavily on this article on finding cross track distance to an arc:\n // https://gis.stackexchange.com/questions/209540/projecting-cross-track-distance-on-great-circle\n\n // Convert spherical (lng, lat) to cartesian vector coords (x, y, z)\n // In the below https://tikz.net/spherical_1/ we convert lng (𝜙) and lat (𝜃)\n // into vectors with x, y, and z components with a length (r) of 1.\n const A = lngLatToVector(posA); // the vector from 0,0,0 to posA\n const B = lngLatToVector(posB); // ... to posB\n const C = lngLatToVector(posC); // ... to posC\n\n // The axis (normal vector) of the great circle plane containing the line segment\n const segmentAxis = cross(A, B);\n\n // Two degenerate cases exist for the segment axis cross product. The first is\n // when vectors are aligned (within the bounds of floating point tolerance).\n // The second is where vectors are antipodal (again within the bounds of\n // tolerance. Both cases produce a [0, 0, 0] cross product which invalidates\n // the rest of the algorithm, but each case must be handled separately:\n // - The aligned case indicates coincidence of A and B. therefore this can be\n // an early return assuming the closest point is the end (for consistency).\n // - The antipodal case is truly degenerate - an infinte number of great\n // circles are possible and one will always pass through C. However, given\n // that this case is both highly unlikely to occur in practice and that is\n // will usually be logically sound to return that the point is on the\n // segment, we choose to return the provided point.\n if (segmentAxis[0] === 0 && segmentAxis[1] === 0 && segmentAxis[2] === 0) {\n if (dot(A, B) > 0) {\n return [[...posB], true];\n } else {\n return [[...posC], false];\n }\n }\n\n // The axis of the great circle passing through the segment's axis and the\n // target point\n const targetAxis = cross(segmentAxis, C);\n\n // This cross product also has a degenerate case where the segment axis is\n // coincident with or antipodal to the target point. In this case the point\n // is equidistant to the entire segment. For consistency, we early return the\n // endpoint as the matching point.\n if (targetAxis[0] === 0 && targetAxis[1] === 0 && targetAxis[2] === 0) {\n return [[...posB], true];\n }\n\n // The line of intersection between the two great circle planes\n const intersectionAxis = cross(targetAxis, segmentAxis);\n\n // Vectors to the two points these great circles intersect are the normalized\n // intersection and its antipodes\n const I1 = normalize(intersectionAxis);\n const I2: Vector = [-I1[0], -I1[1], -I1[2]];\n\n // Figure out which is the closest intersection to this segment of the great circle\n // Note that for points on a unit sphere, the dot product represents the\n // cosine of the angle between the two vectors which monotonically increases\n // the closer the two points are together and therefore determines proximity\n const I = dot(C, I1) > dot(C, I2) ? I1 : I2;\n\n // I is the closest intersection to the segment, though might not actually be\n // ON the segment. To test whether the closest intersection lies on the arc or\n // not, we do a cross product comparison to check rotation around the unit\n // circle defined by the great circle plane.\n const segmentAxisNorm = normalize(segmentAxis);\n const cmpAI = dot(cross(A, I), segmentAxisNorm);\n const cmpIB = dot(cross(I, B), segmentAxisNorm);\n\n // When both comparisons are positive, the rotation from A to I to B is in the\n // same direction, implying that I lies between A and B\n if (cmpAI >= 0 && cmpIB >= 0) {\n return [vectorToLngLat(I), false];\n }\n\n // Finally process the case where the intersection is not on the segment,\n // using the dot product with the original point to find the closest endpoint\n if (dot(A, C) > dot(B, C)) {\n // Clone position when returning as it is reasonable to not expect structural\n // sharing on the returned Position in all return cases\n return [[...posA], false];\n } else {\n return [[...posB], true];\n }\n}\n\nexport { nearestPointOnLine };\nexport default nearestPointOnLine;\n"]}
1
+ {"version":3,"sources":["/home/runner/work/turf/turf/packages/turf-nearest-point-on-line/dist/cjs/index.cjs","../../index.ts"],"names":[],"mappings":"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","file":"/home/runner/work/turf/turf/packages/turf-nearest-point-on-line/dist/cjs/index.cjs","sourcesContent":[null,"import { Feature, Point, Position, LineString, MultiLineString } from \"geojson\";\nimport { distance } from \"@turf/distance\";\nimport { flattenEach } from \"@turf/meta\";\nimport {\n point,\n degreesToRadians,\n radiansToDegrees,\n Coord,\n Units,\n} from \"@turf/helpers\";\nimport { getCoord, getCoords } from \"@turf/invariant\";\n\n/**\n * Returns the nearest point on a line to a given point.\n *\n * If any of the segments in the input line string are antipodal and therefore\n * have an undefined arc, this function will instead return that the point lies\n * on the line.\n *\n * ⚠️ We have begun the process of migrating to different return properties for\n * this function. The new properties we recommend using as of v7.4 are:\n * - lineStringIndex - point was found on the nth LineString of an input MultiLineString. Previously `multiFeatureIndex`\n * - segmentIndex - point was found on the nth segment of the above LineString. Previously `index`\n * - totalDistance - distance from the start of the overall MultiLineString. Previously `location`\n * - lineDistance - distance from the start of the relevant LineString\n * - segmentDistance - distance from the start of the relevant segment\n * - pointDistance - distance between found point is from input reference point. Previously `dist`\n *\n * multiFeatureIndex, index, location, and dist continue to work as previously\n * until at least the next major release.\n *\n * @function\n * @param {Geometry|Feature<LineString|MultiLineString>} lines Lines to snap to\n * @param {Geometry|Feature<Point>|number[]} inputPoint Point to snap from\n * @param {Object} [options={}] Optional parameters\n * @param {Units} [options.units='kilometers'] Supports all valid Turf {@link https://turfjs.org/docs/api/types/Units Units}\n * @returns {Feature<Point>} closest point on the `lines` to the `inputPoint`. The point will have the following properties: `lineStringIndex`: closest point was found on the nth LineString (only relevant if input is MultiLineString), `segmentIndex`: closest point was found on nth line segment of the LineString, `totalDistance`: distance along the line from the absolute start of the MultiLineString, `lineDistance`: distance along the line from the start of the LineString where the closest point was found, `segmentDistance`: distance along the line from the start of the line segment where the closest point was found, `pointDistance`: distance to the input point.\n * @example\n * var line = turf.lineString([\n * [-77.031669, 38.878605],\n * [-77.029609, 38.881946],\n * [-77.020339, 38.884084],\n * [-77.025661, 38.885821],\n * [-77.021884, 38.889563],\n * [-77.019824, 38.892368]\n * ]);\n * var inputPoint = turf.point([-77.037076, 38.884017]);\n *\n * var snapped = turf.nearestPointOnLine(line, inputPoint, {units: 'miles'});\n *\n * //addToMap\n * var addToMap = [line, inputPoint, snapped];\n * snapped.properties['marker-color'] = '#00f';\n */\nfunction nearestPointOnLine<G extends LineString | MultiLineString>(\n lines: Feature<G> | G,\n inputPoint: Coord,\n options: { units?: Units } = {}\n): Feature<\n Point,\n {\n lineStringIndex: number;\n segmentIndex: number;\n totalDistance: number;\n lineDistance: number;\n segmentDistance: number;\n pointDistance: number;\n multiFeatureIndex: number;\n index: number;\n location: number;\n dist: number;\n [key: string]: any;\n }\n> {\n if (!lines || !inputPoint) {\n throw new Error(\"lines and inputPoint are required arguments\");\n }\n\n const inputPos = getCoord(inputPoint);\n\n let closestPt = point([Infinity, Infinity], {\n lineStringIndex: -1,\n segmentIndex: -1,\n totalDistance: -1,\n lineDistance: -1,\n segmentDistance: -1,\n pointDistance: Infinity,\n // deprecated properties START\n multiFeatureIndex: -1,\n index: -1,\n location: -1,\n dist: Infinity,\n // deprecated properties END\n });\n\n let totalDistance = 0.0;\n let lineDistance = 0.0;\n let currentLineStringIndex = -1;\n flattenEach(\n lines,\n function (line: any, _featureIndex: number, lineStringIndex: number) {\n //reset lineDistance at each changed lineStringIndex\n if (currentLineStringIndex !== lineStringIndex) {\n currentLineStringIndex = lineStringIndex;\n lineDistance = 0.0;\n }\n\n const coords: any = getCoords(line);\n const maxSegmentIndex = coords.length - 2;\n\n for (let i = 0; i < coords.length - 1; i++) {\n //start - start of current line section\n const start: Feature<Point, { dist: number }> = point(coords[i]);\n const startPos = getCoord(start);\n\n //stop - end of current line section\n const stop: Feature<Point, { dist: number }> = point(coords[i + 1]);\n const stopPos = getCoord(stop);\n\n // segmentLength\n const segmentLength = distance(start, stop, options);\n let intersectPos: Position;\n let wasEnd: boolean;\n\n // Short circuit if snap point is start or end position of the line\n // Test the end position first for consistency in case they are\n // coincident\n if (stopPos[0] === inputPos[0] && stopPos[1] === inputPos[1]) {\n [intersectPos, wasEnd] = [stopPos, true];\n } else if (startPos[0] === inputPos[0] && startPos[1] === inputPos[1]) {\n [intersectPos, wasEnd] = [startPos, false];\n } else {\n // Otherwise, find the nearest point the hard way.\n [intersectPos, wasEnd] = nearestPointOnSegment(\n startPos,\n stopPos,\n inputPos\n );\n }\n\n const pointDistance = distance(inputPoint, intersectPos, options);\n\n if (pointDistance < closestPt.properties.pointDistance) {\n const segmentDistance = distance(start, intersectPos, options);\n closestPt = point(intersectPos, {\n lineStringIndex: lineStringIndex,\n // Legacy behaviour where index progresses to next segment if we\n // went with the end point this iteration. Though make sure we\n // only progress to the beginning of the next segment if one\n // actually exists.\n segmentIndex: wasEnd && i + 1 <= maxSegmentIndex ? i + 1 : i,\n totalDistance: totalDistance + segmentDistance,\n lineDistance: lineDistance + segmentDistance,\n segmentDistance: segmentDistance,\n pointDistance: pointDistance,\n // deprecated properties START\n multiFeatureIndex: -1,\n index: -1,\n location: -1,\n dist: Infinity,\n // deprecated properties END\n });\n closestPt.properties = {\n ...closestPt.properties,\n multiFeatureIndex: closestPt.properties.lineStringIndex,\n index: closestPt.properties.segmentIndex,\n location: closestPt.properties.totalDistance,\n dist: closestPt.properties.pointDistance,\n // deprecated properties END\n };\n }\n\n // update totalDistance and lineDistance\n totalDistance += segmentLength;\n lineDistance += segmentLength;\n }\n }\n );\n\n return closestPt;\n}\n\n// A simple Vector3 type for cartesian operations.\ntype Vector = [number, number, number];\n\nfunction dot(v1: Vector, v2: Vector): number {\n const [v1x, v1y, v1z] = v1;\n const [v2x, v2y, v2z] = v2;\n return v1x * v2x + v1y * v2y + v1z * v2z;\n}\n\n// https://en.wikipedia.org/wiki/Cross_product\nfunction cross(v1: Vector, v2: Vector): Vector {\n const [v1x, v1y, v1z] = v1;\n const [v2x, v2y, v2z] = v2;\n return [v1y * v2z - v1z * v2y, v1z * v2x - v1x * v2z, v1x * v2y - v1y * v2x];\n}\n\nfunction magnitude(v: Vector): number {\n return Math.sqrt(Math.pow(v[0], 2) + Math.pow(v[1], 2) + Math.pow(v[2], 2));\n}\n\nfunction normalize(v: Vector): Vector {\n const mag = magnitude(v);\n return [v[0] / mag, v[1] / mag, v[2] / mag];\n}\n\nfunction lngLatToVector(a: Position): Vector {\n const lat = degreesToRadians(a[1]);\n const lng = degreesToRadians(a[0]);\n return [\n Math.cos(lat) * Math.cos(lng),\n Math.cos(lat) * Math.sin(lng),\n Math.sin(lat),\n ];\n}\n\nfunction vectorToLngLat(v: Vector): Position {\n const [x, y, z] = v;\n // Clamp the z-value to ensure that is inside the [-1, 1] domain as required\n // by asin. Note therefore that this function should only be applied to unit\n // vectors so z > 1 should not exist\n const zClamp = Math.min(Math.max(z, -1), 1);\n const lat = radiansToDegrees(Math.asin(zClamp));\n const lng = radiansToDegrees(Math.atan2(y, x));\n\n return [lng, lat];\n}\n\nfunction nearestPointOnSegment(\n posA: Position, // start point of segment to measure to\n posB: Position, // end point of segment to measure to\n posC: Position // point to measure from\n): [Position, boolean] {\n // Based heavily on this article on finding cross track distance to an arc:\n // https://gis.stackexchange.com/questions/209540/projecting-cross-track-distance-on-great-circle\n\n // Convert spherical (lng, lat) to cartesian vector coords (x, y, z)\n // In the below https://tikz.net/spherical_1/ we convert lng (𝜙) and lat (𝜃)\n // into vectors with x, y, and z components with a length (r) of 1.\n const A = lngLatToVector(posA); // the vector from 0,0,0 to posA\n const B = lngLatToVector(posB); // ... to posB\n const C = lngLatToVector(posC); // ... to posC\n\n // The axis (normal vector) of the great circle plane containing the line segment\n const segmentAxis = cross(A, B);\n\n // Two degenerate cases exist for the segment axis cross product. The first is\n // when vectors are aligned (within the bounds of floating point tolerance).\n // The second is where vectors are antipodal (again within the bounds of\n // tolerance. Both cases produce a [0, 0, 0] cross product which invalidates\n // the rest of the algorithm, but each case must be handled separately:\n // - The aligned case indicates coincidence of A and B. therefore this can be\n // an early return assuming the closest point is the end (for consistency).\n // - The antipodal case is truly degenerate - an infinte number of great\n // circles are possible and one will always pass through C. However, given\n // that this case is both highly unlikely to occur in practice and that is\n // will usually be logically sound to return that the point is on the\n // segment, we choose to return the provided point.\n if (segmentAxis[0] === 0 && segmentAxis[1] === 0 && segmentAxis[2] === 0) {\n if (dot(A, B) > 0) {\n return [[...posB], true];\n } else {\n return [[...posC], false];\n }\n }\n\n // The axis of the great circle passing through the segment's axis and the\n // target point\n const targetAxis = cross(segmentAxis, C);\n\n // This cross product also has a degenerate case where the segment axis is\n // coincident with or antipodal to the target point. In this case the point\n // is equidistant to the entire segment. For consistency, we early return the\n // endpoint as the matching point.\n if (targetAxis[0] === 0 && targetAxis[1] === 0 && targetAxis[2] === 0) {\n return [[...posB], true];\n }\n\n // The line of intersection between the two great circle planes\n const intersectionAxis = cross(targetAxis, segmentAxis);\n\n // Vectors to the two points these great circles intersect are the normalized\n // intersection and its antipodes\n const I1 = normalize(intersectionAxis);\n const I2: Vector = [-I1[0], -I1[1], -I1[2]];\n\n // Figure out which is the closest intersection to this segment of the great circle\n // Note that for points on a unit sphere, the dot product represents the\n // cosine of the angle between the two vectors which monotonically increases\n // the closer the two points are together and therefore determines proximity\n const I = dot(C, I1) > dot(C, I2) ? I1 : I2;\n\n // I is the closest intersection to the segment, though might not actually be\n // ON the segment. To test whether the closest intersection lies on the arc or\n // not, we do a cross product comparison to check rotation around the unit\n // circle defined by the great circle plane.\n const segmentAxisNorm = normalize(segmentAxis);\n const cmpAI = dot(cross(A, I), segmentAxisNorm);\n const cmpIB = dot(cross(I, B), segmentAxisNorm);\n\n // When both comparisons are positive, the rotation from A to I to B is in the\n // same direction, implying that I lies between A and B\n if (cmpAI >= 0 && cmpIB >= 0) {\n return [vectorToLngLat(I), false];\n }\n\n // Finally process the case where the intersection is not on the segment,\n // using the dot product with the original point to find the closest endpoint\n if (dot(A, C) > dot(B, C)) {\n // Clone position when returning as it is reasonable to not expect structural\n // sharing on the returned Position in all return cases\n return [[...posA], false];\n } else {\n return [[...posB], true];\n }\n}\n\nexport { nearestPointOnLine };\nexport default nearestPointOnLine;\n"]}
@@ -8,12 +8,24 @@ import { Coord, Units } from '@turf/helpers';
8
8
  * have an undefined arc, this function will instead return that the point lies
9
9
  * on the line.
10
10
  *
11
+ * ⚠️ We have begun the process of migrating to different return properties for
12
+ * this function. The new properties we recommend using as of v7.4 are:
13
+ * - lineStringIndex - point was found on the nth LineString of an input MultiLineString. Previously `multiFeatureIndex`
14
+ * - segmentIndex - point was found on the nth segment of the above LineString. Previously `index`
15
+ * - totalDistance - distance from the start of the overall MultiLineString. Previously `location`
16
+ * - lineDistance - distance from the start of the relevant LineString
17
+ * - segmentDistance - distance from the start of the relevant segment
18
+ * - pointDistance - distance between found point is from input reference point. Previously `dist`
19
+ *
20
+ * multiFeatureIndex, index, location, and dist continue to work as previously
21
+ * until at least the next major release.
22
+ *
11
23
  * @function
12
- * @param {Geometry|Feature<LineString|MultiLineString>} lines lines to snap to
13
- * @param {Geometry|Feature<Point>|number[]} pt point to snap from
24
+ * @param {Geometry|Feature<LineString|MultiLineString>} lines Lines to snap to
25
+ * @param {Geometry|Feature<Point>|number[]} inputPoint Point to snap from
14
26
  * @param {Object} [options={}] Optional parameters
15
27
  * @param {Units} [options.units='kilometers'] Supports all valid Turf {@link https://turfjs.org/docs/api/types/Units Units}
16
- * @returns {Feature<Point>} 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.
28
+ * @returns {Feature<Point>} closest point on the `lines` to the `inputPoint`. The point will have the following properties: `lineStringIndex`: closest point was found on the nth LineString (only relevant if input is MultiLineString), `segmentIndex`: closest point was found on nth line segment of the LineString, `totalDistance`: distance along the line from the absolute start of the MultiLineString, `lineDistance`: distance along the line from the start of the LineString where the closest point was found, `segmentDistance`: distance along the line from the start of the line segment where the closest point was found, `pointDistance`: distance to the input point.
17
29
  * @example
18
30
  * var line = turf.lineString([
19
31
  * [-77.031669, 38.878605],
@@ -23,21 +35,27 @@ import { Coord, Units } from '@turf/helpers';
23
35
  * [-77.021884, 38.889563],
24
36
  * [-77.019824, 38.892368]
25
37
  * ]);
26
- * var pt = turf.point([-77.037076, 38.884017]);
38
+ * var inputPoint = turf.point([-77.037076, 38.884017]);
27
39
  *
28
- * var snapped = turf.nearestPointOnLine(line, pt, {units: 'miles'});
40
+ * var snapped = turf.nearestPointOnLine(line, inputPoint, {units: 'miles'});
29
41
  *
30
42
  * //addToMap
31
- * var addToMap = [line, pt, snapped];
43
+ * var addToMap = [line, inputPoint, snapped];
32
44
  * snapped.properties['marker-color'] = '#00f';
33
45
  */
34
- declare function nearestPointOnLine<G extends LineString | MultiLineString>(lines: Feature<G> | G, pt: Coord, options?: {
46
+ declare function nearestPointOnLine<G extends LineString | MultiLineString>(lines: Feature<G> | G, inputPoint: Coord, options?: {
35
47
  units?: Units;
36
48
  }): Feature<Point, {
37
- dist: number;
38
- index: number;
49
+ lineStringIndex: number;
50
+ segmentIndex: number;
51
+ totalDistance: number;
52
+ lineDistance: number;
53
+ segmentDistance: number;
54
+ pointDistance: number;
39
55
  multiFeatureIndex: number;
56
+ index: number;
40
57
  location: number;
58
+ dist: number;
41
59
  [key: string]: any;
42
60
  }>;
43
61
 
@@ -8,12 +8,24 @@ import { Coord, Units } from '@turf/helpers';
8
8
  * have an undefined arc, this function will instead return that the point lies
9
9
  * on the line.
10
10
  *
11
+ * ⚠️ We have begun the process of migrating to different return properties for
12
+ * this function. The new properties we recommend using as of v7.4 are:
13
+ * - lineStringIndex - point was found on the nth LineString of an input MultiLineString. Previously `multiFeatureIndex`
14
+ * - segmentIndex - point was found on the nth segment of the above LineString. Previously `index`
15
+ * - totalDistance - distance from the start of the overall MultiLineString. Previously `location`
16
+ * - lineDistance - distance from the start of the relevant LineString
17
+ * - segmentDistance - distance from the start of the relevant segment
18
+ * - pointDistance - distance between found point is from input reference point. Previously `dist`
19
+ *
20
+ * multiFeatureIndex, index, location, and dist continue to work as previously
21
+ * until at least the next major release.
22
+ *
11
23
  * @function
12
- * @param {Geometry|Feature<LineString|MultiLineString>} lines lines to snap to
13
- * @param {Geometry|Feature<Point>|number[]} pt point to snap from
24
+ * @param {Geometry|Feature<LineString|MultiLineString>} lines Lines to snap to
25
+ * @param {Geometry|Feature<Point>|number[]} inputPoint Point to snap from
14
26
  * @param {Object} [options={}] Optional parameters
15
27
  * @param {Units} [options.units='kilometers'] Supports all valid Turf {@link https://turfjs.org/docs/api/types/Units Units}
16
- * @returns {Feature<Point>} 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.
28
+ * @returns {Feature<Point>} closest point on the `lines` to the `inputPoint`. The point will have the following properties: `lineStringIndex`: closest point was found on the nth LineString (only relevant if input is MultiLineString), `segmentIndex`: closest point was found on nth line segment of the LineString, `totalDistance`: distance along the line from the absolute start of the MultiLineString, `lineDistance`: distance along the line from the start of the LineString where the closest point was found, `segmentDistance`: distance along the line from the start of the line segment where the closest point was found, `pointDistance`: distance to the input point.
17
29
  * @example
18
30
  * var line = turf.lineString([
19
31
  * [-77.031669, 38.878605],
@@ -23,21 +35,27 @@ import { Coord, Units } from '@turf/helpers';
23
35
  * [-77.021884, 38.889563],
24
36
  * [-77.019824, 38.892368]
25
37
  * ]);
26
- * var pt = turf.point([-77.037076, 38.884017]);
38
+ * var inputPoint = turf.point([-77.037076, 38.884017]);
27
39
  *
28
- * var snapped = turf.nearestPointOnLine(line, pt, {units: 'miles'});
40
+ * var snapped = turf.nearestPointOnLine(line, inputPoint, {units: 'miles'});
29
41
  *
30
42
  * //addToMap
31
- * var addToMap = [line, pt, snapped];
43
+ * var addToMap = [line, inputPoint, snapped];
32
44
  * snapped.properties['marker-color'] = '#00f';
33
45
  */
34
- declare function nearestPointOnLine<G extends LineString | MultiLineString>(lines: Feature<G> | G, pt: Coord, options?: {
46
+ declare function nearestPointOnLine<G extends LineString | MultiLineString>(lines: Feature<G> | G, inputPoint: Coord, options?: {
35
47
  units?: Units;
36
48
  }): Feature<Point, {
37
- dist: number;
38
- index: number;
49
+ lineStringIndex: number;
50
+ segmentIndex: number;
51
+ totalDistance: number;
52
+ lineDistance: number;
53
+ segmentDistance: number;
54
+ pointDistance: number;
39
55
  multiFeatureIndex: number;
56
+ index: number;
40
57
  location: number;
58
+ dist: number;
41
59
  [key: string]: any;
42
60
  }>;
43
61
 
package/dist/esm/index.js CHANGED
@@ -27,56 +27,87 @@ import {
27
27
  radiansToDegrees
28
28
  } from "@turf/helpers";
29
29
  import { getCoord, getCoords } from "@turf/invariant";
30
- function nearestPointOnLine(lines, pt, options = {}) {
31
- if (!lines || !pt) {
32
- throw new Error("lines and pt are required arguments");
30
+ function nearestPointOnLine(lines, inputPoint, options = {}) {
31
+ if (!lines || !inputPoint) {
32
+ throw new Error("lines and inputPoint are required arguments");
33
33
  }
34
- const ptPos = getCoord(pt);
34
+ const inputPos = getCoord(inputPoint);
35
35
  let closestPt = point([Infinity, Infinity], {
36
- dist: Infinity,
37
- index: -1,
36
+ lineStringIndex: -1,
37
+ segmentIndex: -1,
38
+ totalDistance: -1,
39
+ lineDistance: -1,
40
+ segmentDistance: -1,
41
+ pointDistance: Infinity,
42
+ // deprecated properties START
38
43
  multiFeatureIndex: -1,
39
- location: -1
44
+ index: -1,
45
+ location: -1,
46
+ dist: Infinity
47
+ // deprecated properties END
40
48
  });
41
- let length = 0;
49
+ let totalDistance = 0;
50
+ let lineDistance = 0;
51
+ let currentLineStringIndex = -1;
42
52
  flattenEach(
43
53
  lines,
44
- function(line, _featureIndex, multiFeatureIndex) {
54
+ function(line, _featureIndex, lineStringIndex) {
55
+ if (currentLineStringIndex !== lineStringIndex) {
56
+ currentLineStringIndex = lineStringIndex;
57
+ lineDistance = 0;
58
+ }
45
59
  const coords = getCoords(line);
60
+ const maxSegmentIndex = coords.length - 2;
46
61
  for (let i = 0; i < coords.length - 1; i++) {
47
62
  const start = point(coords[i]);
48
63
  const startPos = getCoord(start);
49
64
  const stop = point(coords[i + 1]);
50
65
  const stopPos = getCoord(stop);
51
- const sectionLength = distance(start, stop, options);
66
+ const segmentLength = distance(start, stop, options);
52
67
  let intersectPos;
53
68
  let wasEnd;
54
- if (stopPos[0] === ptPos[0] && stopPos[1] === ptPos[1]) {
69
+ if (stopPos[0] === inputPos[0] && stopPos[1] === inputPos[1]) {
55
70
  [intersectPos, wasEnd] = [stopPos, true];
56
- } else if (startPos[0] === ptPos[0] && startPos[1] === ptPos[1]) {
71
+ } else if (startPos[0] === inputPos[0] && startPos[1] === inputPos[1]) {
57
72
  [intersectPos, wasEnd] = [startPos, false];
58
73
  } else {
59
74
  [intersectPos, wasEnd] = nearestPointOnSegment(
60
75
  startPos,
61
76
  stopPos,
62
- ptPos
77
+ inputPos
63
78
  );
64
79
  }
65
- const intersectPt = point(intersectPos, {
66
- dist: distance(pt, intersectPos, options),
67
- multiFeatureIndex,
68
- location: length + distance(start, intersectPos, options)
69
- });
70
- if (intersectPt.properties.dist < closestPt.properties.dist) {
71
- closestPt = __spreadProps(__spreadValues({}, intersectPt), {
72
- properties: __spreadProps(__spreadValues({}, intersectPt.properties), {
73
- // Legacy behaviour where index progresses to next segment # if we
74
- // went with the end point this iteration.
75
- index: wasEnd ? i + 1 : i
76
- })
80
+ const pointDistance = distance(inputPoint, intersectPos, options);
81
+ if (pointDistance < closestPt.properties.pointDistance) {
82
+ const segmentDistance = distance(start, intersectPos, options);
83
+ closestPt = point(intersectPos, {
84
+ lineStringIndex,
85
+ // Legacy behaviour where index progresses to next segment if we
86
+ // went with the end point this iteration. Though make sure we
87
+ // only progress to the beginning of the next segment if one
88
+ // actually exists.
89
+ segmentIndex: wasEnd && i + 1 <= maxSegmentIndex ? i + 1 : i,
90
+ totalDistance: totalDistance + segmentDistance,
91
+ lineDistance: lineDistance + segmentDistance,
92
+ segmentDistance,
93
+ pointDistance,
94
+ // deprecated properties START
95
+ multiFeatureIndex: -1,
96
+ index: -1,
97
+ location: -1,
98
+ dist: Infinity
99
+ // deprecated properties END
100
+ });
101
+ closestPt.properties = __spreadProps(__spreadValues({}, closestPt.properties), {
102
+ multiFeatureIndex: closestPt.properties.lineStringIndex,
103
+ index: closestPt.properties.segmentIndex,
104
+ location: closestPt.properties.totalDistance,
105
+ dist: closestPt.properties.pointDistance
106
+ // deprecated properties END
77
107
  });
78
108
  }
79
- length += sectionLength;
109
+ totalDistance += segmentLength;
110
+ lineDistance += segmentLength;
80
111
  }
81
112
  }
82
113
  );
@@ -1 +1 @@
1
- {"version":3,"sources":["../../index.ts"],"sourcesContent":["import { Feature, Point, Position, LineString, MultiLineString } from \"geojson\";\nimport { distance } from \"@turf/distance\";\nimport { flattenEach } from \"@turf/meta\";\nimport {\n point,\n degreesToRadians,\n radiansToDegrees,\n Coord,\n Units,\n} from \"@turf/helpers\";\nimport { getCoord, getCoords } from \"@turf/invariant\";\n\n/**\n * Returns the nearest point on a line to a given point.\n *\n * If any of the segments in the input line string are antipodal and therefore\n * have an undefined arc, this function will instead return that the point lies\n * on the line.\n *\n * @function\n * @param {Geometry|Feature<LineString|MultiLineString>} lines lines to snap to\n * @param {Geometry|Feature<Point>|number[]} pt point to snap from\n * @param {Object} [options={}] Optional parameters\n * @param {Units} [options.units='kilometers'] Supports all valid Turf {@link https://turfjs.org/docs/api/types/Units Units}\n * @returns {Feature<Point>} 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.\n * @example\n * var line = turf.lineString([\n * [-77.031669, 38.878605],\n * [-77.029609, 38.881946],\n * [-77.020339, 38.884084],\n * [-77.025661, 38.885821],\n * [-77.021884, 38.889563],\n * [-77.019824, 38.892368]\n * ]);\n * var pt = turf.point([-77.037076, 38.884017]);\n *\n * var snapped = turf.nearestPointOnLine(line, pt, {units: 'miles'});\n *\n * //addToMap\n * var addToMap = [line, pt, snapped];\n * snapped.properties['marker-color'] = '#00f';\n */\nfunction nearestPointOnLine<G extends LineString | MultiLineString>(\n lines: Feature<G> | G,\n pt: Coord,\n options: { units?: Units } = {}\n): Feature<\n Point,\n {\n dist: number;\n index: number;\n multiFeatureIndex: number;\n location: number;\n [key: string]: any;\n }\n> {\n if (!lines || !pt) {\n throw new Error(\"lines and pt are required arguments\");\n }\n\n const ptPos = getCoord(pt);\n\n let closestPt: Feature<\n Point,\n { dist: number; index: number; multiFeatureIndex: number; location: number }\n > = point([Infinity, Infinity], {\n dist: Infinity,\n index: -1,\n multiFeatureIndex: -1,\n location: -1,\n });\n\n let length = 0.0;\n flattenEach(\n lines,\n function (line: any, _featureIndex: number, multiFeatureIndex: number) {\n const coords: any = getCoords(line);\n\n for (let i = 0; i < coords.length - 1; i++) {\n //start - start of current line section\n const start: Feature<Point, { dist: number }> = point(coords[i]);\n const startPos = getCoord(start);\n\n //stop - end of current line section\n const stop: Feature<Point, { dist: number }> = point(coords[i + 1]);\n const stopPos = getCoord(stop);\n\n // sectionLength\n const sectionLength = distance(start, stop, options);\n let intersectPos: Position;\n let wasEnd: boolean;\n\n // Short circuit if snap point is start or end position of the line\n // Test the end position first for consistency in case they are\n // coincident\n if (stopPos[0] === ptPos[0] && stopPos[1] === ptPos[1]) {\n [intersectPos, wasEnd] = [stopPos, true];\n } else if (startPos[0] === ptPos[0] && startPos[1] === ptPos[1]) {\n [intersectPos, wasEnd] = [startPos, false];\n } else {\n // Otherwise, find the nearest point the hard way.\n [intersectPos, wasEnd] = nearestPointOnSegment(\n startPos,\n stopPos,\n ptPos\n );\n }\n\n const intersectPt = point(intersectPos, {\n dist: distance(pt, intersectPos, options),\n multiFeatureIndex: multiFeatureIndex,\n location: length + distance(start, intersectPos, options),\n });\n\n if (intersectPt.properties.dist < closestPt.properties.dist) {\n closestPt = {\n ...intersectPt,\n properties: {\n ...intersectPt.properties,\n // Legacy behaviour where index progresses to next segment # if we\n // went with the end point this iteration.\n index: wasEnd ? i + 1 : i,\n },\n };\n }\n\n // update length\n length += sectionLength;\n }\n }\n );\n\n return closestPt;\n}\n\n// A simple Vector3 type for cartesian operations.\ntype Vector = [number, number, number];\n\nfunction dot(v1: Vector, v2: Vector): number {\n const [v1x, v1y, v1z] = v1;\n const [v2x, v2y, v2z] = v2;\n return v1x * v2x + v1y * v2y + v1z * v2z;\n}\n\n// https://en.wikipedia.org/wiki/Cross_product\nfunction cross(v1: Vector, v2: Vector): Vector {\n const [v1x, v1y, v1z] = v1;\n const [v2x, v2y, v2z] = v2;\n return [v1y * v2z - v1z * v2y, v1z * v2x - v1x * v2z, v1x * v2y - v1y * v2x];\n}\n\nfunction magnitude(v: Vector): number {\n return Math.sqrt(Math.pow(v[0], 2) + Math.pow(v[1], 2) + Math.pow(v[2], 2));\n}\n\nfunction normalize(v: Vector): Vector {\n const mag = magnitude(v);\n return [v[0] / mag, v[1] / mag, v[2] / mag];\n}\n\nfunction lngLatToVector(a: Position): Vector {\n const lat = degreesToRadians(a[1]);\n const lng = degreesToRadians(a[0]);\n return [\n Math.cos(lat) * Math.cos(lng),\n Math.cos(lat) * Math.sin(lng),\n Math.sin(lat),\n ];\n}\n\nfunction vectorToLngLat(v: Vector): Position {\n const [x, y, z] = v;\n // Clamp the z-value to ensure that is inside the [-1, 1] domain as required\n // by asin. Note therefore that this function should only be applied to unit\n // vectors so z > 1 should not exist\n const zClamp = Math.min(Math.max(z, -1), 1);\n const lat = radiansToDegrees(Math.asin(zClamp));\n const lng = radiansToDegrees(Math.atan2(y, x));\n\n return [lng, lat];\n}\n\nfunction nearestPointOnSegment(\n posA: Position, // start point of segment to measure to\n posB: Position, // end point of segment to measure to\n posC: Position // point to measure from\n): [Position, boolean] {\n // Based heavily on this article on finding cross track distance to an arc:\n // https://gis.stackexchange.com/questions/209540/projecting-cross-track-distance-on-great-circle\n\n // Convert spherical (lng, lat) to cartesian vector coords (x, y, z)\n // In the below https://tikz.net/spherical_1/ we convert lng (𝜙) and lat (𝜃)\n // into vectors with x, y, and z components with a length (r) of 1.\n const A = lngLatToVector(posA); // the vector from 0,0,0 to posA\n const B = lngLatToVector(posB); // ... to posB\n const C = lngLatToVector(posC); // ... to posC\n\n // The axis (normal vector) of the great circle plane containing the line segment\n const segmentAxis = cross(A, B);\n\n // Two degenerate cases exist for the segment axis cross product. The first is\n // when vectors are aligned (within the bounds of floating point tolerance).\n // The second is where vectors are antipodal (again within the bounds of\n // tolerance. Both cases produce a [0, 0, 0] cross product which invalidates\n // the rest of the algorithm, but each case must be handled separately:\n // - The aligned case indicates coincidence of A and B. therefore this can be\n // an early return assuming the closest point is the end (for consistency).\n // - The antipodal case is truly degenerate - an infinte number of great\n // circles are possible and one will always pass through C. However, given\n // that this case is both highly unlikely to occur in practice and that is\n // will usually be logically sound to return that the point is on the\n // segment, we choose to return the provided point.\n if (segmentAxis[0] === 0 && segmentAxis[1] === 0 && segmentAxis[2] === 0) {\n if (dot(A, B) > 0) {\n return [[...posB], true];\n } else {\n return [[...posC], false];\n }\n }\n\n // The axis of the great circle passing through the segment's axis and the\n // target point\n const targetAxis = cross(segmentAxis, C);\n\n // This cross product also has a degenerate case where the segment axis is\n // coincident with or antipodal to the target point. In this case the point\n // is equidistant to the entire segment. For consistency, we early return the\n // endpoint as the matching point.\n if (targetAxis[0] === 0 && targetAxis[1] === 0 && targetAxis[2] === 0) {\n return [[...posB], true];\n }\n\n // The line of intersection between the two great circle planes\n const intersectionAxis = cross(targetAxis, segmentAxis);\n\n // Vectors to the two points these great circles intersect are the normalized\n // intersection and its antipodes\n const I1 = normalize(intersectionAxis);\n const I2: Vector = [-I1[0], -I1[1], -I1[2]];\n\n // Figure out which is the closest intersection to this segment of the great circle\n // Note that for points on a unit sphere, the dot product represents the\n // cosine of the angle between the two vectors which monotonically increases\n // the closer the two points are together and therefore determines proximity\n const I = dot(C, I1) > dot(C, I2) ? I1 : I2;\n\n // I is the closest intersection to the segment, though might not actually be\n // ON the segment. To test whether the closest intersection lies on the arc or\n // not, we do a cross product comparison to check rotation around the unit\n // circle defined by the great circle plane.\n const segmentAxisNorm = normalize(segmentAxis);\n const cmpAI = dot(cross(A, I), segmentAxisNorm);\n const cmpIB = dot(cross(I, B), segmentAxisNorm);\n\n // When both comparisons are positive, the rotation from A to I to B is in the\n // same direction, implying that I lies between A and B\n if (cmpAI >= 0 && cmpIB >= 0) {\n return [vectorToLngLat(I), false];\n }\n\n // Finally process the case where the intersection is not on the segment,\n // using the dot product with the original point to find the closest endpoint\n if (dot(A, C) > dot(B, C)) {\n // Clone position when returning as it is reasonable to not expect structural\n // sharing on the returned Position in all return cases\n return [[...posA], false];\n } else {\n return [[...posB], true];\n }\n}\n\nexport { nearestPointOnLine };\nexport default nearestPointOnLine;\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../index.ts"],"sourcesContent":["import { Feature, Point, Position, LineString, MultiLineString } from \"geojson\";\nimport { distance } from \"@turf/distance\";\nimport { flattenEach } from \"@turf/meta\";\nimport {\n point,\n degreesToRadians,\n radiansToDegrees,\n Coord,\n Units,\n} from \"@turf/helpers\";\nimport { getCoord, getCoords } from \"@turf/invariant\";\n\n/**\n * Returns the nearest point on a line to a given point.\n *\n * If any of the segments in the input line string are antipodal and therefore\n * have an undefined arc, this function will instead return that the point lies\n * on the line.\n *\n * ⚠️ We have begun the process of migrating to different return properties for\n * this function. The new properties we recommend using as of v7.4 are:\n * - lineStringIndex - point was found on the nth LineString of an input MultiLineString. Previously `multiFeatureIndex`\n * - segmentIndex - point was found on the nth segment of the above LineString. Previously `index`\n * - totalDistance - distance from the start of the overall MultiLineString. Previously `location`\n * - lineDistance - distance from the start of the relevant LineString\n * - segmentDistance - distance from the start of the relevant segment\n * - pointDistance - distance between found point is from input reference point. Previously `dist`\n *\n * multiFeatureIndex, index, location, and dist continue to work as previously\n * until at least the next major release.\n *\n * @function\n * @param {Geometry|Feature<LineString|MultiLineString>} lines Lines to snap to\n * @param {Geometry|Feature<Point>|number[]} inputPoint Point to snap from\n * @param {Object} [options={}] Optional parameters\n * @param {Units} [options.units='kilometers'] Supports all valid Turf {@link https://turfjs.org/docs/api/types/Units Units}\n * @returns {Feature<Point>} closest point on the `lines` to the `inputPoint`. The point will have the following properties: `lineStringIndex`: closest point was found on the nth LineString (only relevant if input is MultiLineString), `segmentIndex`: closest point was found on nth line segment of the LineString, `totalDistance`: distance along the line from the absolute start of the MultiLineString, `lineDistance`: distance along the line from the start of the LineString where the closest point was found, `segmentDistance`: distance along the line from the start of the line segment where the closest point was found, `pointDistance`: distance to the input point.\n * @example\n * var line = turf.lineString([\n * [-77.031669, 38.878605],\n * [-77.029609, 38.881946],\n * [-77.020339, 38.884084],\n * [-77.025661, 38.885821],\n * [-77.021884, 38.889563],\n * [-77.019824, 38.892368]\n * ]);\n * var inputPoint = turf.point([-77.037076, 38.884017]);\n *\n * var snapped = turf.nearestPointOnLine(line, inputPoint, {units: 'miles'});\n *\n * //addToMap\n * var addToMap = [line, inputPoint, snapped];\n * snapped.properties['marker-color'] = '#00f';\n */\nfunction nearestPointOnLine<G extends LineString | MultiLineString>(\n lines: Feature<G> | G,\n inputPoint: Coord,\n options: { units?: Units } = {}\n): Feature<\n Point,\n {\n lineStringIndex: number;\n segmentIndex: number;\n totalDistance: number;\n lineDistance: number;\n segmentDistance: number;\n pointDistance: number;\n multiFeatureIndex: number;\n index: number;\n location: number;\n dist: number;\n [key: string]: any;\n }\n> {\n if (!lines || !inputPoint) {\n throw new Error(\"lines and inputPoint are required arguments\");\n }\n\n const inputPos = getCoord(inputPoint);\n\n let closestPt = point([Infinity, Infinity], {\n lineStringIndex: -1,\n segmentIndex: -1,\n totalDistance: -1,\n lineDistance: -1,\n segmentDistance: -1,\n pointDistance: Infinity,\n // deprecated properties START\n multiFeatureIndex: -1,\n index: -1,\n location: -1,\n dist: Infinity,\n // deprecated properties END\n });\n\n let totalDistance = 0.0;\n let lineDistance = 0.0;\n let currentLineStringIndex = -1;\n flattenEach(\n lines,\n function (line: any, _featureIndex: number, lineStringIndex: number) {\n //reset lineDistance at each changed lineStringIndex\n if (currentLineStringIndex !== lineStringIndex) {\n currentLineStringIndex = lineStringIndex;\n lineDistance = 0.0;\n }\n\n const coords: any = getCoords(line);\n const maxSegmentIndex = coords.length - 2;\n\n for (let i = 0; i < coords.length - 1; i++) {\n //start - start of current line section\n const start: Feature<Point, { dist: number }> = point(coords[i]);\n const startPos = getCoord(start);\n\n //stop - end of current line section\n const stop: Feature<Point, { dist: number }> = point(coords[i + 1]);\n const stopPos = getCoord(stop);\n\n // segmentLength\n const segmentLength = distance(start, stop, options);\n let intersectPos: Position;\n let wasEnd: boolean;\n\n // Short circuit if snap point is start or end position of the line\n // Test the end position first for consistency in case they are\n // coincident\n if (stopPos[0] === inputPos[0] && stopPos[1] === inputPos[1]) {\n [intersectPos, wasEnd] = [stopPos, true];\n } else if (startPos[0] === inputPos[0] && startPos[1] === inputPos[1]) {\n [intersectPos, wasEnd] = [startPos, false];\n } else {\n // Otherwise, find the nearest point the hard way.\n [intersectPos, wasEnd] = nearestPointOnSegment(\n startPos,\n stopPos,\n inputPos\n );\n }\n\n const pointDistance = distance(inputPoint, intersectPos, options);\n\n if (pointDistance < closestPt.properties.pointDistance) {\n const segmentDistance = distance(start, intersectPos, options);\n closestPt = point(intersectPos, {\n lineStringIndex: lineStringIndex,\n // Legacy behaviour where index progresses to next segment if we\n // went with the end point this iteration. Though make sure we\n // only progress to the beginning of the next segment if one\n // actually exists.\n segmentIndex: wasEnd && i + 1 <= maxSegmentIndex ? i + 1 : i,\n totalDistance: totalDistance + segmentDistance,\n lineDistance: lineDistance + segmentDistance,\n segmentDistance: segmentDistance,\n pointDistance: pointDistance,\n // deprecated properties START\n multiFeatureIndex: -1,\n index: -1,\n location: -1,\n dist: Infinity,\n // deprecated properties END\n });\n closestPt.properties = {\n ...closestPt.properties,\n multiFeatureIndex: closestPt.properties.lineStringIndex,\n index: closestPt.properties.segmentIndex,\n location: closestPt.properties.totalDistance,\n dist: closestPt.properties.pointDistance,\n // deprecated properties END\n };\n }\n\n // update totalDistance and lineDistance\n totalDistance += segmentLength;\n lineDistance += segmentLength;\n }\n }\n );\n\n return closestPt;\n}\n\n// A simple Vector3 type for cartesian operations.\ntype Vector = [number, number, number];\n\nfunction dot(v1: Vector, v2: Vector): number {\n const [v1x, v1y, v1z] = v1;\n const [v2x, v2y, v2z] = v2;\n return v1x * v2x + v1y * v2y + v1z * v2z;\n}\n\n// https://en.wikipedia.org/wiki/Cross_product\nfunction cross(v1: Vector, v2: Vector): Vector {\n const [v1x, v1y, v1z] = v1;\n const [v2x, v2y, v2z] = v2;\n return [v1y * v2z - v1z * v2y, v1z * v2x - v1x * v2z, v1x * v2y - v1y * v2x];\n}\n\nfunction magnitude(v: Vector): number {\n return Math.sqrt(Math.pow(v[0], 2) + Math.pow(v[1], 2) + Math.pow(v[2], 2));\n}\n\nfunction normalize(v: Vector): Vector {\n const mag = magnitude(v);\n return [v[0] / mag, v[1] / mag, v[2] / mag];\n}\n\nfunction lngLatToVector(a: Position): Vector {\n const lat = degreesToRadians(a[1]);\n const lng = degreesToRadians(a[0]);\n return [\n Math.cos(lat) * Math.cos(lng),\n Math.cos(lat) * Math.sin(lng),\n Math.sin(lat),\n ];\n}\n\nfunction vectorToLngLat(v: Vector): Position {\n const [x, y, z] = v;\n // Clamp the z-value to ensure that is inside the [-1, 1] domain as required\n // by asin. Note therefore that this function should only be applied to unit\n // vectors so z > 1 should not exist\n const zClamp = Math.min(Math.max(z, -1), 1);\n const lat = radiansToDegrees(Math.asin(zClamp));\n const lng = radiansToDegrees(Math.atan2(y, x));\n\n return [lng, lat];\n}\n\nfunction nearestPointOnSegment(\n posA: Position, // start point of segment to measure to\n posB: Position, // end point of segment to measure to\n posC: Position // point to measure from\n): [Position, boolean] {\n // Based heavily on this article on finding cross track distance to an arc:\n // https://gis.stackexchange.com/questions/209540/projecting-cross-track-distance-on-great-circle\n\n // Convert spherical (lng, lat) to cartesian vector coords (x, y, z)\n // In the below https://tikz.net/spherical_1/ we convert lng (𝜙) and lat (𝜃)\n // into vectors with x, y, and z components with a length (r) of 1.\n const A = lngLatToVector(posA); // the vector from 0,0,0 to posA\n const B = lngLatToVector(posB); // ... to posB\n const C = lngLatToVector(posC); // ... to posC\n\n // The axis (normal vector) of the great circle plane containing the line segment\n const segmentAxis = cross(A, B);\n\n // Two degenerate cases exist for the segment axis cross product. The first is\n // when vectors are aligned (within the bounds of floating point tolerance).\n // The second is where vectors are antipodal (again within the bounds of\n // tolerance. Both cases produce a [0, 0, 0] cross product which invalidates\n // the rest of the algorithm, but each case must be handled separately:\n // - The aligned case indicates coincidence of A and B. therefore this can be\n // an early return assuming the closest point is the end (for consistency).\n // - The antipodal case is truly degenerate - an infinte number of great\n // circles are possible and one will always pass through C. However, given\n // that this case is both highly unlikely to occur in practice and that is\n // will usually be logically sound to return that the point is on the\n // segment, we choose to return the provided point.\n if (segmentAxis[0] === 0 && segmentAxis[1] === 0 && segmentAxis[2] === 0) {\n if (dot(A, B) > 0) {\n return [[...posB], true];\n } else {\n return [[...posC], false];\n }\n }\n\n // The axis of the great circle passing through the segment's axis and the\n // target point\n const targetAxis = cross(segmentAxis, C);\n\n // This cross product also has a degenerate case where the segment axis is\n // coincident with or antipodal to the target point. In this case the point\n // is equidistant to the entire segment. For consistency, we early return the\n // endpoint as the matching point.\n if (targetAxis[0] === 0 && targetAxis[1] === 0 && targetAxis[2] === 0) {\n return [[...posB], true];\n }\n\n // The line of intersection between the two great circle planes\n const intersectionAxis = cross(targetAxis, segmentAxis);\n\n // Vectors to the two points these great circles intersect are the normalized\n // intersection and its antipodes\n const I1 = normalize(intersectionAxis);\n const I2: Vector = [-I1[0], -I1[1], -I1[2]];\n\n // Figure out which is the closest intersection to this segment of the great circle\n // Note that for points on a unit sphere, the dot product represents the\n // cosine of the angle between the two vectors which monotonically increases\n // the closer the two points are together and therefore determines proximity\n const I = dot(C, I1) > dot(C, I2) ? I1 : I2;\n\n // I is the closest intersection to the segment, though might not actually be\n // ON the segment. To test whether the closest intersection lies on the arc or\n // not, we do a cross product comparison to check rotation around the unit\n // circle defined by the great circle plane.\n const segmentAxisNorm = normalize(segmentAxis);\n const cmpAI = dot(cross(A, I), segmentAxisNorm);\n const cmpIB = dot(cross(I, B), segmentAxisNorm);\n\n // When both comparisons are positive, the rotation from A to I to B is in the\n // same direction, implying that I lies between A and B\n if (cmpAI >= 0 && cmpIB >= 0) {\n return [vectorToLngLat(I), false];\n }\n\n // Finally process the case where the intersection is not on the segment,\n // using the dot product with the original point to find the closest endpoint\n if (dot(A, C) > dot(B, C)) {\n // Clone position when returning as it is reasonable to not expect structural\n // sharing on the returned Position in all return cases\n return [[...posA], false];\n } else {\n return [[...posB], true];\n }\n}\n\nexport { nearestPointOnLine };\nexport default nearestPointOnLine;\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,10 +1,11 @@
1
1
  {
2
2
  "name": "@turf/nearest-point-on-line",
3
- "version": "7.3.1",
3
+ "version": "7.3.3",
4
4
  "description": "Finds the nearest point on a line to a given point",
5
5
  "author": "Turf Authors",
6
6
  "contributors": [
7
7
  "Angel Lacret <@alacret>",
8
+ "Emil Junker <@EmilJunker>",
8
9
  "Jon Miles <@jonmiles>",
9
10
  "John Ziebro <@Insighttful>"
10
11
  ],
@@ -51,9 +52,9 @@
51
52
  "test:types": "tsc --esModuleInterop --module node16 --moduleResolution node16 --noEmit --strict types.ts"
52
53
  },
53
54
  "devDependencies": {
54
- "@turf/along": "7.3.1",
55
- "@turf/length": "7.3.1",
56
- "@turf/truncate": "7.3.1",
55
+ "@turf/along": "7.3.3",
56
+ "@turf/length": "7.3.3",
57
+ "@turf/truncate": "7.3.3",
57
58
  "@types/benchmark": "^2.1.5",
58
59
  "@types/tape": "^5.8.1",
59
60
  "benchmark": "^2.1.4",
@@ -65,12 +66,12 @@
65
66
  "write-json-file": "^6.0.0"
66
67
  },
67
68
  "dependencies": {
68
- "@turf/distance": "7.3.1",
69
- "@turf/helpers": "7.3.1",
70
- "@turf/invariant": "7.3.1",
71
- "@turf/meta": "7.3.1",
69
+ "@turf/distance": "7.3.3",
70
+ "@turf/helpers": "7.3.3",
71
+ "@turf/invariant": "7.3.3",
72
+ "@turf/meta": "7.3.3",
72
73
  "@types/geojson": "^7946.0.10",
73
74
  "tslib": "^2.8.1"
74
75
  },
75
- "gitHead": "b7f1b4eafb760431e03955499d8eac9489438219"
76
+ "gitHead": "fa0e2da8ce02d9a82720eae922f89c9386596e04"
76
77
  }