Skip to content

Commit

Permalink
Implement lineSlice with tests
Browse files Browse the repository at this point in the history
  • Loading branch information
leiflinse-trivector committed Jan 30, 2024
1 parent 620d1f9 commit d5e7055
Show file tree
Hide file tree
Showing 4 changed files with 129 additions and 1 deletion.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ Any new benchmarks must be named `*_benchmark.dart` and reside in the
- [ ] [lineIntersect](https://github.com/dartclub/turf_dart/blob/main/lib/src/line_intersect.dart)
- [ ] lineOverlap
- [x] [lineSegment](https://github.com/dartclub/turf_dart/blob/main/lib/src/line_segment.dart)
- [ ] lineSlice
- [x] lineSlice
- [ ] lineSliceAlong
- [ ] lineSplit
- [ ] mask
Expand Down
3 changes: 3 additions & 0 deletions lib/line_slice.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
library turf_along;

export "src/line_slice.dart";
47 changes: 47 additions & 0 deletions lib/src/line_slice.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import 'package:turf/helpers.dart';
import 'package:turf/nearest_point_on_line.dart';
import 'package:turf/src/invariant.dart';

/// Takes a [line], at a start point [startPt], and a stop point [stopPt]
/// and returns a subsection of the line in-between those points.
/// The start & stop points don't need to fall exactly on the line.
///
/// If [startPt] and [stopPt] resolve to the same point on [line], null is returned
/// as the resolved line would only contain one point which isn't supported by LineString.
///
/// This can be useful for extracting only the part of a route between waypoints.
Feature<LineString> lineSlice(
Feature<Point> startPt, Feature<Point> stopPt, Feature<LineString> line) {
final coords = line.geometry;
final startPtGeometry = startPt.geometry;
final stopPtGeometry = stopPt.geometry;
if (coords == null) {
throw Exception('line has no geometry');
}
if (startPtGeometry == null) {
throw Exception('startPt has no geometry');
}
if (stopPtGeometry == null) {
throw Exception('stopPt has no geometry');
}

final startVertex = nearestPointOnLine(coords, startPtGeometry);
final stopVertex = nearestPointOnLine(coords, stopPtGeometry);
late final List<Feature<Point>> ends;
if (startVertex.properties!['index'] <= stopVertex.properties!['index']) {
ends = [startVertex, stopVertex];
} else {
ends = [stopVertex, startVertex];
}
final List<Position> clipCoords = [getCoord(ends[0])];
for (var i = ends[0].properties!['index'] + 1;
i < ends[1].properties!['index'];
i++) {
clipCoords.add(coords.coordinates[i]);
}
clipCoords.add(getCoord(ends[1]));
return Feature<LineString>(
geometry: LineString(coordinates: clipCoords),
properties: line.properties,
);
}
78 changes: 78 additions & 0 deletions test/components/line_slice_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import 'package:test/test.dart';
import 'package:turf/along.dart';
import 'package:turf/distance.dart';
import 'package:turf/helpers.dart';
import 'package:turf/length.dart';
import 'package:turf/src/line_slice.dart';

void main() {
test('lineSlice - exact points', () {
final slice = lineSlice(startFeature, viaFeature, lineFeature);
expect(slice, isNotNull);
expect(slice.properties, isNotNull);
expect(slice.properties!.keys, contains(propName));
expect(slice.properties![propName], equals(propValue));

final expectedLineFeature = Feature<LineString>(
geometry: LineString(coordinates: [start, via]),
);
expect(slice.geometry, isNotNull);
expect(slice.geometry!.coordinates, hasLength(2));
expect(length(slice).round(), equals(length(expectedLineFeature).round()));
});
test('lineSlice - interpolation', () {
const skipDist = 10;

final sliceFrom = along(lineFeature, skipDist, Unit.meters);
expect(sliceFrom, isNotNull);

final slice = lineSlice(sliceFrom, viaFeature, lineFeature);
expect(slice, isNotNull);
expect(slice.properties, isNotNull);
expect(slice.properties!.keys, contains(propName));
expect(slice.properties![propName], equals(propValue));

final expectedLine = Feature<LineString>(
geometry: LineString(coordinates: [start, via]),
);
expect(slice.geometry, isNotNull);
expect(slice.geometry!.coordinates, hasLength(2));
expect(
length(slice, Unit.meters).round(),
equals(length(expectedLine, Unit.meters).round() - skipDist),
);

// Sanity check of test data. No interpolation occurs if start and via are skipDist apart.
expect(distance(Point(coordinates: start), Point(coordinates: via)).round(),
isNot(equals(skipDist)));
});
}

final start = Position.named(
lat: 55.7090430186194,
lng: 13.184645393920405,
);
final via = Position.named(
lat: 55.70901279569489,
lng: 13.185546616182755,
);
final end = Position.named(
lat: 55.70764669578079,
lng: 13.187563637197076,
);
const propName = 'prop1';
const propValue = 1;
final lineFeature = Feature<LineString>(
geometry: LineString(
coordinates: [
start,
via,
end,
],
),
properties: {
propName: propValue,
},
);
final startFeature = Feature<Point>(geometry: Point(coordinates: start));
final viaFeature = Feature<Point>(geometry: Point(coordinates: via));

0 comments on commit d5e7055

Please sign in to comment.