Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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;
Expand All @@ -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)
Expand All @@ -44,6 +55,7 @@
public abstract class MapboxMapMatching extends
MapboxService<MapMatchingResponse, MapMatchingService> {


protected MapboxMapMatching() {
super(MapMatchingService.class);
}
Expand Down Expand Up @@ -77,6 +89,69 @@ protected Call<MapMatchingResponse> 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<MapMatchingResponse> executeCall() throws IOException {

Response<MapMatchingResponse> 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<MapMatchingResponse> callback) {
getCall().enqueue(new Callback<MapMatchingResponse>() {
@Override
public void onResponse(Call<MapMatchingResponse> call,
Response<MapMatchingResponse> response) {
if (!response.isSuccessful()) {
errorDidOccur(callback, response);
return;
}
callback.onResponse(call, response);
}

@Override
public void onFailure(Call<MapMatchingResponse> call, Throwable throwable) {
callback.onFailure(call, throwable);
}
});
}


private void errorDidOccur(@Nullable Callback<MapMatchingResponse> callback,
@NonNull Response<MapMatchingResponse> response) {
// Response gave an error, we try to LOGGER any messages into the LOGGER here.
Converter<ResponseBody, MapMatchingError> 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();

Expand Down
Original file line number Diff line number Diff line change
@@ -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:
* <ul>
* <li><strong>Ok</strong> Normal case.</li>
* <li><strong>NoMatch</strong> The input did not produce any matches, or the waypoints requested
* were not found in the resulting match. features will be an empty array.</li>
* <li><strong>TooManyCoordinates</strong> There are more than 100 points in the request.</li>
* <li><strong>ProfileNotFound</strong> Needs to be a valid profile. </li>
* <li><strong>InvalidInput</strong>message will hold an explanation of the invalid input.</li>
* </ul>
*
* @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<MapMatchingError> 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:
* <ul>
* <li><strong>Ok</strong> Normal case.</li>
* <li><strong>NoMatch</strong> The input did not produce any matches, or the waypoints
* requested were not found in the resulting match. features will be an empty array.</li>
* <li><strong>TooManyCoordinates</strong> There are more than 100 points in the request.</li>
* <li><strong>ProfileNotFound</strong> Needs to be a valid profile. </li>
* <li><strong>InvalidInput</strong>message will hold an explanation of the invalid input.</li>
* </ul>
*
* @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();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -67,6 +76,18 @@ public static Builder builder() {
@Nullable
public abstract List<MapMatchingTracepoint> 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.
*
Expand Down Expand Up @@ -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.
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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;
Expand All @@ -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;
Expand All @@ -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);
Expand Down Expand Up @@ -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<MapMatchingResponse> response = mapMatching.executeCall();
assertThat(response.body().message(), containsString("Could not find"));
assertThat(response.body().code(), containsString("NoSegment"));
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"code":"NoSegment","message":"Could not find a matching segment for input coordinates","matchings":[]}
Loading