Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions packages/turf-nearest-point-on-line/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ on the line.

### Parameters

* `lines` **([Geometry][1] | [Feature][2]<([LineString][3] | [MultiLineString][4])>)** lines to snap to
* `pt` **([Geometry][1] | [Feature][2]<[Point][5]> | [Array][6]<[number][7]>)** point to snap from
* `lines` **([Geometry][1] | [Feature][2]<([LineString][3] | [MultiLineString][4])>)** Lines to snap to
* `inputPoint` **([Geometry][1] | [Feature][2]<[Point][5]> | [Array][6]<[number][7]>)** Point to snap from
* `options` **[Object][8]** Optional parameters (optional, default `{}`)

* `options.units` **Units** Supports all valid Turf [Units][9] (optional, default `'kilometers'`)
Expand All @@ -29,16 +29,16 @@ var line = turf.lineString([
[-77.021884, 38.889563],
[-77.019824, 38.892368]
]);
var pt = turf.point([-77.037076, 38.884017]);
var inputPoint = turf.point([-77.037076, 38.884017]);

var snapped = turf.nearestPointOnLine(line, pt, {units: 'miles'});
var snapped = turf.nearestPointOnLine(line, inputPoint, {units: 'miles'});

//addToMap
var addToMap = [line, pt, snapped];
var addToMap = [line, inputPoint, snapped];
snapped.properties['marker-color'] = '#00f';
```

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.
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.

[1]: https://tools.ietf.org/html/rfc7946#section-3.1

Expand Down
121 changes: 79 additions & 42 deletions packages/turf-nearest-point-on-line/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,11 @@ import { getCoord, getCoords } from "@turf/invariant";
* on the line.
*
* @function
* @param {Geometry|Feature<LineString|MultiLineString>} lines lines to snap to
* @param {Geometry|Feature<Point>|number[]} pt point to snap from
* @param {Geometry|Feature<LineString|MultiLineString>} lines Lines to snap to
* @param {Geometry|Feature<Point>|number[]} inputPoint Point to snap from
* @param {Object} [options={}] Optional parameters
* @param {Units} [options.units='kilometers'] Supports all valid Turf {@link https://turfjs.org/docs/api/types/Units Units}
* @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.
* @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.
* @example
* var line = turf.lineString([
* [-77.031669, 38.878605],
Expand All @@ -32,48 +32,73 @@ import { getCoord, getCoords } from "@turf/invariant";
* [-77.021884, 38.889563],
* [-77.019824, 38.892368]
* ]);
* var pt = turf.point([-77.037076, 38.884017]);
* var inputPoint = turf.point([-77.037076, 38.884017]);
*
* var snapped = turf.nearestPointOnLine(line, pt, {units: 'miles'});
* var snapped = turf.nearestPointOnLine(line, inputPoint, {units: 'miles'});
*
* //addToMap
* var addToMap = [line, pt, snapped];
* var addToMap = [line, inputPoint, snapped];
* snapped.properties['marker-color'] = '#00f';
*/
function nearestPointOnLine<G extends LineString | MultiLineString>(
lines: Feature<G> | G,
pt: Coord,
inputPoint: Coord,
options: { units?: Units } = {}
): Feature<
Point,
{
dist: number;
index: number;
lineStringIndex: number;
segmentIndex: number;
totalDistance: number;
lineDistance: number;
segmentDistance: number;
pointDistance: number;
// deprecated properties START
/** @deprecated use `lineStringIndex` instead */
multiFeatureIndex: number;
/** @deprecated use `segmentIndex` instead */
index: number;
/** @deprecated use `totalDistance` instead */
location: number;
/** @deprecated use `pointDistance` instead */
dist: number;
// deprecated properties END
[key: string]: any;
}
> {
if (!lines || !pt) {
throw new Error("lines and pt are required arguments");
if (!lines || !inputPoint) {
throw new Error("lines and inputPoint are required arguments");
}

const ptPos = getCoord(pt);
const inputPos = getCoord(inputPoint);

let closestPt: Feature<
Point,
{ dist: number; index: number; multiFeatureIndex: number; location: number }
> = point([Infinity, Infinity], {
dist: Infinity,
index: -1,
let closestPt = point([Infinity, Infinity], {
lineStringIndex: -1,
segmentIndex: -1,
totalDistance: -1,
lineDistance: -1,
segmentDistance: -1,
pointDistance: Infinity,
// deprecated properties START
multiFeatureIndex: -1,
index: -1,
location: -1,
dist: Infinity,
// deprecated properties END
});

let length = 0.0;
let totalDistance = 0.0;
let lineDistance = 0.0;
let currentLineStringIndex = -1;
flattenEach(
lines,
function (line: any, _featureIndex: number, multiFeatureIndex: number) {
function (line: any, _featureIndex: number, lineStringIndex: number) {
//reset lineDistance at each changed lineStringIndex
if (currentLineStringIndex !== lineStringIndex) {
currentLineStringIndex = lineStringIndex;
lineDistance = 0.0;
}

const coords: any = getCoords(line);

for (let i = 0; i < coords.length - 1; i++) {
Expand All @@ -85,47 +110,59 @@ function nearestPointOnLine<G extends LineString | MultiLineString>(
const stop: Feature<Point, { dist: number }> = point(coords[i + 1]);
const stopPos = getCoord(stop);

// sectionLength
const sectionLength = distance(start, stop, options);
// segmentLength
const segmentLength = distance(start, stop, options);
let intersectPos: Position;
let wasEnd: boolean;

// Short circuit if snap point is start or end position of the line
// Test the end position first for consistency in case they are
// coincident
if (stopPos[0] === ptPos[0] && stopPos[1] === ptPos[1]) {
if (stopPos[0] === inputPos[0] && stopPos[1] === inputPos[1]) {
[intersectPos, wasEnd] = [stopPos, true];
} else if (startPos[0] === ptPos[0] && startPos[1] === ptPos[1]) {
} else if (startPos[0] === inputPos[0] && startPos[1] === inputPos[1]) {
[intersectPos, wasEnd] = [startPos, false];
} else {
// Otherwise, find the nearest point the hard way.
[intersectPos, wasEnd] = nearestPointOnSegment(
startPos,
stopPos,
ptPos
inputPos
);
}

const intersectPt = point(intersectPos, {
dist: distance(pt, intersectPos, options),
multiFeatureIndex: multiFeatureIndex,
location: length + distance(start, intersectPos, options),
});

if (intersectPt.properties.dist < closestPt.properties.dist) {
closestPt = {
...intersectPt,
properties: {
...intersectPt.properties,
// Legacy behaviour where index progresses to next segment # if we
// went with the end point this iteration.
index: wasEnd ? i + 1 : i,
},
const pointDistance = distance(inputPoint, intersectPos, options);

if (pointDistance < closestPt.properties.pointDistance) {
const segmentDistance = distance(start, intersectPos, options);
closestPt = point(intersectPos, {
lineStringIndex: lineStringIndex,
// Legacy behaviour where index progresses to next segment
// if we went with the end point this iteration.
segmentIndex: wasEnd ? i + 1 : i,
totalDistance: totalDistance + segmentDistance,
lineDistance: lineDistance + segmentDistance,
segmentDistance: segmentDistance,
pointDistance: pointDistance,
// deprecated properties START
multiFeatureIndex: -1,
index: -1,
location: -1,
dist: Infinity,
});
closestPt.properties = {
...closestPt.properties,
multiFeatureIndex: closestPt.properties.lineStringIndex,
index: closestPt.properties.segmentIndex,
location: closestPt.properties.totalDistance,
dist: closestPt.properties.pointDistance,
// deprecated properties END
};
}

// update length
length += sectionLength;
// update totalDistance and lineDistance
totalDistance += segmentLength;
lineDistance += segmentLength;
}
}
);
Expand Down
1 change: 1 addition & 0 deletions packages/turf-nearest-point-on-line/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
"author": "Turf Authors",
"contributors": [
"Angel Lacret <@alacret>",
"Emil Junker <@EmilJunker>",
"Jon Miles <@jonmiles>",
"John Ziebro <@Insighttful>"
],
Expand Down
Loading