Skip to content

Commit 566c257

Browse files
authored
added line-slice-along, radiansToDegrees and degreesToRadians as functions (#800)
1 parent 00874b7 commit 566c257

7 files changed

Lines changed: 334 additions & 18 deletions

File tree

services-turf/src/main/java/com/mapbox/turf/TurfConversion.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,18 @@ public static double lengthToDegrees(double distance, @TurfUnitCriteria String u
5555
return radiansToDegrees(lengthToRadians(distance, units));
5656
}
5757

58+
/**
59+
* Converts an angle in degrees to radians.
60+
*
61+
* @param degrees angle between 0 and 360 degrees
62+
* @return angle in radians
63+
* @since 3.1.0
64+
*/
65+
public static double degreesToRadians(double degrees) {
66+
double radians = degrees % 360;
67+
return radians * Math.PI / 180;
68+
}
69+
5870
/**
5971
* Converts an angle in radians to degrees.
6072
*

services-turf/src/main/java/com/mapbox/turf/TurfMeasurement.java

Lines changed: 17 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
package com.mapbox.turf;
22

3+
import static com.mapbox.turf.TurfConversion.degreesToRadians;
4+
import static com.mapbox.turf.TurfConversion.radiansToDegrees;
5+
36
import android.support.annotation.FloatRange;
47
import android.support.annotation.NonNull;
58

@@ -38,18 +41,16 @@ private TurfMeasurement() {
3841
* @since 1.3.0
3942
*/
4043
public static double bearing(@NonNull Point point1, @NonNull Point point2) {
41-
double degrees2radians = Math.PI / 180;
42-
double radians2degrees = 180 / Math.PI;
4344

44-
double lon1 = degrees2radians * point1.longitude();
45-
double lon2 = degrees2radians * point2.longitude();
46-
double lat1 = degrees2radians * point1.latitude();
47-
double lat2 = degrees2radians * point2.latitude();
45+
double lon1 = degreesToRadians(point1.longitude());
46+
double lon2 = degreesToRadians(point2.longitude());
47+
double lat1 = degreesToRadians(point1.latitude());
48+
double lat2 = degreesToRadians(point2.latitude());
4849
double value1 = Math.sin(lon2 - lon1) * Math.cos(lat2);
4950
double value2 = Math.cos(lat1) * Math.sin(lat2) - Math.sin(lat1)
5051
* Math.cos(lat2) * Math.cos(lon2 - lon1);
5152

52-
return radians2degrees * Math.atan2(value1, value2);
53+
return radiansToDegrees(Math.atan2(value1, value2));
5354
}
5455

5556
/**
@@ -69,11 +70,10 @@ public static double bearing(@NonNull Point point1, @NonNull Point point2) {
6970
public static Point destination(@NonNull Point point, @FloatRange(from = 0) double distance,
7071
@FloatRange(from = -180, to = 180) double bearing,
7172
@NonNull @TurfConstants.TurfUnitCriteria String units) {
72-
double degrees2radians = Math.PI / 180;
73-
double radians2degrees = 180 / Math.PI;
74-
double longitude1 = degrees2radians * point.longitude();
75-
double latitude1 = degrees2radians * point.latitude();
76-
double bearingRad = degrees2radians * bearing;
73+
74+
double longitude1 = degreesToRadians(point.longitude());
75+
double latitude1 = degreesToRadians(point.latitude());
76+
double bearingRad = degreesToRadians(bearing);
7777

7878
double radians = TurfConversion.lengthToRadians(distance, units);
7979

@@ -84,7 +84,7 @@ public static Point destination(@NonNull Point point, @FloatRange(from = 0) doub
8484
Math.cos(radians) - Math.sin(latitude1) * Math.sin(latitude2));
8585

8686
return Point.fromLngLat(
87-
radians2degrees * longitude2, radians2degrees * latitude2);
87+
radiansToDegrees(longitude2), radiansToDegrees(latitude2));
8888
}
8989

9090
/**
@@ -114,11 +114,10 @@ public static double distance(@NonNull Point point1, @NonNull Point point2) {
114114
*/
115115
public static double distance(@NonNull Point point1, @NonNull Point point2,
116116
@NonNull @TurfConstants.TurfUnitCriteria String units) {
117-
double degrees2radians = Math.PI / 180;
118-
double difLat = degrees2radians * (point2.latitude() - point1.latitude());
119-
double difLon = degrees2radians * (point2.longitude() - point1.longitude());
120-
double lat1 = degrees2radians * point1.latitude();
121-
double lat2 = degrees2radians * point2.latitude();
117+
double difLat = degreesToRadians((point2.latitude() - point1.latitude()));
118+
double difLon = degreesToRadians((point2.longitude() - point1.longitude()));
119+
double lat1 = degreesToRadians(point1.latitude());
120+
double lat2 = degreesToRadians(point2.latitude());
122121

123122
double value = Math.pow(Math.sin(difLat / 2), 2)
124123
+ Math.pow(Math.sin(difLon / 2), 2) * Math.cos(lat1) * Math.cos(lat2);

services-turf/src/main/java/com/mapbox/turf/TurfMisc.java

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import static com.mapbox.core.internal.Preconditions.checkNotNull;
44

5+
import android.support.annotation.FloatRange;
56
import android.support.annotation.NonNull;
67
import com.mapbox.geojson.Feature;
78
import com.mapbox.geojson.LineString;
@@ -92,6 +93,120 @@ public static LineString lineSlice(@NonNull Point startPt, @NonNull Point stopPt
9293
return LineString.fromLngLats(points);
9394
}
9495

96+
/**
97+
* Takes a {@link LineString}, a specified distance along the line to a start {@link Point},
98+
* and a specified distance along the line to a stop point
99+
* and returns a subsection of the line in-between those points.
100+
*
101+
* This can be useful for extracting only the part of a route between two distances.
102+
*
103+
* @param line input line
104+
* @param startDist distance along the line to starting point
105+
* @param stopDist distance along the line to ending point
106+
* @param units one of the units found inside {@link TurfConstants.TurfUnitCriteria}
107+
* can be degrees, radians, miles, or kilometers
108+
* @return sliced line
109+
* @throws TurfException signals that a Turf exception of some sort has occurred.
110+
* @see <a href="http://turfjs.org/docs/#lineslicealong">Turf Line slice documentation</a>
111+
* @since 3.1.0
112+
*/
113+
@NonNull
114+
public static LineString lineSliceAlong(@NonNull Feature line,
115+
@FloatRange(from = 0) double startDist,
116+
@FloatRange(from = 0) double stopDist,
117+
@NonNull @TurfConstants.TurfUnitCriteria String units) {
118+
119+
checkNotNull(line.geometry(), "Feature.geometry() == null");
120+
if (!line.geometry().type().equals("LineString")) {
121+
throw new TurfException("input must be a LineString Feature or Geometry");
122+
}
123+
124+
return lineSliceAlong((LineString)line.geometry(), startDist, stopDist, units);
125+
}
126+
127+
/**
128+
* Takes a {@link LineString}, a specified distance along the line to a start {@link Point},
129+
* and a specified distance along the line to a stop point,
130+
* returns a subsection of the line in-between those points.
131+
*
132+
* This can be useful for extracting only the part of a route between two distances.
133+
*
134+
* @param line input line
135+
* @param startDist distance along the line to starting point
136+
* @param stopDist distance along the line to ending point
137+
* @param units one of the units found inside {@link TurfConstants.TurfUnitCriteria}
138+
* can be degrees, radians, miles, or kilometers
139+
* @return sliced line
140+
* @throws TurfException signals that a Turf exception of some sort has occurred.
141+
* @see <a href="http://turfjs.org/docs/#lineslicealong">Turf Line slice documentation</a>
142+
* @since 3.1.0
143+
*/
144+
@NonNull
145+
public static LineString lineSliceAlong(@NonNull LineString line,
146+
@FloatRange(from = 0) double startDist,
147+
@FloatRange(from = 0) double stopDist,
148+
@NonNull @TurfConstants.TurfUnitCriteria String units) {
149+
150+
List<Point> coords = line.coordinates();
151+
152+
if (coords.size() < 2) {
153+
throw new TurfException("Turf lineSlice requires a LineString Geometry made up of "
154+
+ "at least 2 coordinates. The LineString passed in only contains " + coords.size() + ".");
155+
} else if (startDist == stopDist) {
156+
throw new TurfException("Start and stop distance in Turf lineSliceAlong "
157+
+ "cannot equal each other.");
158+
}
159+
160+
List<Point> slice = new ArrayList<>(2);
161+
162+
double travelled = 0;
163+
for (int i = 0; i < coords.size(); i++) {
164+
165+
if (startDist >= travelled && i == coords.size() - 1) {
166+
break;
167+
168+
} else if (travelled > startDist && slice.size() == 0) {
169+
double overshot = startDist - travelled;
170+
if (overshot == 0) {
171+
slice.add(coords.get(i));
172+
return LineString.fromLngLats(slice);
173+
}
174+
double direction = TurfMeasurement.bearing(coords.get(i), coords.get(i - 1)) - 180;
175+
Point interpolated = TurfMeasurement.destination(coords.get(i), overshot, direction, units);
176+
slice.add(interpolated);
177+
}
178+
179+
if (travelled >= stopDist) {
180+
double overshot = stopDist - travelled;
181+
if (overshot == 0) {
182+
slice.add(coords.get(i));
183+
return LineString.fromLngLats(slice);
184+
}
185+
double direction = TurfMeasurement.bearing(coords.get(i), coords.get(i - 1)) - 180;
186+
Point interpolated = TurfMeasurement.destination(coords.get(i), overshot, direction, units);
187+
slice.add(interpolated);
188+
return LineString.fromLngLats(slice);
189+
}
190+
191+
if (travelled >= startDist) {
192+
slice.add(coords.get(i));
193+
}
194+
195+
if (i == coords.size() - 1) {
196+
return LineString.fromLngLats(slice);
197+
}
198+
199+
travelled += TurfMeasurement.distance(coords.get(i), coords.get(i + 1), units);
200+
}
201+
202+
if (travelled < startDist) {
203+
throw new TurfException("Start position is beyond line");
204+
}
205+
206+
return LineString.fromLngLats(slice);
207+
}
208+
209+
95210
/**
96211
* Takes a {@link Point} and a {@link LineString} and calculates the closest Point on the
97212
* LineString.

services-turf/src/test/java/com/mapbox/turf/TurfMiscTest.java

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
import java.io.IOException;
1313
import java.util.ArrayList;
14+
import java.util.Arrays;
1415
import java.util.List;
1516

1617
import static junit.framework.TestCase.assertNotNull;
@@ -26,6 +27,10 @@ public class TurfMiscTest extends TestUtils {
2627
private static final String LINE_SLICE_ROUTE_TWO = "turf-line-slice/route2.geojson";
2728
private static final String LINE_SLICE_VERTICAL = "turf-line-slice/vertical.geojson";
2829

30+
private static final String LINE_SLICE_ALONG_LINE_ONE = "turf-line-slice-along/line1.geojson";
31+
private static final String LINE_SLICE_ALONG_ROUTE_ONE = "turf-line-slice-along/route1.geojson";
32+
private static final String LINE_SLICE_ALONG_ROUTE_TWO = "turf-line-slice-along/route2.geojson";
33+
2934
@Rule
3035
public ExpectedException thrown = ExpectedException.none();
3136

@@ -340,4 +345,126 @@ public void testTurfPointOnLinePointsOnSidesOfLines() throws TurfException {
340345
assertNotEquals(snapped, last);
341346
}
342347
}
348+
349+
@Test
350+
public void testLineSliceAlongLine1() throws IOException, TurfException {
351+
Feature line1 = Feature.fromJson(loadJsonFixture(LINE_SLICE_ALONG_LINE_ONE));
352+
LineString lineStringLine1 = (LineString) line1.geometry();
353+
354+
double start = 500;
355+
double stop = 750;
356+
357+
Point start_point = TurfMeasurement.along(lineStringLine1, start, TurfConstants.UNIT_MILES);
358+
Point end_point = TurfMeasurement.along(lineStringLine1, stop, TurfConstants.UNIT_MILES);
359+
LineString sliced = TurfMisc.lineSliceAlong(line1, start, stop, TurfConstants.UNIT_MILES);
360+
361+
assertEquals(sliced.coordinates().get(0).coordinates(),
362+
start_point.coordinates());
363+
assertEquals(sliced.coordinates().get(sliced.coordinates().size() - 1).coordinates(),
364+
end_point.coordinates());
365+
}
366+
367+
@Test
368+
public void testLineSliceAlongOvershootLine1() throws IOException, TurfException {
369+
Feature line1 = Feature.fromJson(loadJsonFixture(LINE_SLICE_ALONG_LINE_ONE));
370+
LineString lineStringLine1 = (LineString) line1.geometry();
371+
372+
double start = 500;
373+
double stop = 1500;
374+
375+
Point start_point = TurfMeasurement.along(lineStringLine1, start, TurfConstants.UNIT_MILES);
376+
Point end_point = TurfMeasurement.along(lineStringLine1, stop, TurfConstants.UNIT_MILES);
377+
LineString sliced = TurfMisc.lineSliceAlong(line1, start, stop, TurfConstants.UNIT_MILES);
378+
379+
assertEquals(sliced.coordinates().get(0).coordinates(),
380+
start_point.coordinates());
381+
assertEquals(sliced.coordinates().get(sliced.coordinates().size() - 1).coordinates(),
382+
end_point.coordinates());
383+
}
384+
385+
@Test
386+
public void testLineSliceAlongRoute1() throws IOException, TurfException {
387+
Feature route1 = Feature.fromJson(loadJsonFixture(LINE_SLICE_ALONG_ROUTE_ONE));
388+
LineString lineStringRoute1 = (LineString)route1.geometry();
389+
390+
double start = 500;
391+
double stop = 750;
392+
393+
Point start_point = TurfMeasurement.along(lineStringRoute1, start, TurfConstants.UNIT_MILES);
394+
Point end_point = TurfMeasurement.along(lineStringRoute1, stop, TurfConstants.UNIT_MILES);
395+
396+
LineString sliced = TurfMisc.lineSliceAlong(route1, start, stop, TurfConstants.UNIT_MILES);
397+
398+
assertEquals(sliced.coordinates().get(0).coordinates(),
399+
start_point.coordinates());
400+
assertEquals(sliced.coordinates().get(sliced.coordinates().size() - 1).coordinates(),
401+
end_point.coordinates());
402+
}
403+
404+
@Test
405+
public void testLineSliceAlongRoute2() throws IOException, TurfException {
406+
407+
Feature route2 = Feature.fromJson(loadJsonFixture(LINE_SLICE_ALONG_ROUTE_TWO));
408+
LineString lineStringRoute2 = (LineString)route2.geometry();
409+
double start = 25;
410+
double stop = 50;
411+
412+
Point start_point = TurfMeasurement.along(lineStringRoute2, start, TurfConstants.UNIT_MILES);
413+
Point end_point = TurfMeasurement.along(lineStringRoute2, stop, TurfConstants.UNIT_MILES);
414+
LineString sliced = TurfMisc.lineSliceAlong(route2, start, stop, TurfConstants.UNIT_MILES);
415+
416+
assertEquals(sliced.coordinates().get(0).coordinates(),
417+
start_point.coordinates());
418+
assertEquals(sliced.coordinates().get(sliced.coordinates().size() - 1).coordinates(),
419+
end_point.coordinates());
420+
}
421+
422+
@Test
423+
public void testLineAlongStartLongerThanLength() throws Exception {
424+
thrown.expect(TurfException.class);
425+
thrown.expectMessage(startsWith("Start position is beyond line"));
426+
427+
Feature line1 = Feature.fromJson(loadJsonFixture(LINE_SLICE_ALONG_LINE_ONE));
428+
429+
double start = 500000;
430+
double stop = 800000;
431+
TurfMisc.lineSliceAlong(line1, start, stop, TurfConstants.UNIT_MILES);
432+
}
433+
434+
@Test
435+
public void testLineAlongStopLongerThanLength() throws IOException, TurfException {
436+
Feature line1 = Feature.fromJson(loadJsonFixture(LINE_SLICE_ALONG_LINE_ONE));
437+
LineString lineStringLine1 = (LineString) line1.geometry();
438+
439+
double start = 500;
440+
double stop = 800000;
441+
Point start_point = TurfMeasurement.along(lineStringLine1, start, TurfConstants.UNIT_MILES);
442+
List<Point> lineCoordinates = lineStringLine1.coordinates();
443+
LineString sliced = TurfMisc.lineSliceAlong(line1, start, stop, TurfConstants.UNIT_MILES);
444+
assertEquals(sliced.coordinates().get(0).coordinates(),
445+
start_point.coordinates());
446+
assertEquals(sliced.coordinates().get(sliced.coordinates().size() - 1).coordinates(),
447+
lineCoordinates.get(lineCoordinates.size() - 1).coordinates());
448+
}
449+
450+
@Test
451+
public void testShortLine() throws IOException, TurfException {
452+
453+
// Distance between points is about 186 miles
454+
LineString lineStringLine1 = LineString.fromLngLats(Arrays.asList(
455+
Point.fromLngLat(113.99414062499999, 22.350075806124867),
456+
Point.fromLngLat(116.76269531249999, 23.241346102386135)));
457+
458+
double start = 50;
459+
double stop = 100;
460+
461+
Point start_point = TurfMeasurement.along(lineStringLine1, start, TurfConstants.UNIT_MILES);
462+
Point end_point = TurfMeasurement.along(lineStringLine1, stop, TurfConstants.UNIT_MILES);
463+
LineString sliced = TurfMisc.lineSliceAlong(lineStringLine1, start, stop, TurfConstants.UNIT_MILES);
464+
465+
assertEquals(sliced.coordinates().get(0).coordinates(),
466+
start_point.coordinates());
467+
assertEquals(sliced.coordinates().get(sliced.coordinates().size() - 1).coordinates(),
468+
end_point.coordinates());
469+
}
343470
}

0 commit comments

Comments
 (0)