Skip to content

Commit

Permalink
Implement nearestPointOn(Multi)Line (#86)
Browse files Browse the repository at this point in the history
Co-authored-by: Levente Morva <[email protected]>
  • Loading branch information
lukas-h and skreborn authored Apr 19, 2022
1 parent a940904 commit 9c8f9da
Show file tree
Hide file tree
Showing 6 changed files with 537 additions and 1 deletion.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ Any new benchmarks must be named `*_benchmark.dart` and reside in the
- [ ] lineSliceAlong
- [ ] lineSplit
- [ ] mask
- [ ] nearestPointOnLine
- [x] [nearestPointOnLine](https://github.com/dartclub/turf_dart/blob/master/lib/nearest_point_on_line.dart)
- [ ] sector
- [ ] shortestPath
- [ ] unkinkPolygon
Expand Down
3 changes: 3 additions & 0 deletions lib/nearest_point_on_line.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
library turf_nearest_point_on_line;

export 'src/nearest_point_on_line.dart';
41 changes: 41 additions & 0 deletions lib/src/intersection.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import 'geojson.dart';

Point? intersects(LineString line1, LineString line2) {
if (line1.coordinates.length != 2) {
throw Exception('line1 must only contain 2 coordinates');
}

if (line2.coordinates.length != 2) {
throw Exception('line2 must only contain 2 coordinates');
}

final x1 = line1.coordinates[0][0]!;
final y1 = line1.coordinates[0][1]!;
final x2 = line1.coordinates[1][0]!;
final y2 = line1.coordinates[1][1]!;
final x3 = line2.coordinates[0][0]!;
final y3 = line2.coordinates[0][1]!;
final x4 = line2.coordinates[1][0]!;
final y4 = line2.coordinates[1][1]!;

final denom = (y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1);

if (denom == 0) {
return null;
}

final numeA = (x4 - x3) * (y1 - y3) - (y4 - y3) * (x1 - x3);
final numeB = (x2 - x1) * (y1 - y3) - (y2 - y1) * (x1 - x3);

final uA = numeA / denom;
final uB = numeB / denom;

if (uA >= 0 && uA <= 1 && uB >= 0 && uB <= 1) {
final x = x1 + uA * (x2 - x1);
final y = y1 + uA * (y2 - y1);

return Point(coordinates: Position.named(lng: x, lat: y));
}

return null;
}
191 changes: 191 additions & 0 deletions lib/src/nearest_point_on_line.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
import 'dart:math';

import 'bearing.dart';
import 'destination.dart';
import 'distance.dart';
import 'geojson.dart';
import 'helpers.dart';
import 'intersection.dart';

class _Nearest {
final Point point;
final num distance;
final int index;
final num location;

_Nearest({
required this.point,
required this.distance,
required this.index,
required this.location,
});

Feature<Point> toFeature() {
return Feature(
geometry: point,
properties: {
'dist': distance,
'index': index,
'location': location,
},
);
}
}

class _NearestMulti extends _Nearest {
final int line;

_NearestMulti({
required Point point,
required num distance,
required int index,
required num location,
required this.line,
}) : super(
point: point,
distance: distance,
index: index,
location: location,
);

@override
Feature<Point> toFeature() {
return Feature(
geometry: point,
properties: {
'dist': super.distance,
'line': line,
'index': super.index,
'location': super.location,
},
);
}
}

_Nearest _nearestPointOnLine(
LineString line,
Point point, [
Unit unit = Unit.kilometers,
]) {
_Nearest? nearest;

num length = 0;

for (var i = 0; i < line.coordinates.length - 1; ++i) {
final startCoordinates = line.coordinates[i];
final stopCoordinates = line.coordinates[i + 1];

final startPoint = Point(coordinates: startCoordinates);
final stopPoint = Point(coordinates: stopCoordinates);

final sectionLength = distance(startPoint, stopPoint, unit);

final start = _Nearest(
point: startPoint,
distance: distance(point, startPoint, unit),
index: i,
location: length,
);

final stop = _Nearest(
point: stopPoint,
distance: distance(point, stopPoint, unit),
index: i + 1,
location: length + sectionLength,
);

final heightDistance = max(start.distance, stop.distance);
final direction = bearing(startPoint, stopPoint);

final perpendicular1 = destination(
point,
heightDistance,
direction + 90,
unit,
);

final perpendicular2 = destination(
point,
heightDistance,
direction - 90,
unit,
);

final intersectionPoint = intersects(
LineString.fromPoints(points: [perpendicular1, perpendicular2]),
LineString.fromPoints(points: [startPoint, stopPoint]),
);

_Nearest? intersection;

if (intersectionPoint != null) {
intersection = _Nearest(
point: intersectionPoint,
distance: distance(point, intersectionPoint, unit),
index: i,
location: length + distance(startPoint, intersectionPoint, unit),
);
}

if (nearest == null || start.distance < nearest.distance) {
nearest = start;
}

if (stop.distance < nearest.distance) {
nearest = stop;
}

if (intersection != null && intersection.distance < nearest.distance) {
nearest = intersection;
}

length += sectionLength;
}

// A `LineString` is guaranteed to have at least two points and thus a
// nearest point has to exist.

return nearest!;
}

_NearestMulti? _nearestPointOnMultiLine(
MultiLineString lines,
Point point, [
Unit unit = Unit.kilometers,
]) {
_NearestMulti? nearest;

for (var i = 0; i < lines.coordinates.length; ++i) {
final line = LineString(coordinates: lines.coordinates[i]);

final candidate = _nearestPointOnLine(line, point);

if (nearest == null || candidate.distance < nearest.distance) {
nearest = _NearestMulti(
point: candidate.point,
distance: candidate.distance,
index: candidate.index,
location: candidate.location,
line: i,
);
}
}

return nearest;
}

Feature<Point> nearestPointOnLine(
LineString line,
Point point, [
Unit unit = Unit.kilometers,
]) {
return _nearestPointOnLine(line, point, unit).toFeature();
}

Feature<Point>? nearestPointOnMultiLine(
MultiLineString lines,
Point point, [
Unit unit = Unit.kilometers,
]) {
return _nearestPointOnMultiLine(lines, point, unit)?.toFeature();
}
1 change: 1 addition & 0 deletions lib/turf.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ export 'src/geojson.dart';
export 'src/midpoint.dart';
export 'src/helpers.dart';
export 'src/nearest_point.dart';
export 'src/nearest_point_on_line.dart';
Loading

0 comments on commit 9c8f9da

Please sign in to comment.