Skip to content

Commit f377e46

Browse files
Adds lineSlice method (#158)
* Implement lineSlice with tests * add line_slice link --------- Co-authored-by: Lukas Himsel <[email protected]>
1 parent 41d5b4c commit f377e46

File tree

4 files changed

+129
-1
lines changed

4 files changed

+129
-1
lines changed

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@ Any new benchmarks must be named `*_benchmark.dart` and reside in the
149149
- [ ] [lineIntersect](https://github.com/dartclub/turf_dart/blob/main/lib/src/line_intersect.dart)
150150
- [ ] lineOverlap
151151
- [x] [lineSegment](https://github.com/dartclub/turf_dart/blob/main/lib/src/line_segment.dart)
152-
- [ ] lineSlice
152+
- [x] [lineSlice](https://github.com/dartclub/turf_dart/blob/main/lib/src/line_slice.dart)
153153
- [ ] lineSliceAlong
154154
- [ ] lineSplit
155155
- [ ] mask

lib/line_slice.dart

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
library turf_along;
2+
3+
export "src/line_slice.dart";

lib/src/line_slice.dart

+47
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import 'package:turf/helpers.dart';
2+
import 'package:turf/nearest_point_on_line.dart';
3+
import 'package:turf/src/invariant.dart';
4+
5+
/// Takes a [line], at a start point [startPt], and a stop point [stopPt]
6+
/// and returns a subsection of the line in-between those points.
7+
/// The start & stop points don't need to fall exactly on the line.
8+
///
9+
/// If [startPt] and [stopPt] resolve to the same point on [line], null is returned
10+
/// as the resolved line would only contain one point which isn't supported by LineString.
11+
///
12+
/// This can be useful for extracting only the part of a route between waypoints.
13+
Feature<LineString> lineSlice(
14+
Feature<Point> startPt, Feature<Point> stopPt, Feature<LineString> line) {
15+
final coords = line.geometry;
16+
final startPtGeometry = startPt.geometry;
17+
final stopPtGeometry = stopPt.geometry;
18+
if (coords == null) {
19+
throw Exception('line has no geometry');
20+
}
21+
if (startPtGeometry == null) {
22+
throw Exception('startPt has no geometry');
23+
}
24+
if (stopPtGeometry == null) {
25+
throw Exception('stopPt has no geometry');
26+
}
27+
28+
final startVertex = nearestPointOnLine(coords, startPtGeometry);
29+
final stopVertex = nearestPointOnLine(coords, stopPtGeometry);
30+
late final List<Feature<Point>> ends;
31+
if (startVertex.properties!['index'] <= stopVertex.properties!['index']) {
32+
ends = [startVertex, stopVertex];
33+
} else {
34+
ends = [stopVertex, startVertex];
35+
}
36+
final List<Position> clipCoords = [getCoord(ends[0])];
37+
for (var i = ends[0].properties!['index'] + 1;
38+
i < ends[1].properties!['index'];
39+
i++) {
40+
clipCoords.add(coords.coordinates[i]);
41+
}
42+
clipCoords.add(getCoord(ends[1]));
43+
return Feature<LineString>(
44+
geometry: LineString(coordinates: clipCoords),
45+
properties: line.properties,
46+
);
47+
}

test/components/line_slice_test.dart

+78
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
import 'package:test/test.dart';
2+
import 'package:turf/along.dart';
3+
import 'package:turf/distance.dart';
4+
import 'package:turf/helpers.dart';
5+
import 'package:turf/length.dart';
6+
import 'package:turf/src/line_slice.dart';
7+
8+
void main() {
9+
test('lineSlice - exact points', () {
10+
final slice = lineSlice(startFeature, viaFeature, lineFeature);
11+
expect(slice, isNotNull);
12+
expect(slice.properties, isNotNull);
13+
expect(slice.properties!.keys, contains(propName));
14+
expect(slice.properties![propName], equals(propValue));
15+
16+
final expectedLineFeature = Feature<LineString>(
17+
geometry: LineString(coordinates: [start, via]),
18+
);
19+
expect(slice.geometry, isNotNull);
20+
expect(slice.geometry!.coordinates, hasLength(2));
21+
expect(length(slice).round(), equals(length(expectedLineFeature).round()));
22+
});
23+
test('lineSlice - interpolation', () {
24+
const skipDist = 10;
25+
26+
final sliceFrom = along(lineFeature, skipDist, Unit.meters);
27+
expect(sliceFrom, isNotNull);
28+
29+
final slice = lineSlice(sliceFrom, viaFeature, lineFeature);
30+
expect(slice, isNotNull);
31+
expect(slice.properties, isNotNull);
32+
expect(slice.properties!.keys, contains(propName));
33+
expect(slice.properties![propName], equals(propValue));
34+
35+
final expectedLine = Feature<LineString>(
36+
geometry: LineString(coordinates: [start, via]),
37+
);
38+
expect(slice.geometry, isNotNull);
39+
expect(slice.geometry!.coordinates, hasLength(2));
40+
expect(
41+
length(slice, Unit.meters).round(),
42+
equals(length(expectedLine, Unit.meters).round() - skipDist),
43+
);
44+
45+
// Sanity check of test data. No interpolation occurs if start and via are skipDist apart.
46+
expect(distance(Point(coordinates: start), Point(coordinates: via)).round(),
47+
isNot(equals(skipDist)));
48+
});
49+
}
50+
51+
final start = Position.named(
52+
lat: 55.7090430186194,
53+
lng: 13.184645393920405,
54+
);
55+
final via = Position.named(
56+
lat: 55.70901279569489,
57+
lng: 13.185546616182755,
58+
);
59+
final end = Position.named(
60+
lat: 55.70764669578079,
61+
lng: 13.187563637197076,
62+
);
63+
const propName = 'prop1';
64+
const propValue = 1;
65+
final lineFeature = Feature<LineString>(
66+
geometry: LineString(
67+
coordinates: [
68+
start,
69+
via,
70+
end,
71+
],
72+
),
73+
properties: {
74+
propName: propValue,
75+
},
76+
);
77+
final startFeature = Feature<Point>(geometry: Point(coordinates: start));
78+
final viaFeature = Feature<Point>(geometry: Point(coordinates: via));

0 commit comments

Comments
 (0)