Skip to content

Commit

Permalink
Routable points support for services-geocoding (#1522)
Browse files Browse the repository at this point in the history
  • Loading branch information
DzmitryFomchyn authored Dec 1, 2022
1 parent d112384 commit c7a13df
Show file tree
Hide file tree
Showing 11 changed files with 611 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ public interface GeocodingService {
* @param language The locale in which results should be returned.
* @param reverseMode Set the factors that are used to sort nearby results.
* @param fuzzyMatch Set whether to allow the geocoding API to attempt exact matching or not.
* @param routing Set whether to request additional metadata
* about the recommended navigation destination.
* @return A retrofit Call object
* @since 1.0.0
*/
Expand All @@ -52,7 +54,8 @@ Call<GeocodingResponse> getCall(
@Query("limit") String limit,
@Query("language") String language,
@Query("reverseMode") String reverseMode,
@Query("fuzzyMatch") Boolean fuzzyMatch);
@Query("fuzzyMatch") Boolean fuzzyMatch,
@Query("routing") Boolean routing);

/**
* Constructs the html call using the information passed in through the
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,8 @@ protected Call<GeocodingResponse> initializeCall() {
limit(),
languages(),
reverseMode(),
fuzzyMatch());
fuzzyMatch(),
routing());
}

private Call<List<GeocodingResponse>> getBatchCall() {
Expand Down Expand Up @@ -215,6 +216,9 @@ public Call<List<GeocodingResponse>> cloneBatchCall() {
@Nullable
abstract Boolean fuzzyMatch();

@Nullable
abstract Boolean routing();

@Nullable
abstract String clientAppName();

Expand Down Expand Up @@ -560,6 +564,24 @@ public abstract Builder reverseMode(
*/
public abstract Builder fuzzyMatch(Boolean fuzzyMatch);

/**
* Specify whether to request additional metadata about the recommended navigation destination
* corresponding to the feature (true) or not (false, default).
* Only applicable for address features.
*
* For example, if routing=true the response could include data about a point
* on the road the feature fronts.
* Response features may include an array containing one or more routable points.
* Routable points cannot always be determined. Consuming applications should fall back to
* using the feature's normal geometry for routing if a separate routable point
* is not returned.
*
* @param routing optionally set whether to request
* additional metadata about the recommended navigation destination
* @return this builder for chaining options together
*/
public abstract Builder routing(Boolean routing);

/**
* Required to call when this is being built. If no access token provided,
* {@link ServicesException} will be thrown.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

/**
* Array representing a hierarchy of parents. Each parent includes id, text keys along with
* (if avaliable) a wikidata, short_code, and Maki key.
* (if available) a wikidata, short_code, and Maki key.
*
* @see <a href="https://github.com/mapbox/carmen/blob/master/carmen-geojson.md">Carmen Geojson information</a>
* @see <a href="https://www.mapbox.com/api-documentation/search/#geocoding">Mapbox geocoder documentation</a>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,15 @@ public Point center() {
@SerializedName("matching_place_name")
public abstract String matchingPlaceName();

/**
* An object with the routable points for the {@link CarmenFeature}.
*
* @return an object with the routable points for the {@link CarmenFeature}
*/
@Nullable
@SerializedName("routable_points")
public abstract RoutablePoints routablePoints();

/**
* A string of the IETF language tag of the query's primary language.
*
Expand Down Expand Up @@ -482,6 +491,14 @@ public abstract static class Builder {
*/
public abstract Builder language(@Nullable String language);

/**
* An object with the routable points for the {@link CarmenFeature}.
*
* @param routablePoints an object with the routable points for the {@link CarmenFeature}
* @return this builder for chaining options together
*/
public abstract Builder routablePoints(@Nullable RoutablePoints routablePoints);

/**
* Build a new {@link CarmenFeature} object.
*
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
package com.mapbox.api.geocoding.v5.models;

import androidx.annotation.Nullable;

import com.google.auto.value.AutoValue;
import com.google.gson.Gson;
import com.google.gson.TypeAdapter;
import com.google.gson.annotations.SerializedName;
import com.mapbox.geojson.Point;

/**
* Object representing {@link CarmenFeature}'s routable point.
*/
@AutoValue
public abstract class RoutablePoint {

/**
* A string representing the routable point name.
*
* @return routable point name
*/
@Nullable
@SerializedName("name")
public abstract String name();

/**
* A {@link Point} object which represents the routable point location.
*
* @return a GeoJson {@link Point} which defines the routable point location
*/
@Nullable
public Point coordinate() {
final double[] coordinate = rawCoordinate();
if (coordinate != null && coordinate.length == 2) {
return Point.fromLngLat(coordinate[0], coordinate[1]);
}
return null;
}

// No public access thus, we lessen enforcement on mutability here.
@Nullable
@SerializedName("coordinates")
@SuppressWarnings("mutable")
abstract double[] rawCoordinate();

/**
* Convert current instance values into another Builder to quickly change one or more values.
*
* @return a new instance of {@link Builder}
*/
@SuppressWarnings("unused")
public abstract Builder toBuilder();

/**
* Gson type adapter for parsing Gson to this class.
*
* @param gson the built {@link Gson} object
* @return the type adapter for this class
*/
public static TypeAdapter<RoutablePoint> typeAdapter(Gson gson) {
return new AutoValue_RoutablePoint.GsonTypeAdapter(gson);
}

/**
* This builder can be used to set the values describing the {@link RoutablePoint}.
*/
@AutoValue.Builder
@SuppressWarnings("unused")
public abstract static class Builder {

/**
* A string representing the routable point name.
*
* @param name routable point name
* @return this builder for chaining options together
*/
public abstract Builder name(@Nullable String name);

/**
* Raw coordinates (longitude and latitude, order matters)
* that represent the routable point location.
*
* @param coordinate raw coordinates that represent the routable point location
* @return this builder for chaining options together
*/
public abstract Builder rawCoordinate(@Nullable double[] coordinate);

/**
* Build a new {@link RoutablePoint} object.
*
* @return a new {@link RoutablePoint} using the provided values in this builder
*/
public abstract RoutablePoint build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package com.mapbox.api.geocoding.v5.models;

import androidx.annotation.Nullable;

import com.google.auto.value.AutoValue;
import com.google.gson.Gson;
import com.google.gson.TypeAdapter;
import com.google.gson.annotations.SerializedName;

import java.util.List;

/**
* An object with the routable points for the {@link CarmenFeature}.
*/
@AutoValue
public abstract class RoutablePoints {

/**
* A list of routable points for the {@link CarmenFeature}, or null if no points were found.
*
* @return a list of routable points for the {@link CarmenFeature},
* or null if no points were found
*/
@Nullable
@SerializedName("points")
public abstract List<RoutablePoint> points();

/**
* Convert current instance values into another Builder to quickly change one or more values.
*
* @return a new instance of {@link Builder}
*/
@SuppressWarnings("unused")
public abstract Builder toBuilder();

/**
* Gson type adapter for parsing Gson to this class.
*
* @param gson the built {@link Gson} object
* @return the type adapter for this class
*/
public static TypeAdapter<RoutablePoints> typeAdapter(Gson gson) {
return new AutoValue_RoutablePoints.GsonTypeAdapter(gson);
}

/**
* This builder can be used to set the values describing the {@link RoutablePoints}.
*/
@AutoValue.Builder
@SuppressWarnings("unused")
public abstract static class Builder {

/**
* A list of routable points for the {@link CarmenFeature},
* or null if no points were found.
*
* @param points a list of routable points for the {@link CarmenFeature},
* or null if no points were found
* @return this builder for chaining options together
*/
public abstract Builder points(@Nullable List<RoutablePoint> points);

/**
* Build a new {@link RoutablePoints} object.
*
* @return a new {@link RoutablePoints} using the provided values in this builder
*/
public abstract RoutablePoints build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

import static org.hamcrest.Matchers.startsWith;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;

Expand Down Expand Up @@ -312,6 +313,44 @@ public void fuzzyMatch_getsAddedToUrlCorrectly() {
.contains("fuzzyMatch=true"));
}

@Test
public void routingSanity() throws Exception {
MapboxGeocoding mapboxGeocoding = MapboxGeocoding.builder()
.accessToken(ACCESS_TOKEN)
.query("wahsington")
.routing(true)
.baseUrl(mockUrl.toString())
.build();
assertNotNull(mapboxGeocoding);
Response<GeocodingResponse> response = mapboxGeocoding.executeCall();
assertEquals(200, response.code());
}

@Test
public void routing_defaultIsNotSpecified() {
MapboxGeocoding mapboxGeocoding = MapboxGeocoding.builder()
.accessToken(ACCESS_TOKEN)
.query("wahsington")
.baseUrl(mockUrl.toString())
.build();
assertNotNull(mapboxGeocoding);
assertFalse(mapboxGeocoding.cloneCall().request().url().toString()
.contains("routing="));
}

@Test
public void routing_getsAddedToUrlCorrectly() {
MapboxGeocoding mapboxGeocoding = MapboxGeocoding.builder()
.accessToken(ACCESS_TOKEN)
.query("wahsington")
.routing(true)
.baseUrl(mockUrl.toString())
.build();
assertNotNull(mapboxGeocoding);
assertTrue(mapboxGeocoding.cloneCall().request().url().toString()
.contains("routing=true"));
}

@Test
public void intersectionSearchSanity() throws IOException {
MapboxGeocoding mapboxGeocoding = MapboxGeocoding.builder()
Expand Down Expand Up @@ -382,7 +421,7 @@ public void intersectionSearch_AutoSetGeocodingType() {
}

@Test
public void intersectionSearch_EmptyPromixity() {
public void intersectionSearch_EmptyProximity() {
thrown.expect(ServicesException.class);
thrown.expectMessage(
startsWith("Geocoding proximity must be set for intersection search."));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
public class CarmenContextTest extends GeocodingTestUtils {

@Test
public void sanity() throws Exception {
public void sanity() {
CarmenContext carmenContext = CarmenContext.builder().build();
assertNotNull(carmenContext);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ public class CarmenFeatureTest extends GeocodingTestUtils {
private static final String FORWARD_FEATURE_VALID = "forward_feature_valid.json";

@Test
public void sanity() throws Exception {
public void sanity() {
CarmenFeature carmenFeature = CarmenFeature.builder()
.properties(new JsonObject())
.address("1234")
Expand Down Expand Up @@ -104,6 +104,32 @@ public void fromJson_handlesConversionCorrectly() throws Exception {
assertThat(feature.center().latitude(), equalTo(38.897702));
assertThat(feature.center().longitude(), equalTo(-77.036543));
assertThat(feature.language(), nullValue());
assertThat(feature.routablePoints(), notNullValue());
}

@Test
public void routablePoints_handlesCorrectly() throws IOException {
MapboxGeocoding mapboxGeocoding = MapboxGeocoding.builder()
.accessToken(ACCESS_TOKEN)
.query("1600 pennsylvania ave nw")
.baseUrl(mockUrl.toString())
.build();
GeocodingResponse response = mapboxGeocoding.executeCall().body();
assertNotNull(response);

assertThat(response.features().size(), equalTo(5));

CarmenFeature feature = response.features().get(0);
assertThat(feature.routablePoints(), notNullValue());
assertThat(feature.routablePoints().points(), notNullValue());
assertThat(feature.routablePoints().points().size(), equalTo(1));
assertThat(feature.routablePoints().points().get(0).name(), equalTo("default_routable_point"));
assertThat(feature.routablePoints().points().get(0).coordinate(), equalTo(Point.fromLngLat(-77.036544, 38.897703)));

assertThat(response.features().get(1).routablePoints(), notNullValue());
assertThat(response.features().get(1).routablePoints().points(), nullValue());

assertThat(response.features().get(2).routablePoints(), nullValue());
}

@Test
Expand Down
Loading

0 comments on commit c7a13df

Please sign in to comment.