Skip to content

Commit f2337e2

Browse files
authored
Added approaches request paramenter to Directions and MapMatching (#827)
1 parent 2f3ba8d commit f2337e2

12 files changed

Lines changed: 332 additions & 5 deletions

File tree

Makefile

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,10 @@ directions-fixtures:
126126
curl "https://api.mapbox.com/directions/v5/mapbox/driving/-122.403561,37.777689;-122.405786,37.770369.json?access_token=$(MAPBOX_ACCESS_TOKEN)&steps=true&geometries=polyline&banner_instructions=true" \
127127
-o services-directions/src/test/resources/directions_v5_banner_instructions.json
128128

129+
# Directions: route with approaches in request
130+
curl "https://api.mapbox.com/directions/v5/mapbox/driving/13.4301,52.5109;13.432507621760521,52.501725088556014?approaches=unrestricted;curb&access_token=$(MAPBOX_ACCESS_TOKEN)" \
131+
-o services-directions/src/test/resources/directions_v5_approaches.json
132+
129133
mapmatching-fixtures:
130134
curl "https://api.mapbox.com/matching/v5/mapbox/driving/$(MAP_MATCHING_COORDINATES)?geometries=polyline&language=sv&steps=true&access_token=$(MAPBOX_ACCESS_TOKEN)" \
131135
-o services-matching/src/test/resources/map_matching_v5_polyline.json
@@ -134,6 +138,10 @@ mapmatching-fixtures:
134138
curl "https://api.mapbox.com/matching/v5/mapbox/driving/0,-40;0,-20?access_token=$(MAPBOX_ACCESS_TOKEN)" \
135139
-o services-matching/src/test/resources/mapmatching_nosegment_v5_polyline.json
136140

141+
# MapMatching request with approaches
142+
curl "https://api.mapbox.com/matching/v5/mapbox/driving/-117.1728265285492,32.71204416018209;-117.17334151268004,32.71254065549407?approaches=unrestricted;curb&access_token=$(MAPBOX_ACCESS_TOKEN)" \
143+
-o services-matching/src/test/resources/mapmatching_v5_approaches.json
144+
137145

138146
optimization-fixtures:
139147
# request an optimized car trip with no additional options

services-core/src/main/java/com/mapbox/core/utils/TextUtils.java

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,4 +161,25 @@ public static String formatDistributions(List<Integer[]> distributions) {
161161
}
162162
return TextUtils.join(";", distributionsFormatted);
163163
}
164+
165+
/**
166+
* Converts String array with approaches values
167+
* to a string ready for API consumption.
168+
* An approache could be unrestricted, curb or null.
169+
*
170+
* @param approaches a string representing approaches to each coordinate.
171+
* @return a formatted string.
172+
* @since 3.2.0
173+
*/
174+
public static String formatApproaches(String[] approaches) {
175+
for (int i = 0; i < approaches.length; i++) {
176+
if (approaches[i] == null) {
177+
approaches[i] = "";
178+
} else if (!approaches[i].equals("unrestricted")
179+
&& !approaches[i].equals("curb") && !approaches[i].isEmpty()) {
180+
return null;
181+
}
182+
}
183+
return TextUtils.join(";", approaches);
184+
}
164185
}

services-directions/src/main/java/com/mapbox/api/directions/v5/DirectionsCriteria.java

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,26 @@ public final class DirectionsCriteria {
206206
*/
207207
public static final String DESTINATION_LAST = "last";
208208

209+
/**
210+
* The routes can approach waypoints from either side of the road. <p>
211+
*
212+
* Used in MapMatching and Directions API.
213+
*
214+
* @since 3.2.0
215+
*/
216+
public static final String APPROACH_UNRESTRICTED = "unrestricted";
217+
218+
/**
219+
* The route will be returned so that on arrival,
220+
* the waypoint will be found on the side that corresponds with the driving_side of
221+
* the region in which the returned route is located. <p>
222+
*
223+
* Used in MapMatching and Directions API.
224+
*
225+
* @since 3.2.0
226+
*/
227+
public static final String APPROACH_CURB = "curb";
228+
209229
private DirectionsCriteria() {
210230
//not called
211231
}
@@ -322,4 +342,18 @@ private DirectionsCriteria() {
322342
})
323343
public @interface DestinationCriteria {
324344
}
345+
346+
347+
/**
348+
* Retention policy for the approaches parameter in the MapMatching and Directions API.
349+
*
350+
* @since 3.2.0
351+
*/
352+
@Retention(RetentionPolicy.SOURCE)
353+
@StringDef( {
354+
APPROACH_UNRESTRICTED,
355+
APPROACH_CURB
356+
})
357+
public @interface ApproachesCriteria {
358+
}
325359
}

services-directions/src/main/java/com/mapbox/api/directions/v5/DirectionsService.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ public interface DirectionsService {
4545
* useful for navigation
4646
* @param voiceUnits voice units
4747
* @param exclude exclude tolls, motorways or more along your route
48+
* @param approaches which side of the road to approach a waypoint.
4849
* @return the {@link DirectionsResponse} in a Call wrapper
4950
* @since 1.0.0
5051
*/
@@ -68,6 +69,7 @@ Call<DirectionsResponse> getCall(
6869
@Query("voice_instructions") Boolean voiceInstructions,
6970
@Query("banner_instructions") Boolean bannerInstructions,
7071
@Query("voice_units") String voiceUnits,
71-
@Query("exclude") String exclude
72+
@Query("exclude") String exclude,
73+
@Query("approaches") String approaches
7274
);
7375
}

services-directions/src/main/java/com/mapbox/api/directions/v5/MapboxDirections.java

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,8 @@ protected Call<DirectionsResponse> initializeCall() {
8585
voiceInstructions(),
8686
bannerInstructions(),
8787
voiceUnits(),
88-
exclude());
88+
exclude(),
89+
approaches());
8990
}
9091

9192
@Override
@@ -299,6 +300,9 @@ private static String formatCoordinates(List<Point> coordinates) {
299300
@Nullable
300301
abstract String exclude();
301302

303+
@Nullable
304+
abstract String approaches();
305+
302306
/**
303307
* Build a new {@link MapboxDirections} object with the initial values set for
304308
* {@link #baseUrl()}, {@link #profile()}, {@link #user()}, and {@link #geometries()}.
@@ -346,6 +350,7 @@ public abstract static class Builder {
346350
private double[] radiuses;
347351
private Point destination;
348352
private Point origin;
353+
private String[] approaches;
349354

350355
/**
351356
* The username for the account that the directions engine runs on. In most cases, this should
@@ -678,6 +683,29 @@ public Builder radiuses(@FloatRange(from = 0) double... radiuses) {
678683

679684
abstract Builder coordinates(@NonNull List<Point> coordinates);
680685

686+
687+
/**
688+
* Indicates from which side of the road to approach a waypoint.
689+
* Accepts unrestricted (default), curb or null.
690+
* If set to unrestricted , the route can approach waypoints
691+
* from either side of the road. If set to curb , the route will be returned
692+
* so that on arrival, the waypoint will be found on the side that corresponds with the
693+
* driving_side of the region in which the returned route is located.
694+
* If provided, the list of approaches must be the same length as the list of waypoints.
695+
*
696+
* @param approaches null if you'd like the default approaches,
697+
* else one of the options found in
698+
* {@link com.mapbox.api.directions.v5.DirectionsCriteria.ApproachesCriteria}.
699+
* @return this builder for chaining options together
700+
* @since 3.2.0
701+
*/
702+
public Builder addApproaches(String... approaches) {
703+
this.approaches = approaches;
704+
return this;
705+
}
706+
707+
abstract Builder approaches(@Nullable String approaches);
708+
681709
abstract MapboxDirections autoBuild();
682710

683711
/**
@@ -701,6 +729,18 @@ public MapboxDirections build() {
701729
+ " directions API request.");
702730
}
703731

732+
if (approaches != null) {
733+
if (approaches.length != coordinates.size()) {
734+
throw new ServicesException("Number of approach elements must match "
735+
+ "number of coordinates provided.");
736+
}
737+
String formattedApproaches = TextUtils.formatApproaches(approaches);
738+
if (formattedApproaches == null) {
739+
throw new ServicesException("All approaches values must be one of curb, unrestricted");
740+
}
741+
approaches(formattedApproaches);
742+
}
743+
704744
coordinates(coordinates);
705745
bearing(TextUtils.formatBearing(bearings));
706746
annotation(TextUtils.join(",", annotations));

services-directions/src/main/java/com/mapbox/api/directions/v5/models/RouteOptions.java

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,23 @@ public static Builder builder() {
239239
@NonNull
240240
public abstract String requestUuid();
241241

242+
/**
243+
* Indicates from which side of the road to approach a waypoint.
244+
* Accepts unrestricted (default) or curb . If set to unrestricted ,
245+
* the route can approach waypoints from either side of the road.
246+
* If set to curb, the route will be returned so that on arrival,
247+
* the waypoint will be found on the side that corresponds with the driving_side of the region
248+
* in which the returned route is located.
249+
* If provided, the list of approaches must be the same length as the list of waypoints.
250+
* However, you can skip a coordinate and show its position in the list with the ; separator.
251+
*
252+
* @return a string representing approaches for each waypoint
253+
* @since 3.2.0
254+
*/
255+
256+
@Nullable
257+
public abstract String approaches();
258+
242259
/**
243260
* Gson type adapter for parsing Gson to this class.
244261
*
@@ -456,7 +473,18 @@ public abstract Builder overview(
456473
* @since 3.0.0
457474
*/
458475
@Nullable
459-
public abstract Builder exclude(String exclude);
476+
public abstract Builder exclude(@NonNull String exclude);
477+
478+
/**
479+
* The same approaches the user originally made when the request was made.
480+
*
481+
* @param approaches unrestricted, curb or omitted (;)
482+
* @return this builder for chaining options together
483+
* @since 3.2.0
484+
*/
485+
486+
@Nullable
487+
public abstract Builder approaches(String approaches);
460488

461489
/**
462490
* Builds a new instance of the {@link RouteOptions} object.

services-directions/src/test/java/com/mapbox/api/directions/v5/MapboxDirectionsTest.java

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,12 @@
2929
import okhttp3.mockwebserver.RecordedRequest;
3030
import retrofit2.Response;
3131

32+
import static com.mapbox.api.directions.v5.DirectionsCriteria.APPROACH_CURB;
33+
import static com.mapbox.api.directions.v5.DirectionsCriteria.APPROACH_UNRESTRICTED;
3234
import static com.mapbox.api.directions.v5.DirectionsCriteria.GEOMETRY_POLYLINE;
3335
import static com.mapbox.api.directions.v5.DirectionsCriteria.PROFILE_DRIVING;
3436
import static org.hamcrest.CoreMatchers.containsString;
37+
import static org.hamcrest.Matchers.startsWith;
3538
import static org.hamcrest.junit.MatcherAssert.assertThat;
3639
import static org.junit.Assert.assertEquals;
3740
import static org.junit.Assert.assertNotNull;
@@ -47,6 +50,7 @@ public class MapboxDirectionsTest extends TestUtils {
4750
private static final String DIRECTIONS_V5_NO_ROUTE = "directions_v5_no_route.json";
4851
private static final String DIRECTIONS_V5_MAX_SPEED_ANNOTATION = "directions_v5_max_speed_annotation.json";
4952
private static final String DIRECTIONS_V5_BANNER_INSTRUCTIONS = "directions_v5_banner_instructions.json";
53+
private static final String DIRECTIONS_V5_APPROACHES_REQUEST = "directions_v5_approaches.json";
5054

5155
private MockWebServer server;
5256
private HttpUrl mockUrl;
@@ -65,6 +69,8 @@ public MockResponse dispatch(RecordedRequest request) throws InterruptedExceptio
6569
resource = DIRECTIONS_ROTARY_FIXTURE;
6670
} else if (request.getPath().contains("annotations")) {
6771
resource = DIRECTIONS_V5_ANNOTATIONS_FIXTURE;
72+
} else if (request.getPath().contains("approaches")) {
73+
resource = DIRECTIONS_V5_APPROACHES_REQUEST;
6874
} else if (request.getPath().contains("-151.2302")) {
6975
resource = DIRECTIONS_V5_NO_ROUTE;
7076
} else if (request.getPath().contains("-122.403561,37.777689")) {
@@ -552,4 +558,64 @@ public void subBannerInstructionsFromJson() throws Exception {
552558
assertNotNull(component.directions());
553559
assertEquals(2, component.directions().size());
554560
}
561+
562+
@Test
563+
public void sanityApproachesInstructions() throws Exception {
564+
MapboxDirections mapboxDirections = MapboxDirections.builder()
565+
.origin(Point.fromLngLat(1.0, 1.0))
566+
.addWaypoint(Point.fromLngLat(2.0, 2.0))
567+
.addWaypoint(Point.fromLngLat(3.0, 3.0))
568+
.destination(Point.fromLngLat(4.0, 4.0))
569+
.addApproaches(APPROACH_UNRESTRICTED, null, "", APPROACH_CURB)
570+
.baseUrl("https://foobar.com")
571+
.accessToken(ACCESS_TOKEN)
572+
.build();
573+
assertNotNull(mapboxDirections);
574+
assertTrue(mapboxDirections.cloneCall().request().url().toString()
575+
.contains("approaches=unrestricted;;;curb"));
576+
}
577+
@Test
578+
public void build_exceptionThrownWhenNumApproachesDoesNotMatchCoordinates() throws Exception {
579+
thrown.expect(ServicesException.class);
580+
thrown.expectMessage(
581+
startsWith("Number of approach elements must match"));
582+
MapboxDirections mapboxDirections = MapboxDirections.builder()
583+
.origin(Point.fromLngLat(2.0, 2.0))
584+
.destination(Point.fromLngLat(4.0, 4.0))
585+
.addApproaches(APPROACH_UNRESTRICTED)
586+
.baseUrl("https://foobar.com")
587+
.accessToken(ACCESS_TOKEN)
588+
.build();
589+
}
590+
591+
@Test
592+
public void build_exceptionThrownWhenInvalidApproaches() throws Exception {
593+
thrown.expect(ServicesException.class);
594+
thrown.expectMessage(
595+
startsWith("All approaches values must be one of curb, unrestricted"));
596+
MapboxDirections mapboxDirections = MapboxDirections.builder()
597+
.origin(Point.fromLngLat(2.0, 2.0))
598+
.destination(Point.fromLngLat(4.0, 4.0))
599+
.addApproaches(APPROACH_UNRESTRICTED, "restricted")
600+
.baseUrl("https://foobar.com")
601+
.accessToken(ACCESS_TOKEN)
602+
.build();
603+
}
604+
605+
@Test
606+
public void testApproaches() throws Exception {
607+
MapboxDirections mapboxDirections = MapboxDirections.builder()
608+
.profile(PROFILE_DRIVING)
609+
.origin(Point.fromLngLat(13.4301,52.5109))
610+
.destination(Point.fromLngLat(13.432508,52.501725))
611+
.addApproaches(APPROACH_UNRESTRICTED, APPROACH_CURB)
612+
.accessToken(ACCESS_TOKEN)
613+
.baseUrl(mockUrl.toString())
614+
.build();
615+
616+
mapboxDirections.setCallFactory(null);
617+
Response<DirectionsResponse> response = mapboxDirections.executeCall();
618+
assertEquals(200, response.code());
619+
assertEquals("Ok", response.body().code());
620+
}
555621
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"routes":[{"geometry":"y_o_Iac~pA|CjAtNfMfJmYbTdQ`EyN_BaB","legs":[{"summary":"","weight":476.5,"duration":329.3,"steps":[],"distance":1451.7}],"weight_name":"routability","weight":476.5,"duration":329.3,"distance":1451.7}],"waypoints":[{"name":"Andreasstraße","location":[13.430407,52.510853]},{"name":"Eisenbahnstraße","location":[13.432091,52.501882]}],"code":"Ok","uuid":"cjhtkxys0008646paxl5tdc20"}

services-matching/src/main/java/com/mapbox/api/matching/v5/MapMatchingService.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ public interface MapMatchingService {
5757
* marked-up text for voice guidance along the route.
5858
* @param voiceUnits voice units
5959
* @param waypoints Which input coordinates should be treated as waypoints.
60+
* @param approaches which side of the road to approach a waypoint.
6061
* @return the MapMatchingResponse in a Call wrapper
6162
* @since 2.0.0
6263
*/
@@ -79,5 +80,6 @@ Call<MapMatchingResponse> getCall(
7980
@Query("banner_instructions") Boolean bannerInstructions,
8081
@Query("voice_instructions") Boolean voiceInstructions,
8182
@Query("voice_units") String voiceUnits,
82-
@Query("waypoints") String waypoints);
83+
@Query("waypoints") String waypoints,
84+
@Query("approaches") String approaches);
8385
}

0 commit comments

Comments
 (0)