diff --git a/Makefile b/Makefile index 2fa0204f9..24c0b236e 100644 --- a/Makefile +++ b/Makefile @@ -112,6 +112,10 @@ mapmatching-fixtures: curl "https://api.mapbox.com/matching/v5/mapbox/driving/$(MAP_MATCHING_COORDINATES)?geometries=polyline&language=sv&steps=true&access_token=$(MAPBOX_ACCESS_TOKEN)" \ -o services-matching/src/test/resources/mapmatching_v5_polyline.json + # Unmatchable MapMatching request + curl "https://api.mapbox.com/matching/v5/mapbox/driving/0,-40;0,-20?access_token=$(MAPBOX_ACCESS_TOKEN)" \ + -o services-matching/src/test/resources/mapmatching_nosegment_v5_polyline.json + optimization-fixtures: # request an optimized car trip with no additional options 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 7459907a9..8bec39438 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 @@ -1,5 +1,7 @@ package com.mapbox.api.matching.v5; +import static com.sun.xml.internal.ws.spi.db.BindingContextFactory.LOGGER; + import android.support.annotation.FloatRange; import android.support.annotation.IntRange; import android.support.annotation.NonNull; @@ -14,6 +16,7 @@ import com.mapbox.api.directions.v5.DirectionsCriteria.OverviewCriteria; import com.mapbox.api.directions.v5.DirectionsCriteria.ProfileCriteria; import com.mapbox.api.matching.v5.models.MapMatchingAdapterFactory; +import com.mapbox.api.matching.v5.models.MapMatchingError; import com.mapbox.api.matching.v5.models.MapMatchingResponse; import com.mapbox.core.MapboxService; import com.mapbox.core.constants.Constants; @@ -23,11 +26,19 @@ import com.mapbox.core.utils.TextUtils; import com.mapbox.geojson.Point; +import okhttp3.ResponseBody; +import retrofit2.Call; +import retrofit2.Callback; +import retrofit2.Converter; +import retrofit2.Response; + +import java.io.IOException; +import java.lang.annotation.Annotation; + import java.util.ArrayList; import java.util.List; import java.util.Locale; - -import retrofit2.Call; +import java.util.logging.Level; /** * The Mapbox map matching interface (v5) @@ -44,6 +55,7 @@ public abstract class MapboxMapMatching extends MapboxService { + protected MapboxMapMatching() { super(MapMatchingService.class); } @@ -77,6 +89,69 @@ protected Call initializeCall() { waypoints()); } + /** + * Wrapper method for Retrofits {@link Call#execute()} call returning a response specific to the + * Map Matching API. + * + * @return the Map Matching v5 response once the call completes successfully + * @throws IOException Signals that an I/O exception of some sort has occurred + * @since 1.0.0 + */ + @Override + public Response executeCall() throws IOException { + + Response response = getCall().execute(); + if (!response.isSuccessful()) { + errorDidOccur(null, response); + } + return response; + } + + /** + * Wrapper method for Retrofits {@link Call#enqueue(Callback)} call returning a response specific + * to the Map Matching API. Use this method to make a directions request on the Main Thread. + * + * @param callback a {@link Callback} which is used once the {@link MapMatchingResponse} is + * created. + * @since 1.0.0 + */ + @Override + public void enqueueCall(final Callback callback) { + getCall().enqueue(new Callback() { + @Override + public void onResponse(Call call, + Response response) { + if (!response.isSuccessful()) { + errorDidOccur(callback, response); + return; + } + callback.onResponse(call, response); + } + + @Override + public void onFailure(Call call, Throwable throwable) { + callback.onFailure(call, throwable); + } + }); + } + + + private void errorDidOccur(@Nullable Callback callback, + @NonNull Response response) { + // Response gave an error, we try to LOGGER any messages into the LOGGER here. + Converter errorConverter = + getRetrofit().responseBodyConverter(MapMatchingError.class, new Annotation[0]); + if (callback == null) { + return; + } + try { + callback.onFailure(getCall(), + new Throwable(errorConverter.convert(response.errorBody()).message())); + } catch (IOException ioException) { + LOGGER.log(Level.WARNING, "Failed to complete your request. ", ioException); + } + } + @Nullable abstract String clientAppName(); diff --git a/services-matching/src/main/java/com/mapbox/api/matching/v5/models/MapMatchingError.java b/services-matching/src/main/java/com/mapbox/api/matching/v5/models/MapMatchingError.java new file mode 100644 index 000000000..e4b3f3f44 --- /dev/null +++ b/services-matching/src/main/java/com/mapbox/api/matching/v5/models/MapMatchingError.java @@ -0,0 +1,119 @@ +package com.mapbox.api.matching.v5.models; + +import android.support.annotation.Nullable; + +import com.google.auto.value.AutoValue; +import com.google.gson.Gson; +import com.google.gson.TypeAdapter; + +import java.io.Serializable; + +/** + * If an InvalidInput error is thrown, this class can be used to get both the code and the message + * which holds an explanation of the invalid input. + * + * On error, the server responds with different HTTP status codes. + * For responses with HTTP status codes lower than 500, the JSON response body includes the code + * property, which may be used by client programs to manage control flow. + * The response body may also include a message property, with a human-readable explaination + * of the error. If a server error occurs, the HTTP status code will be 500 or higher and + * the response will not include a code property. + * + * @since 3.0.0 + */ +@AutoValue +public abstract class MapMatchingError implements Serializable { + + /** + * Create a new instance of this class by using the {@link Builder} class. + * + * @return this classes {@link Builder} for creating a new instance + * @since 3.0.0 + */ + public static Builder builder() { + return new AutoValue_MapMatchingError.Builder(); + } + + /** + * String indicating the state of the response. This is a separate code than the HTTP status code. + * On normal valid responses, the value will be Ok. The possible responses are listed below: + *
    + *
  • Ok Normal case.
  • + *
  • NoMatch The input did not produce any matches, or the waypoints requested + * were not found in the resulting match. features will be an empty array.
  • + *
  • TooManyCoordinates There are more than 100 points in the request.
  • + *
  • ProfileNotFound Needs to be a valid profile.
  • + *
  • InvalidInputmessage will hold an explanation of the invalid input.
  • + *
+ * + * @return a string with one of the given values described in the list above + * @since 3.0.0 + */ + @Nullable + public abstract String code(); + + /** + * Provides a short message with the explanation of the invalid input. + * + * @return a string containing the message API MapMatching response + * @since 3.0.0 + */ + @Nullable + public abstract String message(); + + /** + * Gson type adapter for parsing Gson to this class. + * + * @param gson the built {@link Gson} object + * @return the type adapter for this class + * @since 3.0.0 + */ + public static TypeAdapter typeAdapter(Gson gson) { + return new AutoValue_MapMatchingError.GsonTypeAdapter(gson); + } + + /** + * This builder can be used to set the values describing the {@link MapMatchingError}. + * + * @since 3.0.0 + */ + @AutoValue.Builder + public abstract static class Builder { + + /** + * String indicating the state of the response. This is a separate code than the HTTP status + * code. On normal valid responses, the value will be Ok. The possible responses are listed + * below: + *
    + *
  • Ok Normal case.
  • + *
  • NoMatch The input did not produce any matches, or the waypoints + * requested were not found in the resulting match. features will be an empty array.
  • + *
  • TooManyCoordinates There are more than 100 points in the request.
  • + *
  • ProfileNotFound Needs to be a valid profile.
  • + *
  • InvalidInputmessage will hold an explanation of the invalid input.
  • + *
+ * + * @param code a string with one of the given values described in the list above + * @return this builder for chaining options together + * @since 3.0.0 + */ + public abstract Builder code(String code); + + /** + * Provides a short message with the explanation of the invalid input. + * + * @param message a string containing the message API Directions response + * @return this builder for chaining options together + * @since 3.0.0 + */ + public abstract Builder message(String message); + + /** + * Build a new {@link MapMatchingError} object. + * + * @return a new {@link MapMatchingError} using the provided values in this builder + * @since 3.0.0 + */ + public abstract MapMatchingError build(); + } +} diff --git a/services-matching/src/main/java/com/mapbox/api/matching/v5/models/MapMatchingMatching.java b/services-matching/src/main/java/com/mapbox/api/matching/v5/models/MapMatchingMatching.java index 58ca3f7a9..c86cf3bf1 100644 --- a/services-matching/src/main/java/com/mapbox/api/matching/v5/models/MapMatchingMatching.java +++ b/services-matching/src/main/java/com/mapbox/api/matching/v5/models/MapMatchingMatching.java @@ -90,6 +90,17 @@ public static Builder builder() { */ public abstract double confidence(); + /** + * Convert the current {@link MapMatchingMatching} to its builder holding the currently assigned + * values. This allows you to modify a single variable and then rebuild the project resulting in + * an updated and modifier {@link MapMatchingMatching}. + * + * @return a {@link MapMatchingMatching.Builder} with the same values set to match the ones + * defined in this {@link MapMatchingMatching} + * @since 3.0.0 + */ + public abstract MapMatchingMatching.Builder toBuilder(); + /** * Gson type adapter for parsing Gson to this class. * diff --git a/services-matching/src/main/java/com/mapbox/api/matching/v5/models/MapMatchingResponse.java b/services-matching/src/main/java/com/mapbox/api/matching/v5/models/MapMatchingResponse.java index 52f125cd6..f7a75dc4b 100644 --- a/services-matching/src/main/java/com/mapbox/api/matching/v5/models/MapMatchingResponse.java +++ b/services-matching/src/main/java/com/mapbox/api/matching/v5/models/MapMatchingResponse.java @@ -45,6 +45,15 @@ public static Builder builder() { @NonNull public abstract String code(); + /** + * Optionally shows up in a directions response if an error or something unexpected occurred. + * + * @return a string containing the message API MapMatching response with if an error occurred + * @since 3.0.0 + */ + @Nullable + public abstract String message(); + /** * List of {@link MapMatchingMatching} objects, essentially a DirectionsWaypoint object with the * addition of a confidence value. @@ -67,6 +76,18 @@ public static Builder builder() { @Nullable public abstract List tracepoints(); + + /** + * Convert the current {@link MapMatchingResponse} to its builder holding the currently assigned + * values. This allows you to modify a single variable and then rebuild the project resulting in + * an updated and modifier {@link MapMatchingResponse}. + * + * @return a {@link MapMatchingResponse.Builder} with the same values set to match the ones + * defined in this {@link MapMatchingResponse} + * @since 3.0.0 + */ + public abstract MapMatchingResponse.Builder toBuilder(); + /** * Gson type adapter for parsing Gson to this class. * @@ -103,6 +124,16 @@ public abstract static class Builder { */ public abstract Builder code(@Nullable String code); + /** + * Optionally shows up in a map maptching response if an error or something unexpected occurred. + * + * @param message a string containing the message API MapMatching response with if an error + * occurred + * @return this builder for chaining options together + * @since 3.0.0 + */ + public abstract Builder message(@Nullable String message); + /** * List of {@link MapMatchingMatching} objects, essentially a DirectionsWaypoint object with the * addition of a confidence value. 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 9de58dcd8..4dbd01d5d 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 @@ -1,5 +1,9 @@ package com.mapbox.api.matching.v5; +import static org.hamcrest.CoreMatchers.containsString; +import static org.hamcrest.junit.MatcherAssert.assertThat; + +import com.mapbox.api.matching.v5.models.MapMatchingResponse; import com.mapbox.core.TestUtils; import com.mapbox.core.exceptions.ServicesException; import com.mapbox.api.directions.v5.DirectionsCriteria; @@ -20,6 +24,7 @@ import okhttp3.mockwebserver.MockResponse; import okhttp3.mockwebserver.MockWebServer; import okhttp3.mockwebserver.RecordedRequest; +import retrofit2.Response; import static org.hamcrest.Matchers.startsWith; import static org.junit.Assert.assertNotNull; @@ -28,6 +33,7 @@ public class MapboxMapMatchingTest extends TestUtils { private static final String MAP_MATCHING_FIXTURE = "map_matching_v5_polyline.json"; + private static final String MAP_MATCHING_ERROR_FIXTURE = "mapmatching_nosegment_v5_polyline.json"; private MockWebServer server; private HttpUrl mockUrl; @@ -38,8 +44,12 @@ public void setUp() throws Exception { server.setDispatcher(new okhttp3.mockwebserver.Dispatcher() { @Override public MockResponse dispatch(RecordedRequest request) throws InterruptedException { + String resource = MAP_MATCHING_FIXTURE; + if (request.getPath().contains("0,-40;0,-20")) { // no matching segment + resource = MAP_MATCHING_ERROR_FIXTURE; + } try { - String response = loadJsonFixture(MAP_MATCHING_FIXTURE); + String response = loadJsonFixture(resource); return new MockResponse().setBody(response); } catch (IOException ioException) { throw new RuntimeException(ioException); @@ -449,4 +459,18 @@ public void sanityRoundExtsInstructions() throws Exception { .contains("roundabout_exits=true")); } + @Test + public void noValidMatchTest() throws Exception { + MapboxMapMatching mapMatching = MapboxMapMatching.builder() + .coordinate(Point.fromLngLat(0, -40)) + .coordinate(Point.fromLngLat(0, -20 )) + .baseUrl(mockUrl.toString()) + .accessToken(ACCESS_TOKEN) + .build(); + + Response response = mapMatching.executeCall(); + assertThat(response.body().message(), containsString("Could not find")); + assertThat(response.body().code(), containsString("NoSegment")); + } + } diff --git a/services-matching/src/test/resources/mapmatching_nosegment_v5_polyline.json b/services-matching/src/test/resources/mapmatching_nosegment_v5_polyline.json new file mode 100644 index 000000000..78945a3ae --- /dev/null +++ b/services-matching/src/test/resources/mapmatching_nosegment_v5_polyline.json @@ -0,0 +1 @@ +{"code":"NoSegment","message":"Could not find a matching segment for input coordinates","matchings":[]} \ No newline at end of file diff --git a/services-matching/src/test/resources/mapmatching_v5_polyline.json b/services-matching/src/test/resources/mapmatching_v5_polyline.json index 485748016..ef3b5d2d6 100644 --- a/services-matching/src/test/resources/mapmatching_v5_polyline.json +++ b/services-matching/src/test/resources/mapmatching_v5_polyline.json @@ -1 +1 @@ -{"matchings":[{"confidence":0.8849067703299458,"geometry":"}_m_Iu{{pAEZwA{@cC}AqBsAaCyA","legs":[{"summary":"Adalbertstraße","weight":60.6,"duration":47.4,"steps":[{"intersections":[{"classes":["restricted"],"out":0,"entry":[true],"bearings":[291],"location":[13.418992,52.500626]}],"driving_side":"right","geometry":"}_m_Iu{{pA?DET","mode":"driving","maneuver":{"bearing_after":291,"bearing_before":0,"location":[13.418992,52.500626],"modifier":"left","type":"depart","instruction":"Kör åt nordväst"},"weight":33.6,"duration":20.4,"name":"","distance":10.2},{"intersections":[{"out":0,"in":1,"entry":[true,false,true],"bearings":[15,105,210],"location":[13.418852,52.500659]}],"driving_side":"right","geometry":"c`m_Iyz{pAoAu@GE","mode":"driving","maneuver":{"bearing_after":21,"bearing_before":291,"location":[13.418852,52.500659],"modifier":"right","type":"turn","instruction":"Sväng höger in på Adalbertstraße"},"weight":27,"duration":27,"name":"Adalbertstraße","distance":52.6},{"intersections":[{"in":0,"entry":[true],"bearings":[202],"location":[13.419148,52.501096]}],"driving_side":"right","geometry":"{bm_Iu|{pA","mode":"driving","maneuver":{"bearing_after":0,"bearing_before":22,"location":[13.419148,52.501096],"modifier":"left","type":"arrive","instruction":"Du är framme vid din 1:a destination, till vänster"},"weight":0,"duration":0,"name":"Adalbertstraße","distance":0}],"distance":62.7},{"summary":"Adalbertstraße","weight":32.6,"duration":20.6,"steps":[{"intersections":[{"out":0,"entry":[true],"bearings":[22],"location":[13.419148,52.501096]},{"out":0,"in":2,"entry":[true,true,false,true],"bearings":[30,120,195,300],"location":[13.419277,52.501287]}],"driving_side":"right","geometry":"{bm_Iu|{pAe@Ym@a@o@a@","mode":"driving","maneuver":{"bearing_after":22,"bearing_before":0,"location":[13.419148,52.501096],"modifier":"left","type":"depart","instruction":"Kör åt nordost på Adalbertstraße"},"weight":32.6,"duration":20.6,"name":"Adalbertstraße","distance":80},{"intersections":[{"in":0,"entry":[true],"bearings":[204],"location":[13.41962,52.501755]}],"driving_side":"right","geometry":"_gm_Is_|pA","mode":"driving","maneuver":{"bearing_after":0,"bearing_before":24,"location":[13.41962,52.501755],"modifier":"right","type":"arrive","instruction":"Du är framme vid din 2:a destination, till höger"},"weight":0,"duration":0,"name":"Adalbertstraße","distance":0}],"distance":80},{"summary":"Adalbertstraße","weight":9.9,"duration":9.8,"steps":[{"intersections":[{"out":0,"entry":[true],"bearings":[24],"location":[13.41962,52.501755]},{"out":0,"in":2,"entry":[true,true,false,true],"bearings":[30,120,210,300],"location":[13.419982,52.502249]}],"driving_side":"right","geometry":"_gm_Is_|pAaBgAOK","mode":"driving","maneuver":{"bearing_after":24,"bearing_before":0,"location":[13.41962,52.501755],"modifier":"right","type":"depart","instruction":"Kör åt nordost på Adalbertstraße"},"weight":9.9,"duration":9.8,"name":"Adalbertstraße","distance":70.4},{"intersections":[{"in":0,"entry":[true],"bearings":[203],"location":[13.420041,52.502334]}],"driving_side":"right","geometry":"qjm_Igb|pA","mode":"driving","maneuver":{"bearing_after":0,"bearing_before":23,"location":[13.420041,52.502334],"modifier":"left","type":"arrive","instruction":"Du är framme vid din 3:e destination, till vänster"},"weight":0,"duration":0,"name":"Adalbertstraße","distance":0}],"distance":70.4},{"summary":"Adalbertstraße","weight":18.8,"duration":18.8,"steps":[{"intersections":[{"out":0,"entry":[true],"bearings":[23],"location":[13.420041,52.502334]}],"driving_side":"right","geometry":"qjm_Igb|pAaCyA","mode":"driving","maneuver":{"bearing_after":23,"bearing_before":0,"location":[13.420041,52.502334],"modifier":"left","type":"depart","instruction":"Kör åt nordost på Adalbertstraße"},"weight":18.8,"duration":18.8,"name":"Adalbertstraße","distance":78.5},{"intersections":[{"in":0,"entry":[true],"bearings":[203],"location":[13.420494,52.502984]}],"driving_side":"right","geometry":"snm_Iae|pA","mode":"driving","maneuver":{"bearing_after":0,"bearing_before":23,"location":[13.420494,52.502984],"modifier":"right","type":"arrive","instruction":"Du är framme vid din destination, till höger"},"weight":0,"duration":0,"name":"Adalbertstraße","distance":0}],"distance":78.5}],"weight_name":"routability","weight":121.9,"duration":96.6,"distance":291.6}],"tracepoints":[{"alternatives_count":0,"waypoint_index":0,"matchings_index":0,"name":"","location":[13.418992,52.500626]},{"alternatives_count":0,"waypoint_index":1,"matchings_index":0,"name":"Adalbertstraße","location":[13.419148,52.501096]},{"alternatives_count":0,"waypoint_index":2,"matchings_index":0,"name":"Adalbertstraße","location":[13.41962,52.501755]},{"alternatives_count":0,"waypoint_index":3,"matchings_index":0,"name":"Adalbertstraße","location":[13.420041,52.502334]},{"alternatives_count":1,"waypoint_index":4,"matchings_index":0,"name":"Adalbertstraße","location":[13.420494,52.502984]}],"code":"Ok"} \ No newline at end of file +{"matchings":[{"confidence":0.8849067703299458,"geometry":"}_m_Iu{{pAEZwA{@cC}AqBsAaCyA","legs":[{"summary":"Adalbertstraße","weight":52.5,"duration":39.3,"steps":[{"intersections":[{"classes":["restricted"],"out":0,"entry":[true],"bearings":[291],"location":[13.418992,52.500626]}],"driving_side":"right","geometry":"}_m_Iu{{pA?DET","mode":"driving","maneuver":{"bearing_after":291,"bearing_before":0,"location":[13.418992,52.500626],"modifier":"left","type":"depart","instruction":"Kör åt nordväst"},"weight":33.6,"duration":20.4,"name":"","distance":10.2},{"intersections":[{"out":0,"in":1,"entry":[true,false,true],"bearings":[15,105,210],"location":[13.418852,52.500659]}],"driving_side":"right","geometry":"c`m_Iyz{pAoAu@GE","mode":"driving","maneuver":{"bearing_after":21,"bearing_before":291,"location":[13.418852,52.500659],"modifier":"right","type":"turn","instruction":"Sväng höger in på Adalbertstraße"},"weight":18.9,"duration":18.9,"name":"Adalbertstraße","distance":52.6},{"intersections":[{"in":0,"entry":[true],"bearings":[202],"location":[13.419148,52.501096]}],"driving_side":"right","geometry":"{bm_Iu|{pA","mode":"driving","maneuver":{"bearing_after":0,"bearing_before":22,"location":[13.419148,52.501096],"modifier":"left","type":"arrive","instruction":"Du är framme vid din 1:a destination, till vänster"},"weight":0,"duration":0,"name":"Adalbertstraße","distance":0}],"distance":62.7},{"summary":"Adalbertstraße","weight":30.1,"duration":18.1,"steps":[{"intersections":[{"out":0,"entry":[true],"bearings":[22],"location":[13.419148,52.501096]},{"out":0,"in":2,"entry":[true,true,false,true],"bearings":[30,120,195,300],"location":[13.419277,52.501287]}],"driving_side":"right","geometry":"{bm_Iu|{pAe@Ym@a@o@a@","mode":"driving","maneuver":{"bearing_after":22,"bearing_before":0,"location":[13.419148,52.501096],"modifier":"left","type":"depart","instruction":"Kör åt nordost på Adalbertstraße"},"weight":30.1,"duration":18.1,"name":"Adalbertstraße","distance":80},{"intersections":[{"in":0,"entry":[true],"bearings":[204],"location":[13.41962,52.501755]}],"driving_side":"right","geometry":"_gm_Is_|pA","mode":"driving","maneuver":{"bearing_after":0,"bearing_before":24,"location":[13.41962,52.501755],"modifier":"right","type":"arrive","instruction":"Du är framme vid din 2:a destination, till höger"},"weight":0,"duration":0,"name":"Adalbertstraße","distance":0}],"distance":80},{"summary":"Adalbertstraße","weight":11.2,"duration":11.1,"steps":[{"intersections":[{"out":0,"entry":[true],"bearings":[24],"location":[13.41962,52.501755]},{"out":0,"in":2,"entry":[true,true,false,true],"bearings":[30,120,210,300],"location":[13.419982,52.502249]}],"driving_side":"right","geometry":"_gm_Is_|pAaBgAOK","mode":"driving","maneuver":{"bearing_after":24,"bearing_before":0,"location":[13.41962,52.501755],"modifier":"right","type":"depart","instruction":"Kör åt nordost på Adalbertstraße"},"weight":11.2,"duration":11.100000000000001,"name":"Adalbertstraße","distance":70.4},{"intersections":[{"in":0,"entry":[true],"bearings":[203],"location":[13.420041,52.502334]}],"driving_side":"right","geometry":"qjm_Igb|pA","mode":"driving","maneuver":{"bearing_after":0,"bearing_before":23,"location":[13.420041,52.502334],"modifier":"left","type":"arrive","instruction":"Du är framme vid din 3:e destination, till vänster"},"weight":0,"duration":0,"name":"Adalbertstraße","distance":0}],"distance":70.4},{"summary":"Adalbertstraße","weight":20.2,"duration":20.2,"steps":[{"intersections":[{"out":0,"entry":[true],"bearings":[23],"location":[13.420041,52.502334]}],"driving_side":"right","geometry":"qjm_Igb|pAaCyA","mode":"driving","maneuver":{"bearing_after":23,"bearing_before":0,"location":[13.420041,52.502334],"modifier":"left","type":"depart","instruction":"Kör åt nordost på Adalbertstraße"},"weight":20.2,"duration":20.2,"name":"Adalbertstraße","distance":78.5},{"intersections":[{"in":0,"entry":[true],"bearings":[203],"location":[13.420494,52.502984]}],"driving_side":"right","geometry":"snm_Iae|pA","mode":"driving","maneuver":{"bearing_after":0,"bearing_before":23,"location":[13.420494,52.502984],"modifier":"right","type":"arrive","instruction":"Du är framme vid din destination, till höger"},"weight":0,"duration":0,"name":"Adalbertstraße","distance":0}],"distance":78.5}],"weight_name":"routability","weight":114,"duration":88.7,"distance":291.6}],"tracepoints":[{"alternatives_count":0,"waypoint_index":0,"matchings_index":0,"name":"","location":[13.418992,52.500626]},{"alternatives_count":0,"waypoint_index":1,"matchings_index":0,"name":"Adalbertstraße","location":[13.419148,52.501096]},{"alternatives_count":0,"waypoint_index":2,"matchings_index":0,"name":"Adalbertstraße","location":[13.41962,52.501755]},{"alternatives_count":0,"waypoint_index":3,"matchings_index":0,"name":"Adalbertstraße","location":[13.420041,52.502334]},{"alternatives_count":1,"waypoint_index":4,"matchings_index":0,"name":"Adalbertstraße","location":[13.420494,52.502984]}],"code":"Ok"} \ No newline at end of file