diff --git a/Makefile b/Makefile index 11bf01fcf..cd88c175f 100644 --- a/Makefile +++ b/Makefile @@ -166,6 +166,10 @@ mapmatching-fixtures: curl "https://api.mapbox.com/matching/v5/mapbox/driving/$(MAP_MATCHING_COORDINATES)?steps=true&overview=full&geometries=polyline6&roundabout_exits=true&voice_instructions=true&language=he&access_token=$(MAPBOX_ACCESS_TOKEN)" \ -o services-matching/src/test/resources/map_matching_v5_invalid_voice_language.json + # MapMatching with post request + curl -d "coordinates=2.344003915786743,48.85805170891599;2.346750497817993,48.85727523615161;2.348681688308716,48.85936462637049;2.349550724029541,48.86084691113991;2.349550724029541,48.8608892614883;2.349625825881958,48.86102337068847;2.34982967376709,48.86125629633996&steps=true&tidy=true&waypoints=0;6" "https://api.mapbox.com/matching/v5/mapbox/driving?access_token=$(MAPBOX_ACCESS_TOKEN)" \ + -o services-matching/src/test/resources/map_matching_v5_post.json + optimization-fixtures: # request an optimized car trip with no additional options curl "https://api.mapbox.com/optimized-trips/v1/mapbox/driving/-122.42,37.78;-122.45,37.91;-122.48,37.73?access_token=$(MAPBOX_ACCESS_TOKEN)" \ diff --git a/services-matching/src/main/java/com/mapbox/api/matching/v5/MapMatchingService.java b/services-matching/src/main/java/com/mapbox/api/matching/v5/MapMatchingService.java index d89e0ea16..615560643 100644 --- a/services-matching/src/main/java/com/mapbox/api/matching/v5/MapMatchingService.java +++ b/services-matching/src/main/java/com/mapbox/api/matching/v5/MapMatchingService.java @@ -3,8 +3,11 @@ import com.mapbox.api.matching.v5.models.MapMatchingResponse; import retrofit2.Call; +import retrofit2.http.Field; +import retrofit2.http.FormUrlEncoded; import retrofit2.http.GET; import retrofit2.http.Header; +import retrofit2.http.POST; import retrofit2.http.Path; import retrofit2.http.Query; @@ -16,7 +19,7 @@ public interface MapMatchingService { /** - * Constructs the html call using the information passed in through the + * Constructs the GET call using the information passed in through the * {@link MapboxMapMatching.Builder}. * * @param userAgent user agent @@ -84,4 +87,76 @@ Call getCall( @Query("waypoints") String waypoints, @Query("waypoint_names") String waypointNames, @Query("approaches") String approaches); + + + /** + * Constructs the POST call using the information passed in through the + * {@link MapboxMapMatching.Builder}. + * + * @param userAgent user agent + * @param user user + * @param profile directions profile ID; either mapbox/driving, mapbox/walking, + * or mapbox/cycling + * @param coordinates inaccurate traces from a GPS unit or a phone + * @param accessToken Mapbox access token + * @param geometries format of the returned geometry. Allowed values are: geojson + * (as LineString), polyline with precision 5, polyline6. The default + * value is polyline + * @param radiuses a list of integers in meters indicating the assumed precision of + * the used tracking device. There must be as many radiuses as there + * are coordinates in the request, each separated by ;. Values can be + * a number between 0 and 30. Use higher numbers (20-30) for noisy + * traces and lower numbers (1-10) for clean traces. The default value + * is 5 + * @param steps whether to return steps and turn-by-turn instructions. Can be true + * or false. The default is false + * @param overview type of returned overview geometry. Can be full (the most detailed + * geometry available), simplified (a simplified version of the full + * geometry), or false (no overview geometry). The default is simplified + * @param timestamps timestamps corresponding to each coordinate provided in the request; + * must be numbers in Unix time (seconds since the Unix epoch). There + * must be as many timestamps as there are coordinates in the request, + * each separated by {@code ;} + * @param annotations whether or not to return additional metadata for each coordinate + * along the match geometry. Can be one or all of 'duration', + * 'distance', or 'nodes', each separated by ,. See the response + * object for more details on what it is included with annotations + * @param language language of returned turn-by-turn text instructions + * @param tidy whether or not to transparently remove clusters and re-sample + * traces for improved map matching results + * @param roundaboutExits Whether or not to emit instructions at roundabout exits. + * @param bannerInstructions Whether or not to return banner objects associated with + * the `routeSteps`. Should be used in conjunction with `steps`. + * @param voiceInstructions whether or not to return + * marked-up text for voice guidance along the route. + * @param voiceUnits voice units + * @param waypoints Which input coordinates should be treated as waypoints. + * @param waypointNames wustom names for waypoints used for the arrival instruction. + * @param approaches which side of the road to approach a waypoint. + * @return the MapMatchingResponse in a Call wrapper + * @since 4.4.0 + */ + @FormUrlEncoded + @POST("matching/v5/{user}/{profile}") + Call postCall( + @Header("User-Agent") String userAgent, + @Path("user") String user, + @Path("profile") String profile, + @Field("coordinates") String coordinates, + @Query("access_token") String accessToken, + @Field("geometries") String geometries, + @Field("radiuses") String radiuses, + @Field("steps") Boolean steps, + @Field("overview") String overview, + @Field("timestamps") String timestamps, + @Field("annotations") String annotations, + @Field("language") String language, + @Field("tidy") Boolean tidy, + @Field("roundabout_exits") Boolean roundaboutExits, + @Field("banner_instructions") Boolean bannerInstructions, + @Field("voice_instructions") Boolean voiceInstructions, + @Field("voice_units") String voiceUnits, + @Field("waypoints") String waypoints, + @Field("waypoint_names") String waypointNames, + @Field("approaches") String approaches); } diff --git a/services-matching/src/main/java/com/mapbox/api/matching/v5/MapboxMapMatching.java b/services-matching/src/main/java/com/mapbox/api/matching/v5/MapboxMapMatching.java index f5b1cd24c..0e6c7b62b 100644 --- a/services-matching/src/main/java/com/mapbox/api/matching/v5/MapboxMapMatching.java +++ b/services-matching/src/main/java/com/mapbox/api/matching/v5/MapboxMapMatching.java @@ -71,6 +71,29 @@ protected GsonBuilder getGsonBuilder() { @Override protected Call initializeCall() { + if (usePostMethod()) { + return getService().postCall( + ApiCallHelper.getHeaderUserAgent(clientAppName()), + user(), + profile(), + coordinates(), + accessToken(), + geometries(), + radiuses(), + steps(), + overview(), + timestamps(), + annotations(), + language(), + tidy(), + roundaboutExits(), + bannerInstructions(), + voiceInstructions(), + voiceUnits(), + waypoints(), + waypointNames(), + approaches()); + } return getService().getCall( ApiCallHelper.getHeaderUserAgent(clientAppName()), user(), @@ -242,6 +265,8 @@ private static List formatCoordinates(String coordinates) { return coordinatesFormatted; } + @NonNull + abstract Boolean usePostMethod(); @Nullable abstract String clientAppName(); @@ -319,7 +344,8 @@ public static Builder builder() { .baseUrl(Constants.BASE_API_URL) .profile(DirectionsCriteria.PROFILE_DRIVING) .geometries(DirectionsCriteria.GEOMETRY_POLYLINE6) - .user(DirectionsCriteria.PROFILE_DEFAULT_USER); + .user(DirectionsCriteria.PROFILE_DEFAULT_USER) + .usePostMethod(false); } /** @@ -338,6 +364,29 @@ public abstract static class Builder { private String[] waypointNames; private String[] approaches; + /** + * Use POST method to request data. + * The default is to use GET. + * @return this builder for chaining options together + * @since 4.4.0 + */ + public Builder post() { + usePostMethod(true); + return this; + } + + /** + * Use GET method to request data. + * @return this builder for chaining options together + * @since 4.4.0 + */ + public Builder get() { + usePostMethod(false); + return this; + } + + abstract Builder usePostMethod(@NonNull Boolean usePost); + /** * Required to call when this is being built. If no access token provided, * {@link ServicesException} will be thrown. diff --git a/services-matching/src/test/java/com/mapbox/api/matching/v5/MapboxMapMatchingTest.java b/services-matching/src/test/java/com/mapbox/api/matching/v5/MapboxMapMatchingTest.java index c41841298..00a73f6f7 100644 --- a/services-matching/src/test/java/com/mapbox/api/matching/v5/MapboxMapMatchingTest.java +++ b/services-matching/src/test/java/com/mapbox/api/matching/v5/MapboxMapMatchingTest.java @@ -678,4 +678,31 @@ public void testWithWaypointNames() throws Exception { assertEquals(200, response.code()); assertEquals("Ok", response.body().code()); } + + @Test + public void testUsePostMethod() throws Exception { + MapboxMapMatching mapMatching = MapboxMapMatching.builder() + .accessToken(ACCESS_TOKEN) + .baseUrl(mockUrl.toString()) + .profile(PROFILE_DRIVING) + .steps(true) + .tidy(true) + .waypoints("0;6") + .coordinates(Arrays.asList( + Point.fromLngLat(2.344003915786743,48.85805170891599), + Point.fromLngLat(2.346750497817993,48.85727523615161), + Point.fromLngLat(2.348681688308716,48.85936462637049), + Point.fromLngLat(2.349550724029541,48.86084691113991), + Point.fromLngLat(2.349550724029541,48.8608892614883), + Point.fromLngLat(2.349625825881958,48.86102337068847), + Point.fromLngLat(2.34982967376709,48.86125629633996))) + .post() + .build(); + Response response = mapMatching.executeCall(); + assertEquals(200, response.code()); + assertEquals("Ok", response.body().code()); + + assertNotNull(response.body().matchings()); + assertEquals(1, response.body().matchings().size()); + } } diff --git a/services-matching/src/test/resources/map_matching_v5_post.json b/services-matching/src/test/resources/map_matching_v5_post.json new file mode 100644 index 000000000..10349cb35 --- /dev/null +++ b/services-matching/src/test/resources/map_matching_v5_post.json @@ -0,0 +1 @@ +{"matchings":[{"confidence":0.8646240884788093,"geometry":"{qeiHayhMjDkSOm@cDaB{R}I","legs":[{"summary":"Quai de la Mégisserie, Boulevard de Sébastopol","weight":204.3,"duration":140.5,"steps":[{"intersections":[{"out":0,"entry":[true],"bearings":[112],"location":[2.344011,48.858064]},{"out":1,"in":2,"entry":[false,true,false],"bearings":[30,105,285],"location":[2.344384,48.857963]},{"out":1,"in":2,"entry":[true,true,false],"bearings":[15,105,285],"location":[2.345408,48.857687]},{"out":0,"in":1,"entry":[true,false,false],"bearings":[120,285,330],"location":[2.346993,48.857275]},{"out":0,"in":2,"entry":[true,false,false],"bearings":[105,195,300],"location":[2.34712,48.857239]}],"driving_side":"right","geometry":"{qeiHayhMRiAt@mEReADUTqARqA@EJu@FYF]","mode":"driving","maneuver":{"bearing_after":112,"bearing_before":0,"location":[2.344011,48.858064],"type":"depart","instruction":"Head southeast on Quai de la Mégisserie"},"weight":59.099999999999994,"duration":43.800000000000004,"name":"Quai de la Mégisserie","distance":257.2},{"intersections":[{"out":0,"in":2,"entry":[true,true,false],"bearings":[60,120,285],"location":[2.347272,48.857201]},{"out":0,"in":2,"entry":[true,false,false,true],"bearings":[15,120,195,300],"location":[2.347777,48.857724]},{"out":0,"in":2,"entry":[true,false,false,true],"bearings":[15,120,195,300],"location":[2.348211,48.8585]},{"out":0,"in":2,"entry":[true,true,false],"bearings":[15,105,195],"location":[2.34833,48.858721]},{"out":0,"in":2,"entry":[true,true,false],"bearings":[15,120,195],"location":[2.348758,48.859525]},{"out":0,"in":2,"entry":[true,true,false],"bearings":[15,90,195],"location":[2.349062,48.860069]}],"driving_side":"right","geometry":"oleiHmmiMIa@EKGGCCCAc@SQIQKWKs@]{@a@EAMGGCSKOGuCoAKE}As@MGaBs@eAi@_Ac@IE","mode":"driving","maneuver":{"bearing_after":66,"bearing_before":109,"location":[2.347272,48.857201],"modifier":"left","type":"turn","instruction":"Turn left onto Place du Châtelet"},"weight":145.2,"duration":96.7,"name":"Place du Châtelet","distance":493.20000000000005},{"intersections":[{"in":0,"entry":[true],"bearings":[200],"location":[2.349738,48.861278]}],"driving_side":"right","geometry":"_ffiH{|iM","mode":"driving","maneuver":{"bearing_after":0,"bearing_before":20,"location":[2.349738,48.861278],"modifier":"right","type":"arrive","instruction":"You have arrived at your destination, on the right"},"weight":0,"duration":0,"name":"Boulevard de Sébastopol","distance":0}],"distance":750.4}],"weight_name":"routability","weight":204.3,"duration":140.5,"distance":750.4}],"tracepoints":[{"alternatives_count":0,"waypoint_index":0,"matchings_index":0,"distance":1.4298733755647755,"name":"Quai de la Mégisserie","location":[2.344011,48.858064]},{"alternatives_count":0,"waypoint_index":null,"matchings_index":0,"distance":6.317472000209194,"name":"Quai de la Mégisserie","location":[2.346781,48.857328]},{"alternatives_count":0,"waypoint_index":null,"matchings_index":0,"distance":0.6275897933079265,"name":"Boulevard de Sébastopol","location":[2.348674,48.859367]},{"alternatives_count":0,"waypoint_index":null,"matchings_index":0,"distance":3.765538759196103,"name":"Boulevard de Sébastopol","location":[2.349503,48.860859]},null,{"alternatives_count":0,"waypoint_index":null,"matchings_index":0,"distance":2.090012803498222,"name":"Boulevard de Sébastopol","location":[2.349599,48.861029]},{"alternatives_count":0,"waypoint_index":1,"matchings_index":0,"distance":7.178624584475835,"name":"Boulevard de Sébastopol","location":[2.349738,48.861278]}],"code":"Ok"} \ No newline at end of file