Skip to content

Commit

Permalink
feat: implement pointToLineDistance
Browse files Browse the repository at this point in the history
  • Loading branch information
mhammerc committed Aug 9, 2024
1 parent 0a58cb6 commit 47a6e47
Show file tree
Hide file tree
Showing 26 changed files with 730 additions and 4 deletions.
8 changes: 4 additions & 4 deletions Progress.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@

## Progress
This document tracks the progress being made to port over all of the Turf functionality to

This document tracks the progress being made to port over all of the Turf functionality to
Dart. This is an on going project and functions are being added once needed. If you'd like to contribute by adding a Turf function that's missing, please open a GitHub issue still with information relative to why you need this functionality.

### Measurement
Expand All @@ -20,7 +20,7 @@ Dart. This is an on going project and functions are being added once needed. If
- [x] [midpoint](https://github.com/dartclub/turf_dart/blob/main/lib/src/midpoint.dart)
- [ ] pointOnFeature
- [ ] polygonTangents
- [ ] pointToLineDistance
- [x] [pointToLineDistance](https://github.com/dartclub/turf_dart/blob/main/lib/src/point_to_line_distance.dart)
- [x] [rhumbBearing](https://github.com/dartclub/turf_dart/blob/main/lib/src/rhumb_bearing.dart)
- [x] [rhumbDestination](https://github.com/dartclub/turf_dart/blob/main/lib/src/rhumb_destination.dart)
- [x] [rhumbDistance](https://github.com/dartclub/turf_dart/blob/main/lib/src/rhumb_distance.dart)
Expand Down Expand Up @@ -177,4 +177,4 @@ Dart. This is an on going project and functions are being added once needed. If
- [x] [radiansToLength](https://github.com/dartclub/turf_dart/blob/main/lib/src/helpers.dart)
- [x] [radiansToDegrees](https://github.com/dartclub/turf_dart/blob/main/lib/src/helpers.dart)
- [ ] toMercator
- [ ] toWgs84
- [ ] toWgs84
4 changes: 4 additions & 0 deletions lib/point_to_line_distance.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
library turf_point_to_line_distance;

export 'package:geotypes/geotypes.dart';
export 'src/point_to_line_distance.dart';
12 changes: 12 additions & 0 deletions lib/src/helpers.dart
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,18 @@ enum Corner {
centroid,
}

/// Whether to calculate the distance based on geodesic (spheroid) or
/// planar (flat) method.
enum DistanceGeometry {
/// Calculations will be made on a 2D plane, NOT taking into account the
/// earth curvature.
planar,

/// Calculate the distance with geodesic (spheroid) equations. It will take
/// into account the earth as a sphere.
geodesic,
}

/// Earth Radius used with the Harvesine formula and approximates using a spherical (non-ellipsoid) Earth.
const earthRadius = 6371008.8;

Expand Down
80 changes: 80 additions & 0 deletions lib/src/point_to_line_distance.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import 'package:turf/distance.dart';
import 'package:turf/line_segment.dart';
import 'helpers.dart';

// Sourced from https://turfjs.org (MIT license) and from
// http://geomalgorithms.com/a02-_lines.html

/// Returns the minimum distance between a [point] and a [line], being the
/// distance from a line the minimum distance between the point and any
/// segment of the [LineString].
///
/// Example:
/// ```dart
/// final point = Point(coordinates: Position(0, 0));
/// final line = LineString(coordinates: [Position(1, 1), Position(-1, 1)]);
///
/// final distance = pointToLineDistance(point, line, unit: Unit.miles);
/// // distance == 69.11854715938406
/// ```
num pointToLineDistance(
Point point,
LineString line, {
Unit unit = Unit.kilometers,
DistanceGeometry method = DistanceGeometry.geodesic,
}) {
var distance = double.infinity;
final position = point.coordinates;

segmentEach(line, (segment, _, __, ___, ____) {
final a = segment.geometry!.coordinates[0];
final b = segment.geometry!.coordinates[1];
final d = _distanceToSegment(position, a, b, unit: unit, method: method);

if (d < distance) {
distance = d.toDouble();
}
});

return convertLength(distance, Unit.degrees, unit);
}

/// Returns the distance between a point P on a segment AB.
num _distanceToSegment(
Position p,
Position a,
Position b, {
required Unit unit,
required DistanceGeometry method,
}) {
final v = b - a;
final w = p - a;

final c1 = w.dotProduct(v);
if (c1 <= 0) {
return _calcDistance(p, a, method: method, unit: Unit.degrees);
}

final c2 = v.dotProduct(v);
if (c2 <= c1) {
return _calcDistance(p, b, method: method, unit: Unit.degrees);
}

final b2 = c1 / c2;
final pb = a + Position(v[0]! * b2, v[1]! * b2);
return _calcDistance(p, pb, method: method, unit: Unit.degrees);
}

num _calcDistance(
Position a,
Position b, {
required Unit unit,
required DistanceGeometry method,
}) {
if (method == DistanceGeometry.planar) {
return rhumbDistance(Point(coordinates: a), Point(coordinates: b), unit);
}

// Otherwise DistanceGeometry.geodesic
return distanceRaw(a, b, unit);
}
1 change: 1 addition & 0 deletions lib/turf.dart
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ export 'meta.dart';
export 'midpoint.dart';
export 'nearest_point_on_line.dart';
export 'nearest_point.dart';
export 'point_to_line_distance.dart';
export 'polygon_smooth.dart';
export 'polygon_to_line.dart';
export 'polyline.dart';
Expand Down
96 changes: 96 additions & 0 deletions test/components/point_to_line_distance_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import 'dart:convert';
import 'dart:io';

import 'package:test/test.dart';
import 'package:turf/helpers.dart';
import 'package:turf/src/point_to_line_distance.dart';

final distances = {
"city-line1.geojson": 1.0299686758,
"city-line2.geojson": 3.6186172981,
"city-segment-inside1.geojson": 1.1489389115,
"city-segment-inside2.geojson": 1.0280898152,
"city-segment-inside3.geojson": 3.5335695907,
"city-segment-obtuse1.geojson": 2.8573246363,
"city-segment-obtuse2.geojson": 3.3538913334,
"city-segment-projected1.geojson": 3.5886611693,
"city-segment-projected2.geojson": 4.163469898,
"issue-1156.geojson": 189.6618028794,
"line-fiji.geojson": 27.1266612008,
"line-resolute-bay.geojson": 425.0745081528,
"line1.geojson": 23.4224834672,
"line2.geojson": 188.015686924,
"segment-fiji.geojson": 27.6668301762,
"segment1.geojson": 69.0934195756,
"segment1a.geojson": 69.0934195756,
"segment2.geojson": 69.0934195756,
"segment3.geojson": 69.0828960461,
"segment4.geojson": 332.8803863574
};

void main() {
group('pointToLineDistance', () {
group('in == out', () {
final inDir = Directory('./test/examples/point_to_line_distance/in');

for (final file in inDir.listSync(recursive: true)) {
if (file is File && file.path.endsWith('.geojson')) {
testFile(file);
}
}
});

group('unit tests', () {
testPlanarGeodesic();
});
});
}

void testFile(File file) {
test(file.path, () {
final inSource = file.readAsStringSync();
final collection = FeatureCollection.fromJson(jsonDecode(inSource));

final rawPoint = collection.features[0];
final rawLine = collection.features[1];

final point = Feature<Point>.fromJson(rawPoint.toJson());
final line = Feature<LineString>.fromJson(rawLine.toJson());

final properties = rawPoint.properties ?? {};
final unitRaw = properties["units"] as String?;

var unit = Unit.kilometers;
if (unitRaw == 'meters') {
unit = Unit.meters;
} else if (unitRaw == 'miles') {
unit = Unit.miles;
} else {
expect(unitRaw, null, reason: '"units" was given but not handled.');
}

final distance =
pointToLineDistance(point.geometry!, line.geometry!, unit: unit);

final name = file.path.substring(file.path.lastIndexOf('/') + 1);

expect(distance, closeTo(distances[name]!, 0.01));
});
}

void testPlanarGeodesic() {
test('Check planar and geodesic results are different', () {
final pt = Point(coordinates: Position(0, 0));
final line = LineString(coordinates: [
Position(10, 10),
Position(-1, 1),
]);

final geoOut =
pointToLineDistance(pt, line, method: DistanceGeometry.geodesic);
final planarOut =
pointToLineDistance(pt, line, method: DistanceGeometry.planar);

expect(geoOut, isNot(equals(planarOut)));
});
}
30 changes: 30 additions & 0 deletions test/examples/point_to_line_distance/in/city-line1.geojson
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"properties": {},
"geometry": {
"type": "Point",
"coordinates": [-0.3767967224121093, 39.4689324766527]
}
},
{
"type": "Feature",
"properties": {},
"geometry": {
"type": "LineString",
"coordinates": [
[-0.40567874908447266, 39.47386857192064],
[-0.3963661193847656, 39.47578991028725],
[-0.38035869598388666, 39.482216070269594],
[-0.3776121139526367, 39.48195108571802],
[-0.3689002990722656, 39.47641930269614],
[-0.35945892333984375, 39.46349905420083],
[-0.35782814025878906, 39.45982131412374],
[-0.3458118438720703, 39.453890134716616]
]
}
}
]
}
27 changes: 27 additions & 0 deletions test/examples/point_to_line_distance/in/city-line2.geojson
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"properties": {},
"geometry": {
"type": "Point",
"coordinates": [-3.592529296875, 40.573804799488194]
}
},
{
"type": "Feature",
"properties": {},
"geometry": {
"type": "LineString",
"coordinates": [
[-3.8884735107421875, 40.420292132688964],
[-3.736724853515625, 40.276906410822825],
[-3.5025787353515625, 40.422383097039905],
[-3.5018920898437496, 40.516409213865586],
[-3.668060302734375, 40.559199680578075]
]
}
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"properties": {
"units": "miles"
},
"geometry": {
"type": "Point",
"coordinates": [-6.0047149658203125, 37.365109304227246]
}
},
{
"type": "Feature",
"properties": {},
"geometry": {
"type": "LineString",
"coordinates": [
[-6.0150146484375, 37.38011551844836],
[-5.931415557861328, 37.39702801486944]
]
}
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"properties": {},
"geometry": {
"type": "Point",
"coordinates": [-0.3767967224121093, 39.4689324766527]
}
},
{
"type": "Feature",
"properties": {},
"geometry": {
"type": "LineString",
"coordinates": [
[-0.3689861297607422, 39.47648555419739],
[-0.3595447540283203, 39.46363158174706]
]
}
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"properties": {},
"geometry": {
"type": "Point",
"coordinates": [-3.592529296875, 40.573804799488194]
}
},
{
"type": "Feature",
"properties": {},
"geometry": {
"type": "LineString",
"coordinates": [
[-3.503265380859375, 40.51693121343741],
[-3.6694335937500004, 40.560764667193595]
]
}
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"properties": {},
"geometry": {
"type": "Point",
"coordinates": [-6.030292510986328, 37.35746862390723]
}
},
{
"type": "Feature",
"properties": {},
"geometry": {
"type": "LineString",
"coordinates": [
[-6.0150146484375, 37.38011551844836],
[-5.931415557861328, 37.39702801486944]
]
}
}
]
}
Loading

0 comments on commit 47a6e47

Please sign in to comment.