@turf/nearest-point-on-line 7.1.0-alpha.70 → 7.2.0
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 +9 -9
- package/dist/cjs/index.cjs +97 -53
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs/index.d.cts +2 -2
- package/dist/esm/index.d.ts +2 -2
- package/dist/esm/index.js +99 -55
- package/dist/esm/index.js.map +1 -1
- package/package.json +15 -18
package/README.md
CHANGED
|
@@ -4,12 +4,12 @@
|
|
|
4
4
|
|
|
5
5
|
## nearestPointOnLine
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
Returns the nearest point on a line to a given point.
|
|
8
8
|
|
|
9
9
|
### Parameters
|
|
10
10
|
|
|
11
|
-
* `lines` **([Geometry][
|
|
12
|
-
* `pt` **([Geometry][
|
|
11
|
+
* `lines` **([Geometry][1] | [Feature][2]<([LineString][3] | [MultiLineString][4])>)** lines to snap to
|
|
12
|
+
* `pt` **([Geometry][1] | [Feature][2]<[Point][5]> | [Array][6]<[number][7]>)** point to snap from
|
|
13
13
|
* `options` **[Object][8]** Optional parameters (optional, default `{}`)
|
|
14
14
|
|
|
15
15
|
* `options.units` **[string][9]** can be degrees, radians, miles, or kilometers (optional, default `'kilometers'`)
|
|
@@ -34,17 +34,17 @@ var addToMap = [line, pt, snapped];
|
|
|
34
34
|
snapped.properties['marker-color'] = '#00f';
|
|
35
35
|
```
|
|
36
36
|
|
|
37
|
-
Returns **[Feature][
|
|
37
|
+
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.
|
|
38
38
|
|
|
39
|
-
[1]: https://tools.ietf.org/html/rfc7946#section-3.1
|
|
39
|
+
[1]: https://tools.ietf.org/html/rfc7946#section-3.1
|
|
40
40
|
|
|
41
|
-
[2]: https://tools.ietf.org/html/rfc7946#section-3.
|
|
41
|
+
[2]: https://tools.ietf.org/html/rfc7946#section-3.2
|
|
42
42
|
|
|
43
|
-
[3]: https://tools.ietf.org/html/rfc7946#section-3.1
|
|
43
|
+
[3]: https://tools.ietf.org/html/rfc7946#section-3.1.4
|
|
44
44
|
|
|
45
|
-
[4]: https://tools.ietf.org/html/rfc7946#section-3.
|
|
45
|
+
[4]: https://tools.ietf.org/html/rfc7946#section-3.1.5
|
|
46
46
|
|
|
47
|
-
[5]: https://tools.ietf.org/html/rfc7946#section-3.1.
|
|
47
|
+
[5]: https://tools.ietf.org/html/rfc7946#section-3.1.2
|
|
48
48
|
|
|
49
49
|
[6]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array
|
|
50
50
|
|
package/dist/cjs/index.cjs
CHANGED
|
@@ -19,17 +19,19 @@ var __spreadValues = (a, b) => {
|
|
|
19
19
|
var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
|
|
20
20
|
|
|
21
21
|
// index.ts
|
|
22
|
-
var _bearing = require('@turf/bearing');
|
|
23
22
|
var _distance = require('@turf/distance');
|
|
24
|
-
var _destination = require('@turf/destination');
|
|
25
|
-
var _lineintersect = require('@turf/line-intersect');
|
|
26
23
|
var _meta = require('@turf/meta');
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
|
|
27
28
|
var _helpers = require('@turf/helpers');
|
|
28
29
|
var _invariant = require('@turf/invariant');
|
|
29
30
|
function nearestPointOnLine(lines, pt, options = {}) {
|
|
30
31
|
if (!lines || !pt) {
|
|
31
32
|
throw new Error("lines and pt are required arguments");
|
|
32
33
|
}
|
|
34
|
+
const ptPos = _invariant.getCoord.call(void 0, pt);
|
|
33
35
|
let closestPt = _helpers.point.call(void 0, [Infinity, Infinity], {
|
|
34
36
|
dist: Infinity,
|
|
35
37
|
index: -1,
|
|
@@ -44,64 +46,39 @@ function nearestPointOnLine(lines, pt, options = {}) {
|
|
|
44
46
|
for (let i = 0; i < coords.length - 1; i++) {
|
|
45
47
|
const start = _helpers.point.call(void 0, coords[i]);
|
|
46
48
|
start.properties.dist = _distance.distance.call(void 0, pt, start, options);
|
|
49
|
+
const startPos = _invariant.getCoord.call(void 0, start);
|
|
47
50
|
const stop = _helpers.point.call(void 0, coords[i + 1]);
|
|
48
51
|
stop.properties.dist = _distance.distance.call(void 0, pt, stop, options);
|
|
52
|
+
const stopPos = _invariant.getCoord.call(void 0, stop);
|
|
49
53
|
const sectionLength = _distance.distance.call(void 0, start, stop, options);
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
pt,
|
|
63
|
-
heightDistance,
|
|
64
|
-
direction - 90,
|
|
65
|
-
options
|
|
66
|
-
);
|
|
67
|
-
const intersect = _lineintersect.lineIntersect.call(void 0,
|
|
68
|
-
_helpers.lineString.call(void 0, [
|
|
69
|
-
perpendicularPt1.geometry.coordinates,
|
|
70
|
-
perpendicularPt2.geometry.coordinates
|
|
71
|
-
]),
|
|
72
|
-
_helpers.lineString.call(void 0, [start.geometry.coordinates, stop.geometry.coordinates])
|
|
73
|
-
);
|
|
74
|
-
let intersectPt;
|
|
75
|
-
if (intersect.features.length > 0 && intersect.features[0]) {
|
|
76
|
-
intersectPt = __spreadProps(__spreadValues({}, intersect.features[0]), {
|
|
77
|
-
properties: {
|
|
78
|
-
dist: _distance.distance.call(void 0, pt, intersect.features[0], options),
|
|
79
|
-
multiFeatureIndex,
|
|
80
|
-
location: length + _distance.distance.call(void 0, start, intersect.features[0], options)
|
|
81
|
-
}
|
|
82
|
-
});
|
|
54
|
+
let intersectPos;
|
|
55
|
+
let wasEnd;
|
|
56
|
+
if (startPos[0] === ptPos[0] && startPos[1] === ptPos[1]) {
|
|
57
|
+
[intersectPos, , wasEnd] = [startPos, void 0, false];
|
|
58
|
+
} else if (stopPos[0] === ptPos[0] && stopPos[1] === ptPos[1]) {
|
|
59
|
+
[intersectPos, , wasEnd] = [stopPos, void 0, true];
|
|
60
|
+
} else {
|
|
61
|
+
[intersectPos, , wasEnd] = nearestPointOnSegment(
|
|
62
|
+
start.geometry.coordinates,
|
|
63
|
+
stop.geometry.coordinates,
|
|
64
|
+
_invariant.getCoord.call(void 0, pt)
|
|
65
|
+
);
|
|
83
66
|
}
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
})
|
|
91
|
-
});
|
|
92
|
-
}
|
|
93
|
-
if (stop.properties.dist < closestPt.properties.dist) {
|
|
94
|
-
closestPt = __spreadProps(__spreadValues({}, stop), {
|
|
95
|
-
properties: __spreadProps(__spreadValues({}, stop.properties), {
|
|
96
|
-
index: i + 1,
|
|
97
|
-
multiFeatureIndex,
|
|
98
|
-
location: length + sectionLength
|
|
99
|
-
})
|
|
67
|
+
let intersectPt;
|
|
68
|
+
if (intersectPos) {
|
|
69
|
+
intersectPt = _helpers.point.call(void 0, intersectPos, {
|
|
70
|
+
dist: _distance.distance.call(void 0, pt, intersectPos, options),
|
|
71
|
+
multiFeatureIndex,
|
|
72
|
+
location: length + _distance.distance.call(void 0, start, intersectPos, options)
|
|
100
73
|
});
|
|
101
74
|
}
|
|
102
75
|
if (intersectPt && intersectPt.properties.dist < closestPt.properties.dist) {
|
|
103
76
|
closestPt = __spreadProps(__spreadValues({}, intersectPt), {
|
|
104
|
-
properties: __spreadProps(__spreadValues({}, intersectPt.properties), {
|
|
77
|
+
properties: __spreadProps(__spreadValues({}, intersectPt.properties), {
|
|
78
|
+
// Legacy behaviour where index progresses to next segment # if we
|
|
79
|
+
// went with the end point this iteration.
|
|
80
|
+
index: wasEnd ? i + 1 : i
|
|
81
|
+
})
|
|
105
82
|
});
|
|
106
83
|
}
|
|
107
84
|
length += sectionLength;
|
|
@@ -110,6 +87,73 @@ function nearestPointOnLine(lines, pt, options = {}) {
|
|
|
110
87
|
);
|
|
111
88
|
return closestPt;
|
|
112
89
|
}
|
|
90
|
+
function dot(v1, v2) {
|
|
91
|
+
const [v1x, v1y, v1z] = v1;
|
|
92
|
+
const [v2x, v2y, v2z] = v2;
|
|
93
|
+
return v1x * v2x + v1y * v2y + v1z * v2z;
|
|
94
|
+
}
|
|
95
|
+
function cross(v1, v2) {
|
|
96
|
+
const [v1x, v1y, v1z] = v1;
|
|
97
|
+
const [v2x, v2y, v2z] = v2;
|
|
98
|
+
return [v1y * v2z - v1z * v2y, v1z * v2x - v1x * v2z, v1x * v2y - v1y * v2x];
|
|
99
|
+
}
|
|
100
|
+
function magnitude(v) {
|
|
101
|
+
return Math.sqrt(Math.pow(v[0], 2) + Math.pow(v[1], 2) + Math.pow(v[2], 2));
|
|
102
|
+
}
|
|
103
|
+
function angle(v1, v2) {
|
|
104
|
+
const theta = dot(v1, v2) / (magnitude(v1) * magnitude(v2));
|
|
105
|
+
return Math.acos(Math.min(Math.max(theta, -1), 1));
|
|
106
|
+
}
|
|
107
|
+
function lngLatToVector(a) {
|
|
108
|
+
const lat = _helpers.degreesToRadians.call(void 0, a[1]);
|
|
109
|
+
const lng = _helpers.degreesToRadians.call(void 0, a[0]);
|
|
110
|
+
return [
|
|
111
|
+
Math.cos(lat) * Math.cos(lng),
|
|
112
|
+
Math.cos(lat) * Math.sin(lng),
|
|
113
|
+
Math.sin(lat)
|
|
114
|
+
];
|
|
115
|
+
}
|
|
116
|
+
function vectorToLngLat(v) {
|
|
117
|
+
const [x, y, z] = v;
|
|
118
|
+
const lat = _helpers.radiansToDegrees.call(void 0, Math.asin(z));
|
|
119
|
+
const lng = _helpers.radiansToDegrees.call(void 0, Math.atan2(y, x));
|
|
120
|
+
return [lng, lat];
|
|
121
|
+
}
|
|
122
|
+
function nearestPointOnSegment(posA, posB, posC) {
|
|
123
|
+
const A = lngLatToVector(posA);
|
|
124
|
+
const B = lngLatToVector(posB);
|
|
125
|
+
const C = lngLatToVector(posC);
|
|
126
|
+
const [Cx, Cy, Cz] = C;
|
|
127
|
+
const [D, E, F] = cross(A, B);
|
|
128
|
+
const a = E * Cz - F * Cy;
|
|
129
|
+
const b = F * Cx - D * Cz;
|
|
130
|
+
const c = D * Cy - E * Cx;
|
|
131
|
+
const f = c * E - b * F;
|
|
132
|
+
const g = a * F - c * D;
|
|
133
|
+
const h = b * D - a * E;
|
|
134
|
+
const t = 1 / Math.sqrt(Math.pow(f, 2) + Math.pow(g, 2) + Math.pow(h, 2));
|
|
135
|
+
const I1 = [f * t, g * t, h * t];
|
|
136
|
+
const I2 = [-1 * f * t, -1 * g * t, -1 * h * t];
|
|
137
|
+
const angleAB = angle(A, B);
|
|
138
|
+
const angleAI1 = angle(A, I1);
|
|
139
|
+
const angleBI1 = angle(B, I1);
|
|
140
|
+
const angleAI2 = angle(A, I2);
|
|
141
|
+
const angleBI2 = angle(B, I2);
|
|
142
|
+
let I;
|
|
143
|
+
if (angleAI1 < angleAI2 && angleAI1 < angleBI2 || angleBI1 < angleAI2 && angleBI1 < angleBI2) {
|
|
144
|
+
I = I1;
|
|
145
|
+
} else {
|
|
146
|
+
I = I2;
|
|
147
|
+
}
|
|
148
|
+
if (angle(A, I) > angleAB || angle(B, I) > angleAB) {
|
|
149
|
+
if (_distance.distance.call(void 0, vectorToLngLat(I), vectorToLngLat(A)) <= _distance.distance.call(void 0, vectorToLngLat(I), vectorToLngLat(B))) {
|
|
150
|
+
return [vectorToLngLat(A), true, false];
|
|
151
|
+
} else {
|
|
152
|
+
return [vectorToLngLat(B), false, true];
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
return [vectorToLngLat(I), false, false];
|
|
156
|
+
}
|
|
113
157
|
var turf_nearest_point_on_line_default = nearestPointOnLine;
|
|
114
158
|
|
|
115
159
|
|
package/dist/cjs/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;AACA,SAAS,eAAe;AACxB,SAAS,gBAAgB;AACzB,SAAS,mBAAmB;AAC5B,SAAS,iBAAiB,sBAAsB;AAChD,SAAS,mBAAmB;AAC5B,SAAS,OAAO,kBAAgC;AAChD,SAAS,iBAAiB;AA4B1B,SAAS,mBACP,OACA,IACA,UAA6B,CAAC,GAU9B;AACA,MAAI,CAAC,SAAS,CAAC,IAAI;AACjB,UAAM,IAAI,MAAM,qCAAqC;AAAA,EACvD;AAEA,MAAI,YAGA,MAAM,CAAC,UAAU,QAAQ,GAAG;AAAA,IAC9B,MAAM;AAAA,IACN,OAAO;AAAA,IACP,mBAAmB;AAAA,IACnB,UAAU;AAAA,EACZ,CAAC;AAED,MAAI,SAAS;AACb;AAAA,IACE;AAAA,IACA,SAAU,MAAW,eAAuB,mBAA2B;AACrE,YAAM,SAAc,UAAU,IAAI;AAElC,eAAS,IAAI,GAAG,IAAI,OAAO,SAAS,GAAG,KAAK;AAE1C,cAAM,QAA0C,MAAM,OAAO,CAAC,CAAC;AAC/D,cAAM,WAAW,OAAO,SAAS,IAAI,OAAO,OAAO;AAEnD,cAAM,OAAyC,MAAM,OAAO,IAAI,CAAC,CAAC;AAClE,aAAK,WAAW,OAAO,SAAS,IAAI,MAAM,OAAO;AAEjD,cAAM,gBAAgB,SAAS,OAAO,MAAM,OAAO;AAEnD,cAAM,iBAAiB,KAAK;AAAA,UAC1B,MAAM,WAAW;AAAA,UACjB,KAAK,WAAW;AAAA,QAClB;AACA,cAAM,YAAY,QAAQ,OAAO,IAAI;AACrC,cAAM,mBAAmB;AAAA,UACvB;AAAA,UACA;AAAA,UACA,YAAY;AAAA,UACZ;AAAA,QACF;AACA,cAAM,mBAAmB;AAAA,UACvB;AAAA,UACA;AAAA,UACA,YAAY;AAAA,UACZ;AAAA,QACF;AACA,cAAM,YAAY;AAAA,UAChB,WAAW;AAAA,YACT,iBAAiB,SAAS;AAAA,YAC1B,iBAAiB,SAAS;AAAA,UAC5B,CAAC;AAAA,UACD,WAAW,CAAC,MAAM,SAAS,aAAa,KAAK,SAAS,WAAW,CAAC;AAAA,QACpE;AACA,YAAI;AAOJ,YAAI,UAAU,SAAS,SAAS,KAAK,UAAU,SAAS,CAAC,GAAG;AAC1D,wBAAc,iCACT,UAAU,SAAS,CAAC,IADX;AAAA,YAEZ,YAAY;AAAA,cACV,MAAM,SAAS,IAAI,UAAU,SAAS,CAAC,GAAG,OAAO;AAAA,cACjD;AAAA,cACA,UACE,SAAS,SAAS,OAAO,UAAU,SAAS,CAAC,GAAG,OAAO;AAAA,YAC3D;AAAA,UACF;AAAA,QACF;AAEA,YAAI,MAAM,WAAW,OAAO,UAAU,WAAW,MAAM;AACrD,sBAAY,iCACP,QADO;AAAA,YAEV,YAAY,iCACP,MAAM,aADC;AAAA,cAEV,OAAO;AAAA,cACP;AAAA,cACA,UAAU;AAAA,YACZ;AAAA,UACF;AAAA,QACF;AAEA,YAAI,KAAK,WAAW,OAAO,UAAU,WAAW,MAAM;AACpD,sBAAY,iCACP,OADO;AAAA,YAEV,YAAY,iCACP,KAAK,aADE;AAAA,cAEV,OAAO,IAAI;AAAA,cACX;AAAA,cACA,UAAU,SAAS;AAAA,YACrB;AAAA,UACF;AAAA,QACF;AAEA,YACE,eACA,YAAY,WAAW,OAAO,UAAU,WAAW,MACnD;AACA,sBAAY,iCACP,cADO;AAAA,YAEV,YAAY,iCAAK,YAAY,aAAjB,EAA6B,OAAO,EAAE;AAAA,UACpD;AAAA,QACF;AAEA,kBAAU;AAAA,MACZ;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;AAGA,IAAO,qCAAQ","sourcesContent":["import { Feature, Point, LineString, MultiLineString } from \"geojson\";\nimport { bearing } from \"@turf/bearing\";\nimport { distance } from \"@turf/distance\";\nimport { destination } from \"@turf/destination\";\nimport { lineIntersect as lineIntersects } from \"@turf/line-intersect\";\nimport { flattenEach } from \"@turf/meta\";\nimport { point, lineString, Coord, Units } from \"@turf/helpers\";\nimport { getCoords } from \"@turf/invariant\";\n\n/**\n * Takes a {@link Point} and a {@link LineString} and calculates the closest Point on the (Multi)LineString.\n *\n * @name nearestPointOnLine\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 {string} [options.units='kilometers'] can be degrees, radians, miles, or kilometers\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 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\n const start: Feature<Point, { dist: number }> = point(coords[i]);\n start.properties.dist = distance(pt, start, options);\n //stop\n const stop: Feature<Point, { dist: number }> = point(coords[i + 1]);\n stop.properties.dist = distance(pt, stop, options);\n // sectionLength\n const sectionLength = distance(start, stop, options);\n //perpendicular\n const heightDistance = Math.max(\n start.properties.dist,\n stop.properties.dist\n );\n const direction = bearing(start, stop);\n const perpendicularPt1 = destination(\n pt,\n heightDistance,\n direction + 90,\n options\n );\n const perpendicularPt2 = destination(\n pt,\n heightDistance,\n direction - 90,\n options\n );\n const intersect = lineIntersects(\n lineString([\n perpendicularPt1.geometry.coordinates,\n perpendicularPt2.geometry.coordinates,\n ]),\n lineString([start.geometry.coordinates, stop.geometry.coordinates])\n );\n let intersectPt:\n | Feature<\n Point,\n { dist: number; multiFeatureIndex: number; location: number }\n >\n | undefined;\n\n if (intersect.features.length > 0 && intersect.features[0]) {\n intersectPt = {\n ...intersect.features[0],\n properties: {\n dist: distance(pt, intersect.features[0], options),\n multiFeatureIndex: multiFeatureIndex,\n location:\n length + distance(start, intersect.features[0], options),\n },\n };\n }\n\n if (start.properties.dist < closestPt.properties.dist) {\n closestPt = {\n ...start,\n properties: {\n ...start.properties,\n index: i,\n multiFeatureIndex: multiFeatureIndex,\n location: length,\n },\n };\n }\n\n if (stop.properties.dist < closestPt.properties.dist) {\n closestPt = {\n ...stop,\n properties: {\n ...stop.properties,\n index: i + 1,\n multiFeatureIndex: multiFeatureIndex,\n location: length + sectionLength,\n },\n };\n }\n\n if (\n intersectPt &&\n intersectPt.properties.dist < closestPt.properties.dist\n ) {\n closestPt = {\n ...intersectPt,\n properties: { ...intersectPt.properties, index: i },\n };\n }\n // update length\n length += sectionLength;\n }\n }\n );\n\n return closestPt;\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 * @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 {string} [options.units='kilometers'] can be degrees, radians, miles, or kilometers\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 start.properties.dist = distance(pt, start, options);\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 stop.properties.dist = distance(pt, stop, options);\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 // segment.\n if (startPos[0] === ptPos[0] && startPos[1] === ptPos[1]) {\n [intersectPos, , wasEnd] = [startPos, undefined, false];\n } else if (stopPos[0] === ptPos[0] && stopPos[1] === ptPos[1]) {\n [intersectPos, , wasEnd] = [stopPos, undefined, true];\n } else {\n // Otherwise, find the nearest point the hard way.\n [intersectPos, , wasEnd] = nearestPointOnSegment(\n start.geometry.coordinates,\n stop.geometry.coordinates,\n getCoord(pt)\n );\n }\n let intersectPt:\n | Feature<\n Point,\n { dist: number; multiFeatureIndex: number; location: number }\n >\n | undefined;\n\n if (intersectPos) {\n intersectPt = point(intersectPos, {\n dist: distance(pt, intersectPos, options),\n multiFeatureIndex: multiFeatureIndex,\n location: length + distance(start, intersectPos, options),\n });\n }\n\n if (\n intersectPt &&\n intersectPt.properties.dist < closestPt.properties.dist\n ) {\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/*\n * Plan is to externalise these vector functions to a simple third party\n * library.\n * Possible candidate is @amandaghassaei/vector-math though having some import\n * issues.\n */\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) {\n return Math.sqrt(Math.pow(v[0], 2) + Math.pow(v[1], 2) + Math.pow(v[2], 2));\n}\n\nfunction angle(v1: Vector, v2: Vector): number {\n const theta = dot(v1, v2) / (magnitude(v1) * magnitude(v2));\n return Math.acos(Math.min(Math.max(theta, -1), 1));\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 const lat = radiansToDegrees(Math.asin(z));\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, 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 // Components of target point.\n const [Cx, Cy, Cz] = C;\n\n // Calculate coefficients.\n const [D, E, F] = cross(A, B);\n const a = E * Cz - F * Cy;\n const b = F * Cx - D * Cz;\n const c = D * Cy - E * Cx;\n\n const f = c * E - b * F;\n const g = a * F - c * D;\n const h = b * D - a * E;\n\n const t = 1 / Math.sqrt(Math.pow(f, 2) + Math.pow(g, 2) + Math.pow(h, 2));\n\n // Vectors to the two points these great circles intersect.\n const I1: Vector = [f * t, g * t, h * t];\n const I2: Vector = [-1 * f * t, -1 * g * t, -1 * h * t];\n\n // Figure out which is the closest intersection to this segment of the great\n // circle.\n const angleAB = angle(A, B);\n const angleAI1 = angle(A, I1);\n const angleBI1 = angle(B, I1);\n const angleAI2 = angle(A, I2);\n const angleBI2 = angle(B, I2);\n\n let I: Vector;\n\n if (\n (angleAI1 < angleAI2 && angleAI1 < angleBI2) ||\n (angleBI1 < angleAI2 && angleBI1 < angleBI2)\n ) {\n I = I1;\n } else {\n I = I2;\n }\n\n // I is the closest intersection to the segment, though might not actually be\n // ON the segment.\n\n // If angle AI or BI is greater than angleAB, I lies on the circle *beyond* A\n // and B so use the closest of A or B as the intersection\n if (angle(A, I) > angleAB || angle(B, I) > angleAB) {\n if (\n distance(vectorToLngLat(I), vectorToLngLat(A)) <=\n distance(vectorToLngLat(I), vectorToLngLat(B))\n ) {\n return [vectorToLngLat(A), true, false];\n } else {\n return [vectorToLngLat(B), false, true];\n }\n }\n\n // As angleAI nor angleBI don't exceed angleAB, I is on the segment\n return [vectorToLngLat(I), false, false];\n}\n\nexport { nearestPointOnLine };\nexport default nearestPointOnLine;\n"]}
|
package/dist/cjs/index.d.cts
CHANGED
|
@@ -2,9 +2,9 @@ import { LineString, MultiLineString, Feature, Point } from 'geojson';
|
|
|
2
2
|
import { Coord, Units } from '@turf/helpers';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
|
-
*
|
|
5
|
+
* Returns the nearest point on a line to a given point.
|
|
6
6
|
*
|
|
7
|
-
* @
|
|
7
|
+
* @function
|
|
8
8
|
* @param {Geometry|Feature<LineString|MultiLineString>} lines lines to snap to
|
|
9
9
|
* @param {Geometry|Feature<Point>|number[]} pt point to snap from
|
|
10
10
|
* @param {Object} [options={}] Optional parameters
|
package/dist/esm/index.d.ts
CHANGED
|
@@ -2,9 +2,9 @@ import { LineString, MultiLineString, Feature, Point } from 'geojson';
|
|
|
2
2
|
import { Coord, Units } from '@turf/helpers';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
|
-
*
|
|
5
|
+
* Returns the nearest point on a line to a given point.
|
|
6
6
|
*
|
|
7
|
-
* @
|
|
7
|
+
* @function
|
|
8
8
|
* @param {Geometry|Feature<LineString|MultiLineString>} lines lines to snap to
|
|
9
9
|
* @param {Geometry|Feature<Point>|number[]} pt point to snap from
|
|
10
10
|
* @param {Object} [options={}] Optional parameters
|
package/dist/esm/index.js
CHANGED
|
@@ -19,17 +19,19 @@ var __spreadValues = (a, b) => {
|
|
|
19
19
|
var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
|
|
20
20
|
|
|
21
21
|
// index.ts
|
|
22
|
-
import { bearing } from "@turf/bearing";
|
|
23
22
|
import { distance } from "@turf/distance";
|
|
24
|
-
import { destination } from "@turf/destination";
|
|
25
|
-
import { lineIntersect as lineIntersects } from "@turf/line-intersect";
|
|
26
23
|
import { flattenEach } from "@turf/meta";
|
|
27
|
-
import {
|
|
28
|
-
|
|
24
|
+
import {
|
|
25
|
+
point,
|
|
26
|
+
degreesToRadians,
|
|
27
|
+
radiansToDegrees
|
|
28
|
+
} from "@turf/helpers";
|
|
29
|
+
import { getCoord, getCoords } from "@turf/invariant";
|
|
29
30
|
function nearestPointOnLine(lines, pt, options = {}) {
|
|
30
31
|
if (!lines || !pt) {
|
|
31
32
|
throw new Error("lines and pt are required arguments");
|
|
32
33
|
}
|
|
34
|
+
const ptPos = getCoord(pt);
|
|
33
35
|
let closestPt = point([Infinity, Infinity], {
|
|
34
36
|
dist: Infinity,
|
|
35
37
|
index: -1,
|
|
@@ -44,64 +46,39 @@ function nearestPointOnLine(lines, pt, options = {}) {
|
|
|
44
46
|
for (let i = 0; i < coords.length - 1; i++) {
|
|
45
47
|
const start = point(coords[i]);
|
|
46
48
|
start.properties.dist = distance(pt, start, options);
|
|
49
|
+
const startPos = getCoord(start);
|
|
47
50
|
const stop = point(coords[i + 1]);
|
|
48
51
|
stop.properties.dist = distance(pt, stop, options);
|
|
52
|
+
const stopPos = getCoord(stop);
|
|
49
53
|
const sectionLength = distance(start, stop, options);
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
pt,
|
|
63
|
-
heightDistance,
|
|
64
|
-
direction - 90,
|
|
65
|
-
options
|
|
66
|
-
);
|
|
67
|
-
const intersect = lineIntersects(
|
|
68
|
-
lineString([
|
|
69
|
-
perpendicularPt1.geometry.coordinates,
|
|
70
|
-
perpendicularPt2.geometry.coordinates
|
|
71
|
-
]),
|
|
72
|
-
lineString([start.geometry.coordinates, stop.geometry.coordinates])
|
|
73
|
-
);
|
|
74
|
-
let intersectPt;
|
|
75
|
-
if (intersect.features.length > 0 && intersect.features[0]) {
|
|
76
|
-
intersectPt = __spreadProps(__spreadValues({}, intersect.features[0]), {
|
|
77
|
-
properties: {
|
|
78
|
-
dist: distance(pt, intersect.features[0], options),
|
|
79
|
-
multiFeatureIndex,
|
|
80
|
-
location: length + distance(start, intersect.features[0], options)
|
|
81
|
-
}
|
|
82
|
-
});
|
|
83
|
-
}
|
|
84
|
-
if (start.properties.dist < closestPt.properties.dist) {
|
|
85
|
-
closestPt = __spreadProps(__spreadValues({}, start), {
|
|
86
|
-
properties: __spreadProps(__spreadValues({}, start.properties), {
|
|
87
|
-
index: i,
|
|
88
|
-
multiFeatureIndex,
|
|
89
|
-
location: length
|
|
90
|
-
})
|
|
91
|
-
});
|
|
54
|
+
let intersectPos;
|
|
55
|
+
let wasEnd;
|
|
56
|
+
if (startPos[0] === ptPos[0] && startPos[1] === ptPos[1]) {
|
|
57
|
+
[intersectPos, , wasEnd] = [startPos, void 0, false];
|
|
58
|
+
} else if (stopPos[0] === ptPos[0] && stopPos[1] === ptPos[1]) {
|
|
59
|
+
[intersectPos, , wasEnd] = [stopPos, void 0, true];
|
|
60
|
+
} else {
|
|
61
|
+
[intersectPos, , wasEnd] = nearestPointOnSegment(
|
|
62
|
+
start.geometry.coordinates,
|
|
63
|
+
stop.geometry.coordinates,
|
|
64
|
+
getCoord(pt)
|
|
65
|
+
);
|
|
92
66
|
}
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
})
|
|
67
|
+
let intersectPt;
|
|
68
|
+
if (intersectPos) {
|
|
69
|
+
intersectPt = point(intersectPos, {
|
|
70
|
+
dist: distance(pt, intersectPos, options),
|
|
71
|
+
multiFeatureIndex,
|
|
72
|
+
location: length + distance(start, intersectPos, options)
|
|
100
73
|
});
|
|
101
74
|
}
|
|
102
75
|
if (intersectPt && intersectPt.properties.dist < closestPt.properties.dist) {
|
|
103
76
|
closestPt = __spreadProps(__spreadValues({}, intersectPt), {
|
|
104
|
-
properties: __spreadProps(__spreadValues({}, intersectPt.properties), {
|
|
77
|
+
properties: __spreadProps(__spreadValues({}, intersectPt.properties), {
|
|
78
|
+
// Legacy behaviour where index progresses to next segment # if we
|
|
79
|
+
// went with the end point this iteration.
|
|
80
|
+
index: wasEnd ? i + 1 : i
|
|
81
|
+
})
|
|
105
82
|
});
|
|
106
83
|
}
|
|
107
84
|
length += sectionLength;
|
|
@@ -110,6 +87,73 @@ function nearestPointOnLine(lines, pt, options = {}) {
|
|
|
110
87
|
);
|
|
111
88
|
return closestPt;
|
|
112
89
|
}
|
|
90
|
+
function dot(v1, v2) {
|
|
91
|
+
const [v1x, v1y, v1z] = v1;
|
|
92
|
+
const [v2x, v2y, v2z] = v2;
|
|
93
|
+
return v1x * v2x + v1y * v2y + v1z * v2z;
|
|
94
|
+
}
|
|
95
|
+
function cross(v1, v2) {
|
|
96
|
+
const [v1x, v1y, v1z] = v1;
|
|
97
|
+
const [v2x, v2y, v2z] = v2;
|
|
98
|
+
return [v1y * v2z - v1z * v2y, v1z * v2x - v1x * v2z, v1x * v2y - v1y * v2x];
|
|
99
|
+
}
|
|
100
|
+
function magnitude(v) {
|
|
101
|
+
return Math.sqrt(Math.pow(v[0], 2) + Math.pow(v[1], 2) + Math.pow(v[2], 2));
|
|
102
|
+
}
|
|
103
|
+
function angle(v1, v2) {
|
|
104
|
+
const theta = dot(v1, v2) / (magnitude(v1) * magnitude(v2));
|
|
105
|
+
return Math.acos(Math.min(Math.max(theta, -1), 1));
|
|
106
|
+
}
|
|
107
|
+
function lngLatToVector(a) {
|
|
108
|
+
const lat = degreesToRadians(a[1]);
|
|
109
|
+
const lng = degreesToRadians(a[0]);
|
|
110
|
+
return [
|
|
111
|
+
Math.cos(lat) * Math.cos(lng),
|
|
112
|
+
Math.cos(lat) * Math.sin(lng),
|
|
113
|
+
Math.sin(lat)
|
|
114
|
+
];
|
|
115
|
+
}
|
|
116
|
+
function vectorToLngLat(v) {
|
|
117
|
+
const [x, y, z] = v;
|
|
118
|
+
const lat = radiansToDegrees(Math.asin(z));
|
|
119
|
+
const lng = radiansToDegrees(Math.atan2(y, x));
|
|
120
|
+
return [lng, lat];
|
|
121
|
+
}
|
|
122
|
+
function nearestPointOnSegment(posA, posB, posC) {
|
|
123
|
+
const A = lngLatToVector(posA);
|
|
124
|
+
const B = lngLatToVector(posB);
|
|
125
|
+
const C = lngLatToVector(posC);
|
|
126
|
+
const [Cx, Cy, Cz] = C;
|
|
127
|
+
const [D, E, F] = cross(A, B);
|
|
128
|
+
const a = E * Cz - F * Cy;
|
|
129
|
+
const b = F * Cx - D * Cz;
|
|
130
|
+
const c = D * Cy - E * Cx;
|
|
131
|
+
const f = c * E - b * F;
|
|
132
|
+
const g = a * F - c * D;
|
|
133
|
+
const h = b * D - a * E;
|
|
134
|
+
const t = 1 / Math.sqrt(Math.pow(f, 2) + Math.pow(g, 2) + Math.pow(h, 2));
|
|
135
|
+
const I1 = [f * t, g * t, h * t];
|
|
136
|
+
const I2 = [-1 * f * t, -1 * g * t, -1 * h * t];
|
|
137
|
+
const angleAB = angle(A, B);
|
|
138
|
+
const angleAI1 = angle(A, I1);
|
|
139
|
+
const angleBI1 = angle(B, I1);
|
|
140
|
+
const angleAI2 = angle(A, I2);
|
|
141
|
+
const angleBI2 = angle(B, I2);
|
|
142
|
+
let I;
|
|
143
|
+
if (angleAI1 < angleAI2 && angleAI1 < angleBI2 || angleBI1 < angleAI2 && angleBI1 < angleBI2) {
|
|
144
|
+
I = I1;
|
|
145
|
+
} else {
|
|
146
|
+
I = I2;
|
|
147
|
+
}
|
|
148
|
+
if (angle(A, I) > angleAB || angle(B, I) > angleAB) {
|
|
149
|
+
if (distance(vectorToLngLat(I), vectorToLngLat(A)) <= distance(vectorToLngLat(I), vectorToLngLat(B))) {
|
|
150
|
+
return [vectorToLngLat(A), true, false];
|
|
151
|
+
} else {
|
|
152
|
+
return [vectorToLngLat(B), false, true];
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
return [vectorToLngLat(I), false, false];
|
|
156
|
+
}
|
|
113
157
|
var turf_nearest_point_on_line_default = nearestPointOnLine;
|
|
114
158
|
export {
|
|
115
159
|
turf_nearest_point_on_line_default as default,
|
package/dist/esm/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../index.ts"],"sourcesContent":["import { Feature, Point, LineString, MultiLineString } from \"geojson\";\nimport { bearing } from \"@turf/bearing\";\nimport { distance } from \"@turf/distance\";\nimport { destination } from \"@turf/destination\";\nimport { lineIntersect as lineIntersects } from \"@turf/line-intersect\";\nimport { flattenEach } from \"@turf/meta\";\nimport { point, lineString, Coord, Units } from \"@turf/helpers\";\nimport { getCoords } from \"@turf/invariant\";\n\n/**\n * Takes a {@link Point} and a {@link LineString} and calculates the closest Point on the (Multi)LineString.\n *\n * @name nearestPointOnLine\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 {string} [options.units='kilometers'] can be degrees, radians, miles, or kilometers\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 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\n const start: Feature<Point, { dist: number }> = point(coords[i]);\n start.properties.dist = distance(pt, start, options);\n //stop\n const stop: Feature<Point, { dist: number }> = point(coords[i + 1]);\n stop.properties.dist = distance(pt, stop, options);\n // sectionLength\n const sectionLength = distance(start, stop, options);\n //perpendicular\n const heightDistance = Math.max(\n start.properties.dist,\n stop.properties.dist\n );\n const direction = bearing(start, stop);\n const perpendicularPt1 = destination(\n pt,\n heightDistance,\n direction + 90,\n options\n );\n const perpendicularPt2 = destination(\n pt,\n heightDistance,\n direction - 90,\n options\n );\n const intersect = lineIntersects(\n lineString([\n perpendicularPt1.geometry.coordinates,\n perpendicularPt2.geometry.coordinates,\n ]),\n lineString([start.geometry.coordinates, stop.geometry.coordinates])\n );\n let intersectPt:\n | Feature<\n Point,\n { dist: number; multiFeatureIndex: number; location: number }\n >\n | undefined;\n\n if (intersect.features.length > 0 && intersect.features[0]) {\n intersectPt = {\n ...intersect.features[0],\n properties: {\n dist: distance(pt, intersect.features[0], options),\n multiFeatureIndex: multiFeatureIndex,\n location:\n length + distance(start, intersect.features[0], options),\n },\n };\n }\n\n if (start.properties.dist < closestPt.properties.dist) {\n closestPt = {\n ...start,\n properties: {\n ...start.properties,\n index: i,\n multiFeatureIndex: multiFeatureIndex,\n location: length,\n },\n };\n }\n\n if (stop.properties.dist < closestPt.properties.dist) {\n closestPt = {\n ...stop,\n properties: {\n ...stop.properties,\n index: i + 1,\n multiFeatureIndex: multiFeatureIndex,\n location: length + sectionLength,\n },\n };\n }\n\n if (\n intersectPt &&\n intersectPt.properties.dist < closestPt.properties.dist\n ) {\n closestPt = {\n ...intersectPt,\n properties: { ...intersectPt.properties, index: i },\n };\n }\n // update length\n length += sectionLength;\n }\n }\n );\n\n return closestPt;\n}\n\nexport { nearestPointOnLine };\nexport default nearestPointOnLine;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AACA,SAAS,eAAe;AACxB,SAAS,gBAAgB;AACzB,SAAS,mBAAmB;AAC5B,SAAS,iBAAiB,sBAAsB;AAChD,SAAS,mBAAmB;AAC5B,SAAS,OAAO,kBAAgC;AAChD,SAAS,iBAAiB;AA4B1B,SAAS,mBACP,OACA,IACA,UAA6B,CAAC,GAU9B;AACA,MAAI,CAAC,SAAS,CAAC,IAAI;AACjB,UAAM,IAAI,MAAM,qCAAqC;AAAA,EACvD;AAEA,MAAI,YAGA,MAAM,CAAC,UAAU,QAAQ,GAAG;AAAA,IAC9B,MAAM;AAAA,IACN,OAAO;AAAA,IACP,mBAAmB;AAAA,IACnB,UAAU;AAAA,EACZ,CAAC;AAED,MAAI,SAAS;AACb;AAAA,IACE;AAAA,IACA,SAAU,MAAW,eAAuB,mBAA2B;AACrE,YAAM,SAAc,UAAU,IAAI;AAElC,eAAS,IAAI,GAAG,IAAI,OAAO,SAAS,GAAG,KAAK;AAE1C,cAAM,QAA0C,MAAM,OAAO,CAAC,CAAC;AAC/D,cAAM,WAAW,OAAO,SAAS,IAAI,OAAO,OAAO;AAEnD,cAAM,OAAyC,MAAM,OAAO,IAAI,CAAC,CAAC;AAClE,aAAK,WAAW,OAAO,SAAS,IAAI,MAAM,OAAO;AAEjD,cAAM,gBAAgB,SAAS,OAAO,MAAM,OAAO;AAEnD,cAAM,iBAAiB,KAAK;AAAA,UAC1B,MAAM,WAAW;AAAA,UACjB,KAAK,WAAW;AAAA,QAClB;AACA,cAAM,YAAY,QAAQ,OAAO,IAAI;AACrC,cAAM,mBAAmB;AAAA,UACvB;AAAA,UACA;AAAA,UACA,YAAY;AAAA,UACZ;AAAA,QACF;AACA,cAAM,mBAAmB;AAAA,UACvB;AAAA,UACA;AAAA,UACA,YAAY;AAAA,UACZ;AAAA,QACF;AACA,cAAM,YAAY;AAAA,UAChB,WAAW;AAAA,YACT,iBAAiB,SAAS;AAAA,YAC1B,iBAAiB,SAAS;AAAA,UAC5B,CAAC;AAAA,UACD,WAAW,CAAC,MAAM,SAAS,aAAa,KAAK,SAAS,WAAW,CAAC;AAAA,QACpE;AACA,YAAI;AAOJ,YAAI,UAAU,SAAS,SAAS,KAAK,UAAU,SAAS,CAAC,GAAG;AAC1D,wBAAc,iCACT,UAAU,SAAS,CAAC,IADX;AAAA,YAEZ,YAAY;AAAA,cACV,MAAM,SAAS,IAAI,UAAU,SAAS,CAAC,GAAG,OAAO;AAAA,cACjD;AAAA,cACA,UACE,SAAS,SAAS,OAAO,UAAU,SAAS,CAAC,GAAG,OAAO;AAAA,YAC3D;AAAA,UACF;AAAA,QACF;AAEA,YAAI,MAAM,WAAW,OAAO,UAAU,WAAW,MAAM;AACrD,sBAAY,iCACP,QADO;AAAA,YAEV,YAAY,iCACP,MAAM,aADC;AAAA,cAEV,OAAO;AAAA,cACP;AAAA,cACA,UAAU;AAAA,YACZ;AAAA,UACF;AAAA,QACF;AAEA,YAAI,KAAK,WAAW,OAAO,UAAU,WAAW,MAAM;AACpD,sBAAY,iCACP,OADO;AAAA,YAEV,YAAY,iCACP,KAAK,aADE;AAAA,cAEV,OAAO,IAAI;AAAA,cACX;AAAA,cACA,UAAU,SAAS;AAAA,YACrB;AAAA,UACF;AAAA,QACF;AAEA,YACE,eACA,YAAY,WAAW,OAAO,UAAU,WAAW,MACnD;AACA,sBAAY,iCACP,cADO;AAAA,YAEV,YAAY,iCAAK,YAAY,aAAjB,EAA6B,OAAO,EAAE;AAAA,UACpD;AAAA,QACF;AAEA,kBAAU;AAAA,MACZ;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;AAGA,IAAO,qCAAQ;","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 * @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 {string} [options.units='kilometers'] can be degrees, radians, miles, or kilometers\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 start.properties.dist = distance(pt, start, options);\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 stop.properties.dist = distance(pt, stop, options);\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 // segment.\n if (startPos[0] === ptPos[0] && startPos[1] === ptPos[1]) {\n [intersectPos, , wasEnd] = [startPos, undefined, false];\n } else if (stopPos[0] === ptPos[0] && stopPos[1] === ptPos[1]) {\n [intersectPos, , wasEnd] = [stopPos, undefined, true];\n } else {\n // Otherwise, find the nearest point the hard way.\n [intersectPos, , wasEnd] = nearestPointOnSegment(\n start.geometry.coordinates,\n stop.geometry.coordinates,\n getCoord(pt)\n );\n }\n let intersectPt:\n | Feature<\n Point,\n { dist: number; multiFeatureIndex: number; location: number }\n >\n | undefined;\n\n if (intersectPos) {\n intersectPt = point(intersectPos, {\n dist: distance(pt, intersectPos, options),\n multiFeatureIndex: multiFeatureIndex,\n location: length + distance(start, intersectPos, options),\n });\n }\n\n if (\n intersectPt &&\n intersectPt.properties.dist < closestPt.properties.dist\n ) {\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/*\n * Plan is to externalise these vector functions to a simple third party\n * library.\n * Possible candidate is @amandaghassaei/vector-math though having some import\n * issues.\n */\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) {\n return Math.sqrt(Math.pow(v[0], 2) + Math.pow(v[1], 2) + Math.pow(v[2], 2));\n}\n\nfunction angle(v1: Vector, v2: Vector): number {\n const theta = dot(v1, v2) / (magnitude(v1) * magnitude(v2));\n return Math.acos(Math.min(Math.max(theta, -1), 1));\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 const lat = radiansToDegrees(Math.asin(z));\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, 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 // Components of target point.\n const [Cx, Cy, Cz] = C;\n\n // Calculate coefficients.\n const [D, E, F] = cross(A, B);\n const a = E * Cz - F * Cy;\n const b = F * Cx - D * Cz;\n const c = D * Cy - E * Cx;\n\n const f = c * E - b * F;\n const g = a * F - c * D;\n const h = b * D - a * E;\n\n const t = 1 / Math.sqrt(Math.pow(f, 2) + Math.pow(g, 2) + Math.pow(h, 2));\n\n // Vectors to the two points these great circles intersect.\n const I1: Vector = [f * t, g * t, h * t];\n const I2: Vector = [-1 * f * t, -1 * g * t, -1 * h * t];\n\n // Figure out which is the closest intersection to this segment of the great\n // circle.\n const angleAB = angle(A, B);\n const angleAI1 = angle(A, I1);\n const angleBI1 = angle(B, I1);\n const angleAI2 = angle(A, I2);\n const angleBI2 = angle(B, I2);\n\n let I: Vector;\n\n if (\n (angleAI1 < angleAI2 && angleAI1 < angleBI2) ||\n (angleBI1 < angleAI2 && angleBI1 < angleBI2)\n ) {\n I = I1;\n } else {\n I = I2;\n }\n\n // I is the closest intersection to the segment, though might not actually be\n // ON the segment.\n\n // If angle AI or BI is greater than angleAB, I lies on the circle *beyond* A\n // and B so use the closest of A or B as the intersection\n if (angle(A, I) > angleAB || angle(B, I) > angleAB) {\n if (\n distance(vectorToLngLat(I), vectorToLngLat(A)) <=\n distance(vectorToLngLat(I), vectorToLngLat(B))\n ) {\n return [vectorToLngLat(A), true, false];\n } else {\n return [vectorToLngLat(B), false, true];\n }\n }\n\n // As angleAI nor angleBI don't exceed angleAB, I is on the segment\n return [vectorToLngLat(I), false, false];\n}\n\nexport { nearestPointOnLine };\nexport default nearestPointOnLine;\n"],"mappings":"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","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@turf/nearest-point-on-line",
|
|
3
|
-
"version": "7.
|
|
3
|
+
"version": "7.2.0",
|
|
4
4
|
"description": "turf nearest-point-on-line module",
|
|
5
5
|
"author": "Turf Authors",
|
|
6
6
|
"license": "MIT",
|
|
@@ -46,30 +46,27 @@
|
|
|
46
46
|
"test:types": "tsc --esModuleInterop --module node16 --moduleResolution node16 --noEmit --strict types.ts"
|
|
47
47
|
},
|
|
48
48
|
"devDependencies": {
|
|
49
|
-
"@turf/along": "^7.
|
|
50
|
-
"@turf/length": "^7.
|
|
51
|
-
"@turf/truncate": "^7.
|
|
49
|
+
"@turf/along": "^7.2.0",
|
|
50
|
+
"@turf/length": "^7.2.0",
|
|
51
|
+
"@turf/truncate": "^7.2.0",
|
|
52
52
|
"@types/benchmark": "^2.1.5",
|
|
53
|
-
"@types/tape": "^4.
|
|
53
|
+
"@types/tape": "^4.13.4",
|
|
54
54
|
"benchmark": "^2.1.4",
|
|
55
55
|
"load-json-file": "^7.0.1",
|
|
56
56
|
"npm-run-all": "^4.1.5",
|
|
57
|
-
"tape": "^5.
|
|
58
|
-
"tsup": "^8.
|
|
59
|
-
"tsx": "^4.
|
|
60
|
-
"typescript": "^5.
|
|
57
|
+
"tape": "^5.9.0",
|
|
58
|
+
"tsup": "^8.3.5",
|
|
59
|
+
"tsx": "^4.19.2",
|
|
60
|
+
"typescript": "^5.5.4",
|
|
61
61
|
"write-json-file": "^5.0.0"
|
|
62
62
|
},
|
|
63
63
|
"dependencies": {
|
|
64
|
-
"@turf/
|
|
65
|
-
"@turf/
|
|
66
|
-
"@turf/
|
|
67
|
-
"@turf/
|
|
68
|
-
"@turf/invariant": "^7.1.0-alpha.70+948cdafaf",
|
|
69
|
-
"@turf/line-intersect": "^7.1.0-alpha.70+948cdafaf",
|
|
70
|
-
"@turf/meta": "^7.1.0-alpha.70+948cdafaf",
|
|
64
|
+
"@turf/distance": "^7.2.0",
|
|
65
|
+
"@turf/helpers": "^7.2.0",
|
|
66
|
+
"@turf/invariant": "^7.2.0",
|
|
67
|
+
"@turf/meta": "^7.2.0",
|
|
71
68
|
"@types/geojson": "^7946.0.10",
|
|
72
|
-
"tslib": "^2.
|
|
69
|
+
"tslib": "^2.8.1"
|
|
73
70
|
},
|
|
74
|
-
"gitHead": "
|
|
71
|
+
"gitHead": "7b0f0374c4668cd569f8904c71e2ae7d941be867"
|
|
75
72
|
}
|