diff --git a/samples/src/main/java/com/mapbox/samples/BasicV6BatchGeocoding.java b/samples/src/main/java/com/mapbox/samples/BasicV6BatchGeocoding.java new file mode 100644 index 000000000..2b62d75a3 --- /dev/null +++ b/samples/src/main/java/com/mapbox/samples/BasicV6BatchGeocoding.java @@ -0,0 +1,93 @@ +package com.mapbox.samples; + +import com.mapbox.api.geocoding.v6.MapboxV6BatchGeocoding; +import com.mapbox.api.geocoding.v6.V6RequestOptions; +import com.mapbox.api.geocoding.v6.V6StructuredInputQuery; +import com.mapbox.api.geocoding.v6.V6ForwardGeocodingRequestOptions; +import com.mapbox.api.geocoding.v6.models.V6BatchResponse; +import com.mapbox.api.geocoding.v6.models.V6Feature; +import com.mapbox.api.geocoding.v6.models.V6FeatureType; +import com.mapbox.api.geocoding.v6.models.V6Response; +import com.mapbox.api.geocoding.v6.V6ReverseGeocodingRequestOptions; +import com.mapbox.geojson.BoundingBox; +import com.mapbox.geojson.Point; +import com.mapbox.sample.BuildConfig; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +import retrofit2.Call; +import retrofit2.Callback; +import retrofit2.Response; + +public class BasicV6BatchGeocoding { + + private static List requestOptions() { + final V6ForwardGeocodingRequestOptions forwardOptions = V6ForwardGeocodingRequestOptions + .builder("1600 Pennsylvania Avenue NW, Washington, DC 20500, United States") + .types(V6FeatureType.ADDRESS) + .limit(1) + .boundingBox(BoundingBox.fromLngLats(-80, 35, -70, 40)) + .build(); + + final V6StructuredInputQuery structuredInputQuery = V6StructuredInputQuery.builder() + .addressNumber("1600") + .street("Pennsylvania Avenue NW") + .postcode("20500") + .place("Washington, DC") + .build(); + + final V6ForwardGeocodingRequestOptions structuredInputOptions = V6ForwardGeocodingRequestOptions + .builder(structuredInputQuery) + .country("us") + .build(); + + final V6ReverseGeocodingRequestOptions reverseOptions = V6ReverseGeocodingRequestOptions + .builder(Point.fromLngLat(-73.986136, 40.748895)) + .types(V6FeatureType.ADDRESS) + .build(); + + return Arrays.asList(forwardOptions, structuredInputOptions, reverseOptions); + } + + public static void main(String[] args) { + final MapboxV6BatchGeocoding geocoding = MapboxV6BatchGeocoding + .builder( + BuildConfig.MAPBOX_ACCESS_TOKEN, + requestOptions() + ) + .build(); + + geocoding.enqueueCall(new Callback() { + @Override + public void onResponse(Call call, Response response) { + System.out.println("Response code: " + response.code()); + System.out.println("Response message: " + response.message()); + + final V6BatchResponse body = response.body(); + if (body == null) { + System.out.println("Response body is null"); + return; + } + + System.out.println("Number of responses: " + body.responses().size()); + + for (V6Response v6Response : body.responses()) { + System.out.println("Number of results: " + v6Response.features().size()); + + final String results = v6Response.features().stream() + .map(V6Feature::toString) + .collect(Collectors.joining(", \n")); + + System.out.println("Features: " + results); + } + } + + @Override + public void onFailure(Call call, Throwable t) { + System.out.println(t); + } + }); + } +} diff --git a/samples/src/main/java/com/mapbox/samples/BasicV6ForwardGeocoding.java b/samples/src/main/java/com/mapbox/samples/BasicV6ForwardGeocoding.java new file mode 100644 index 000000000..78e693242 --- /dev/null +++ b/samples/src/main/java/com/mapbox/samples/BasicV6ForwardGeocoding.java @@ -0,0 +1,52 @@ +package com.mapbox.samples; + +import com.mapbox.api.geocoding.v6.MapboxV6Geocoding; +import com.mapbox.api.geocoding.v6.V6ForwardGeocodingRequestOptions; +import com.mapbox.api.geocoding.v6.models.V6Response; +import com.mapbox.api.geocoding.v6.models.V6Feature; +import com.mapbox.api.geocoding.v6.models.V6FeatureType; +import com.mapbox.sample.BuildConfig; + +import java.util.stream.Collectors; + +import retrofit2.Call; +import retrofit2.Callback; +import retrofit2.Response; + +public class BasicV6ForwardGeocoding { + + public static void main(String[] args) { + final V6ForwardGeocodingRequestOptions requestOptions = V6ForwardGeocodingRequestOptions + .builder("740 15th St NW, Washington, DC 20005") + .types(V6FeatureType.ADDRESS) + .build(); + + final MapboxV6Geocoding geocoding = MapboxV6Geocoding + .builder(BuildConfig.MAPBOX_ACCESS_TOKEN, requestOptions) + .build(); + + geocoding.enqueueCall(new Callback() { + @Override + public void onResponse(Call call, Response response) { + final V6Response body = response.body(); + if (body == null) { + System.out.println("Response body is null"); + return; + } + + System.out.println("Number of results: " + body.features().size()); + + final String results = body.features().stream() + .map(V6Feature::toString) + .collect(Collectors.joining(", \n")); + + System.out.println("Features: " + results); + } + + @Override + public void onFailure(Call call, Throwable t) { + System.out.println(t); + } + }); + } +} diff --git a/samples/src/main/java/com/mapbox/samples/BasicV6ReverseGeocoding.java b/samples/src/main/java/com/mapbox/samples/BasicV6ReverseGeocoding.java new file mode 100644 index 000000000..b7a219af1 --- /dev/null +++ b/samples/src/main/java/com/mapbox/samples/BasicV6ReverseGeocoding.java @@ -0,0 +1,51 @@ +package com.mapbox.samples; + +import com.mapbox.api.geocoding.v6.MapboxV6Geocoding; +import com.mapbox.api.geocoding.v6.models.V6Feature; +import com.mapbox.api.geocoding.v6.models.V6Response; +import com.mapbox.api.geocoding.v6.V6ReverseGeocodingRequestOptions; +import com.mapbox.geojson.Point; +import com.mapbox.sample.BuildConfig; + +import java.util.stream.Collectors; + +import retrofit2.Call; +import retrofit2.Callback; +import retrofit2.Response; + +public class BasicV6ReverseGeocoding { + + public static void main(String[] args) { + final V6ReverseGeocodingRequestOptions requestOptions = V6ReverseGeocodingRequestOptions + .builder(Point.fromLngLat(-77.03397315668123, 38.89991317162873)) + .build(); + + final MapboxV6Geocoding geocoding = MapboxV6Geocoding + .builder(BuildConfig.MAPBOX_ACCESS_TOKEN, requestOptions) + .build(); + + geocoding.enqueueCall(new Callback() { + @Override + public void onResponse(Call call, Response response) { + final V6Response body = response.body(); + if (body == null) { + System.out.println("Response body is null"); + return; + } + + System.out.println("Number of results: " + body.features().size()); + + final String results = body.features().stream() + .map(V6Feature::toString) + .collect(Collectors.joining(", \n")); + + System.out.println("Features: " + results); + } + + @Override + public void onFailure(Call call, Throwable t) { + System.out.println(t); + } + }); + } +} diff --git a/samples/src/main/java/com/mapbox/samples/BasicV6StructuredInputForwardGeocoding.java b/samples/src/main/java/com/mapbox/samples/BasicV6StructuredInputForwardGeocoding.java new file mode 100644 index 000000000..5cd4eaa71 --- /dev/null +++ b/samples/src/main/java/com/mapbox/samples/BasicV6StructuredInputForwardGeocoding.java @@ -0,0 +1,62 @@ +package com.mapbox.samples; + +import com.mapbox.api.geocoding.v6.V6StructuredInputQuery; +import com.mapbox.api.geocoding.v6.MapboxV6Geocoding; +import com.mapbox.api.geocoding.v6.V6ForwardGeocodingRequestOptions; +import com.mapbox.api.geocoding.v6.models.V6Feature; +import com.mapbox.api.geocoding.v6.models.V6FeatureType; +import com.mapbox.api.geocoding.v6.models.V6Response; +import com.mapbox.sample.BuildConfig; + +import java.util.stream.Collectors; + +import retrofit2.Call; +import retrofit2.Callback; +import retrofit2.Response; + +public class BasicV6StructuredInputForwardGeocoding { + + public static void main(String[] args) { + final V6StructuredInputQuery query = V6StructuredInputQuery.builder() + .addressNumber("740") + .street("15th St") + .place("Washington") + .postcode("20005") + .build(); + + final V6ForwardGeocodingRequestOptions requestOptions = V6ForwardGeocodingRequestOptions + .builder(query) + .country("United States") + .types(V6FeatureType.ADDRESS) + .autocomplete(false) + .build(); + + final MapboxV6Geocoding geocoding = MapboxV6Geocoding + .builder(BuildConfig.MAPBOX_ACCESS_TOKEN, requestOptions) + .build(); + + geocoding.enqueueCall(new Callback() { + @Override + public void onResponse(Call call, Response response) { + final V6Response body = response.body(); + if (body == null) { + System.out.println("Response body is null"); + return; + } + + System.out.println("Number of results: " + body.features().size()); + + final String results = body.features().stream() + .map(V6Feature::toString) + .collect(Collectors.joining(", \n")); + + System.out.println("Features: " + results); + } + + @Override + public void onFailure(Call call, Throwable t) { + System.out.println(t); + } + }); + } +} diff --git a/services-geocoding/src/main/java/com/mapbox/api/geocoding/v6/MapboxV6BaseGeocoding.java b/services-geocoding/src/main/java/com/mapbox/api/geocoding/v6/MapboxV6BaseGeocoding.java new file mode 100644 index 000000000..4c0c81b8e --- /dev/null +++ b/services-geocoding/src/main/java/com/mapbox/api/geocoding/v6/MapboxV6BaseGeocoding.java @@ -0,0 +1,66 @@ +package com.mapbox.api.geocoding.v6; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.mapbox.core.MapboxService; + +/** + * Base class for entry points to Mapbox V6 geocoding: forward geocoding, reverse geocoding + * and batch geocoding. See derived classes for more information. + * @param response type. + */ +public abstract class MapboxV6BaseGeocoding extends MapboxService { + + @NonNull + protected abstract String accessToken(); + + @Nullable + protected abstract Boolean permanent(); + + @Nullable + protected abstract String clientAppName(); + + @NonNull + @Override + protected abstract String baseUrl(); + + protected MapboxV6BaseGeocoding() { + super(V6GeocodingService.class); + } + + /** + * Base class for Mapbox V6 Geocoding Builders. + * @param type of Builder + */ + public abstract static class BaseBuilder { + + protected abstract T accessToken(@NonNull String accessToken); + + /** + * Specify whether you intend to store the results of the query. Backend default is false. + * + * @param permanent specify whether you intend to store the results + * @return this builder for chaining options together + * + * @see Storing Geocoding Results + */ + public abstract T permanent(@NonNull Boolean permanent); + + /** + * Base package name or other simple string identifier. Used inside the calls user agent header. + * + * @param clientAppName base package name or other simple string identifier + * @return this builder for chaining options together + */ + public abstract T clientAppName(@NonNull String clientAppName); + + /** + * Optionally change the APIs base URL to something other then the default Mapbox one. + * + * @param baseUrl base url used as end point + * @return this builder for chaining options together + */ + public abstract T baseUrl(@NonNull String baseUrl); + } +} diff --git a/services-geocoding/src/main/java/com/mapbox/api/geocoding/v6/MapboxV6BatchGeocoding.java b/services-geocoding/src/main/java/com/mapbox/api/geocoding/v6/MapboxV6BatchGeocoding.java new file mode 100644 index 000000000..3bc032c38 --- /dev/null +++ b/services-geocoding/src/main/java/com/mapbox/api/geocoding/v6/MapboxV6BatchGeocoding.java @@ -0,0 +1,111 @@ +package com.mapbox.api.geocoding.v6; + +import androidx.annotation.NonNull; + +import com.google.auto.value.AutoValue; +import com.google.gson.GsonBuilder; +import com.mapbox.api.geocoding.v6.models.V6BatchResponse; +import com.mapbox.core.constants.Constants; +import com.mapbox.core.exceptions.ServicesException; +import com.mapbox.core.utils.ApiCallHelper; +import com.mapbox.core.utils.MapboxUtils; +import com.mapbox.geojson.GeometryAdapterFactory; + +import java.util.Arrays; +import java.util.List; + +import okhttp3.RequestBody; +import retrofit2.Call; + +/** + * This class gives you access to Mapbox V6 batch geocoding. + * + * The batch geocoding query type allows you to request up to 1000 forward or reverse geocoding + * queries in a single request. + * + * @see Batch Geocoding + */ +@AutoValue +public abstract class MapboxV6BatchGeocoding extends MapboxV6BaseGeocoding { + + @NonNull + abstract List requestOptions(); + + @Override + protected GsonBuilder getGsonBuilder() { + return new GsonBuilder() + .registerTypeAdapterFactory(V6GeocodingAdapterFactory.create()) + .registerTypeAdapterFactory(GeometryAdapterFactory.create()); + } + + @Override + protected Call initializeCall() { + final String optionsJson = V6GeocodingUtils.serialize(requestOptions()); + + final RequestBody body = RequestBody.create( + optionsJson, + okhttp3.MediaType.parse("application/json; charset=utf-8") + ); + + return getService().batchGeocoding( + ApiCallHelper.getHeaderUserAgent(clientAppName()), + accessToken(), + permanent(), + body + ); + } + + /** + * Creates a new {@link MapboxV6BatchGeocoding.Builder} object. + * + * @param accessToken a valid Mapbox Access Token used for API request + * @param requestOptions options specified for this geocoding request + * @return Builder object. + * @throws ServicesException if accessToken is in incorrect format + */ + public static MapboxV6BatchGeocoding.Builder builder( + @NonNull String accessToken, + @NonNull V6RequestOptions... requestOptions + ) { + return builder(accessToken, Arrays.asList(requestOptions)); + } + + /** + * Creates a new {@link MapboxV6BatchGeocoding.Builder} object. + * + * @param accessToken a valid Mapbox Access Token used for API request + * @param requestOptions options specified for this geocoding request + * @return Builder object. + * @throws ServicesException if accessToken is in incorrect format + */ + public static MapboxV6BatchGeocoding.Builder builder( + @NonNull String accessToken, + @NonNull List requestOptions + ) { + if (!MapboxUtils.isAccessTokenValid(accessToken)) { + throw new ServicesException( + "Using Mapbox Services requires setting a valid access accessToken"); + } + return new AutoValue_MapboxV6BatchGeocoding.Builder() + .accessToken(accessToken) + .requestOptions(requestOptions) + .baseUrl(Constants.BASE_API_URL); + } + + /** + * This builder is used to create a new instance that holds request options + * for the batch geocoding request. + */ + @AutoValue.Builder + public abstract static class Builder extends BaseBuilder { + + abstract Builder requestOptions(@NonNull List requestOptions); + + /** + * Build a new {@link MapboxV6BatchGeocoding} object. + * + * @return a new {@link MapboxV6BatchGeocoding} using the provided values in this builder + */ + public abstract MapboxV6BatchGeocoding build(); + } +} diff --git a/services-geocoding/src/main/java/com/mapbox/api/geocoding/v6/MapboxV6Geocoding.java b/services-geocoding/src/main/java/com/mapbox/api/geocoding/v6/MapboxV6Geocoding.java new file mode 100644 index 000000000..28dc71a81 --- /dev/null +++ b/services-geocoding/src/main/java/com/mapbox/api/geocoding/v6/MapboxV6Geocoding.java @@ -0,0 +1,178 @@ +package com.mapbox.api.geocoding.v6; + +import androidx.annotation.NonNull; + +import com.google.auto.value.AutoValue; +import com.google.gson.GsonBuilder; +import com.mapbox.api.geocoding.v6.models.V6Response; +import com.mapbox.core.constants.Constants; +import com.mapbox.core.exceptions.ServicesException; +import com.mapbox.core.utils.ApiCallHelper; +import com.mapbox.core.utils.MapboxUtils; +import com.mapbox.geojson.GeometryAdapterFactory; +import com.mapbox.geojson.Point; + +import retrofit2.Call; + +/** + * This class gives you access to Mapbox forward and reverse V6 geocoding. + *

+ * Forward geocoding lets you convert location text into geographic coordinates, turning + * {@code 2 Lincoln Memorial Circle NW} into a {@link Point} with the coordinates + * {@code -77.050, 38.889}. + *

+ * The reverse geocoding query type allows you to look up a pair of coordinates and returns the + * geographic features there, including a standardized address or place and full geographic context. + * + * @see Forward Geocoding + * @see Reverse Geocoding + */ +@AutoValue +public abstract class MapboxV6Geocoding extends MapboxV6BaseGeocoding { + + @NonNull + abstract V6RequestOptions requestOptions(); + + @Override + protected GsonBuilder getGsonBuilder() { + return new GsonBuilder() + .registerTypeAdapterFactory(V6GeocodingAdapterFactory.create()) + .registerTypeAdapterFactory(GeometryAdapterFactory.create()); + } + + @Override + protected Call initializeCall() { + final V6RequestOptions requestOptions = requestOptions(); + if (requestOptions instanceof V6ReverseGeocodingRequestOptions) { + return reverseGeocodingCall((V6ReverseGeocodingRequestOptions) requestOptions); + } else if (requestOptions instanceof V6ForwardGeocodingRequestOptions) { + final V6ForwardGeocodingRequestOptions forwardOptions = + (V6ForwardGeocodingRequestOptions) requestOptions; + if (forwardOptions.query() != null) { + return forwardGeocodingCall(forwardOptions, forwardOptions.query()); + } else { + return structuredInputCall(forwardOptions); + } + } else { + throw new ServicesException("Unsupported type of request options: " + requestOptions + .getClass()); + } + } + + @NonNull + private Call forwardGeocodingCall( + @NonNull V6ForwardGeocodingRequestOptions options, + @NonNull String query + ) { + return getService().forwardGeocoding( + ApiCallHelper.getHeaderUserAgent(clientAppName()), + query, + accessToken(), + permanent(), + options.autocomplete(), + options.apiFormattedBbox(), + options.country(), + options.language(), + options.limit(), + options.proximity(), + options.apiFormattedTypes(), + options.worldview() + ); + } + + @NonNull + private Call structuredInputCall( + @NonNull V6ForwardGeocodingRequestOptions options + ) { + return getService().structureInputForwardGeocoding( + ApiCallHelper.getHeaderUserAgent(clientAppName()), + accessToken(), + options.addressLine1(), + options.addressNumber(), + options.street(), + options.block(), + options.place(), + options.region(), + options.postcode(), + options.locality(), + options.neighborhood(), + permanent(), + options.autocomplete(), + options.apiFormattedBbox(), + options.country(), + options.language(), + options.limit(), + options.proximity(), + options.apiFormattedTypes(), + options.worldview() + ); + } + + @NonNull + private Call reverseGeocodingCall( + @NonNull V6ReverseGeocodingRequestOptions options + ) { + return getService().reverseGeocoding( + ApiCallHelper.getHeaderUserAgent(clientAppName()), + accessToken(), + options.longitude(), + options.latitude(), + permanent(), + options.country(), + options.language(), + options.limit(), + options.apiFormattedTypes(), + options.worldview() + ); + } + + /** + * Creates a new {@link MapboxV6Geocoding.Builder} object. + * + * @param accessToken a valid Mapbox Access Token used for API request + * @param requestOptions options specified for this geocoding request + * @return Builder object. + * @throws ServicesException if accessToken is in incorrect format + * or passed requestOptions have unsupported type + */ + public static MapboxV6Geocoding.Builder builder( + @NonNull String accessToken, + @NonNull V6RequestOptions requestOptions + ) { + if (!MapboxUtils.isAccessTokenValid(accessToken)) { + throw new ServicesException( + "Using Mapbox Services requires setting a valid access accessToken"); + } + + if ( + !(requestOptions instanceof V6ForwardGeocodingRequestOptions) + && !(requestOptions instanceof V6ReverseGeocodingRequestOptions) + ) { + throw new ServicesException( + "Unsupported type of request options: " + requestOptions.getClass() + ); + } + + return new AutoValue_MapboxV6Geocoding.Builder() + .accessToken(accessToken) + .baseUrl(Constants.BASE_API_URL) + .requestOptions(requestOptions); + } + + /** + * This builder is used to create a new instance that holds request options + * for the forward geocoding request. + */ + @AutoValue.Builder + public abstract static class Builder extends BaseBuilder { + + abstract Builder requestOptions(@NonNull V6RequestOptions requestOptions); + + /** + * Build a new {@link MapboxV6Geocoding} object. + * + * @return a new {@link MapboxV6Geocoding} using the provided values in this builder + */ + public abstract MapboxV6Geocoding build(); + } +} diff --git a/services-geocoding/src/main/java/com/mapbox/api/geocoding/v6/V6ForwardGeocodingRequestOptions.java b/services-geocoding/src/main/java/com/mapbox/api/geocoding/v6/V6ForwardGeocodingRequestOptions.java new file mode 100644 index 000000000..8037b47c4 --- /dev/null +++ b/services-geocoding/src/main/java/com/mapbox/api/geocoding/v6/V6ForwardGeocodingRequestOptions.java @@ -0,0 +1,372 @@ +package com.mapbox.api.geocoding.v6; + +import androidx.annotation.IntRange; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.google.auto.value.AutoValue; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.TypeAdapter; +import com.google.gson.annotations.SerializedName; +import com.mapbox.api.geocoding.v6.models.V6FeatureType; +import com.mapbox.api.geocoding.v6.models.V6Worldview; +import com.mapbox.core.exceptions.ServicesException; +import com.mapbox.core.utils.TextUtils; +import com.mapbox.geojson.BoundingBox; +import com.mapbox.geojson.GeometryAdapterFactory; +import com.mapbox.geojson.Point; + +import java.util.Arrays; +import java.util.List; +import java.util.Locale; + +/** + * Request parameters used to refine the results of a V6 forward geocoding query. + * + * @see Forward geocoding + */ +@AutoValue +public abstract class V6ForwardGeocodingRequestOptions implements V6RequestOptions { + + @SerializedName("q") + @Nullable + abstract String query(); + + @SerializedName("address_line1") + @Nullable + abstract String addressLine1(); + + @SerializedName("address_number") + @Nullable + abstract String addressNumber(); + + @SerializedName("street") + @Nullable + abstract String street(); + + @SerializedName("block") + @Nullable + abstract String block(); + + @SerializedName("place") + @Nullable + abstract String place(); + + @SerializedName("region") + @Nullable + abstract String region(); + + @SerializedName("postcode") + @Nullable + abstract String postcode(); + + @SerializedName("locality") + @Nullable + abstract String locality(); + + @SerializedName("neighborhood") + @Nullable + abstract String neighborhood(); + + @SerializedName("autocomplete") + @Nullable + abstract Boolean autocomplete(); + + @SerializedName("bbox") + @Nullable + abstract List bbox(); + + String apiFormattedBbox() { + final List bbox = bbox(); + if (bbox != null && bbox.size() == 4) { + return String.format(Locale.US, "%s,%s,%s,%s", + TextUtils.formatCoordinate(bbox.get(0)), + TextUtils.formatCoordinate(bbox.get(1)), + TextUtils.formatCoordinate(bbox.get(2)), + TextUtils.formatCoordinate(bbox.get(3)) + ); + } else { + return null; + } + } + + @SerializedName("country") + @Nullable + abstract String country(); + + @SerializedName("language") + @Nullable + abstract String language(); + + @SerializedName("limit") + @Nullable + abstract Integer limit(); + + @SerializedName("proximity") + @Nullable + abstract String proximity(); + + @SerializedName("types") + @Nullable + abstract List types(); + + @Nullable + String apiFormattedTypes() { + final List types = types(); + if (types != null) { + return TextUtils.join(",", types.toArray()); + } else { + return null; + } + } + + @SerializedName("worldview") + @Nullable + abstract String worldview(); + + /** + * Creates a new {@link V6ForwardGeocodingRequestOptions.Builder} object with query parameter. + * + * @param query The feature you're trying to look up. This could be an address, a city name, etc. + * @return Builder object. + * @throws ServicesException if query is empty + */ + public static V6ForwardGeocodingRequestOptions.Builder builder(@NonNull String query) { + if (query.isEmpty()) { + throw new ServicesException("Search query can't be empty"); + } + return new $AutoValue_V6ForwardGeocodingRequestOptions.Builder() + .query(query); + } + + /** + * Creates a new {@link V6ForwardGeocodingRequestOptions.Builder} object with + * structured input query parameter. + * + * @param query structured input query. + * @return Builder object. + */ + public static V6ForwardGeocodingRequestOptions.Builder builder( + @NonNull V6StructuredInputQuery query + ) { + return new $AutoValue_V6ForwardGeocodingRequestOptions.Builder() + .addressLine1(query.addressLine1()) + .addressNumber(query.addressNumber()) + .street(query.street()) + .block(query.block()) + .place(query.place()) + .region(query.region()) + .postcode(query.postcode()) + .locality(query.locality()) + .neighborhood(query.neighborhood()); + } + + /** + * 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 typeAdapter(Gson gson) { + return new AutoValue_V6ForwardGeocodingRequestOptions.GsonTypeAdapter(gson); + } + + @NonNull + String toJson() { + final Gson gson = new GsonBuilder() + .registerTypeAdapterFactory(GeometryAdapterFactory.create()) + .registerTypeAdapterFactory(V6GeocodingAdapterFactory.create()) + .create(); + return gson.toJson(this, V6ForwardGeocodingRequestOptions.class); + } + + /** + * This builder is used to create a new instance that holds request options + * for the forward geocoding request. + */ + @AutoValue.Builder + public abstract static class Builder { + + abstract Builder query(@NonNull String query); + + // Structured input options + abstract Builder addressLine1(String addressLine1); + + abstract Builder addressNumber(String addressNumber); + + abstract Builder street(String street); + + abstract Builder block(String block); + + abstract Builder place(String place); + + abstract Builder region(String region); + + abstract Builder postcode(String postcode); + + abstract Builder locality(String locality); + + abstract Builder neighborhood(String neighborhood); + + /** + * Specify whether to return autocomplete results (true by default). + * When autocomplete is enabled, results will be included that start with the requested string, + * rather than just responses that match it exactly. + * For example, a query for India might return both India and Indiana with autocomplete enabled, + * but only India if it's disabled. + *

+ * When autocomplete is enabled, each user keystroke counts as one request to the Geocoding API. + * For example, a search for "Cali" would be reflected as four separate Geocoding API requests. + * To reduce the total requests sent, you can configure your application to only call the + * Geocoding API after a specific number of characters are typed. + * + * @param autocomplete optionally set whether to return autocomplete results + * @return this builder for chaining options together + */ + public abstract Builder autocomplete(@Nullable Boolean autocomplete); + + /** + * Limit results to only those contained within the supplied bounding box. + * The bounding box cannot cross the 180th meridian. + * + * @param boundingBox the bounding box + * @return this builder for chaining options together + */ + public Builder boundingBox(@NonNull BoundingBox boundingBox) { + return bbox( + Arrays.asList( + boundingBox.southwest().longitude(), + boundingBox.southwest().latitude(), + boundingBox.northeast().longitude(), + boundingBox.northeast().latitude() + ) + ); + } + + abstract Builder bbox(List bbox); + + /** + * Limit results to one or more country. + * Each country can be represented as: + * - ISO 3166 alpha 2 country codes + * - Generally recognized country name + * - In some cases like Hong Kong, an area of quasi-national administrative status + * that has been given a designated country code under ISO 3166-1. + * + * @param country limit geocoding results to one or more country + * @return this builder for chaining options together + */ + public Builder country(@NonNull String... country) { + return country(TextUtils.join(",", country)); + } + + abstract Builder country(String country); + + /** + * Set the language of the text supplied in responses. Also affects result scoring, + * with results matching the user's query in the requested language being preferred over results + * that match in another language. For example, an autocomplete query for things that start with + * Frank might return Frankfurt as the first result with an English (en) language parameter, + * but Frankreich ("France") with a German (de) language parameter. + *

+ * Options are IETF language tags + * comprised of a mandatory + * ISO 639-1 language code + * and, optionally, one or more IETF subtags for country or script. + * + * @param language language of the text supplied in responses + * @return this builder for chaining options together + */ + public Builder language(Locale language) { + return language(language.getLanguage()); + } + + /** + * Set the language of the text supplied in responses. Also affects result scoring, + * with results matching the user's query in the requested language being preferred over results + * that match in another language. For example, an autocomplete query for things that start with + * Frank might return Frankfurt as the first result with an English (en) language parameter, + * but Frankreich ("France") with a German (de) language parameter. + *

+ * Options are IETF language tags + * comprised of a mandatory + * ISO 639-1 language code + * and, optionally, one or more IETF subtags for country or script. + * + * @param language language of the text supplied in responses + * @return this builder for chaining options together + */ + public abstract Builder language(String language); + + /** + * Specify the maximum number of results to return. + * The default is 5 and the maximum supported is 10. + * + * @param limit the number of returned results + * @return this builder for chaining options together + */ + public abstract Builder limit(@IntRange(from = 1, to = 10) Integer limit); + + /** + * Bias the response to favor results that are closer to the location that is retrieved + * by reverse IP lookup. + * + * @return this builder for chaining options together + */ + public Builder withIpAsProximity() { + return proximity("ip"); + } + + /** + * Bias the response to favor results that are closer to this location. + * + * @param proximity a point to bias the response + * @return this builder for chaining options together + */ + public Builder proximity(@NonNull Point proximity) { + return proximity(String.format( + Locale.US, + "%s,%s", + TextUtils.formatCoordinate(proximity.longitude()), + TextUtils.formatCoordinate(proximity.latitude()) + )); + } + + abstract Builder proximity(@NonNull String proximity); + + /** + * Filter results to include only a subset (one or more) of the available feature types. + * Only values defined in {@link V6FeatureType.FeatureType} are allowed. + * + * @param types filter results to include only a subset of the available feature types + * @return this builder for chaining options together + * @see Geographic Feature Types + */ + public Builder types(@NonNull @V6FeatureType.FeatureType String... types) { + return types(Arrays.asList(types)); + } + + abstract Builder types(List types); + + /** + * Returns features that are defined differently by audiences that belong to various regional, + * cultural, or political groups. + *

+ * Available worldviews are defined in {@link com.mapbox.api.geocoding.v6.models.V6Worldview}. + * If worldview is not set, the us worldview boundaries are returned by default. + * + * @param worldview worldview code + * @return this builder for chaining options together + * @see Worldviews + */ + public abstract Builder worldview(@V6Worldview.Worldview @NonNull String worldview); + + /** + * Build a new {@link V6ForwardGeocodingRequestOptions} object. + * + * @return a new {@link V6ForwardGeocodingRequestOptions} + */ + public abstract V6ForwardGeocodingRequestOptions build(); + } +} diff --git a/services-geocoding/src/main/java/com/mapbox/api/geocoding/v6/V6GeocodingAdapterFactory.java b/services-geocoding/src/main/java/com/mapbox/api/geocoding/v6/V6GeocodingAdapterFactory.java new file mode 100644 index 000000000..dcceb0da9 --- /dev/null +++ b/services-geocoding/src/main/java/com/mapbox/api/geocoding/v6/V6GeocodingAdapterFactory.java @@ -0,0 +1,21 @@ +package com.mapbox.api.geocoding.v6; + +import com.google.gson.TypeAdapterFactory; +import com.mapbox.auto.value.gson.GsonTypeAdapterFactory; + +/** + * A Geocoding type adapter factory for convenience when using AutoValue and handling + * serialization/deserialization. The majority of this class gets generated during compilation time. + */ +@GsonTypeAdapterFactory +public abstract class V6GeocodingAdapterFactory implements TypeAdapterFactory { + + /** + * Create a new instance of this type adapter factory. + * + * @return a new GSON TypeAdapterFactory + */ + public static TypeAdapterFactory create() { + return new AutoValueGson_V6GeocodingAdapterFactory(); + } +} diff --git a/services-geocoding/src/main/java/com/mapbox/api/geocoding/v6/V6GeocodingService.java b/services-geocoding/src/main/java/com/mapbox/api/geocoding/v6/V6GeocodingService.java new file mode 100644 index 000000000..29c4b0b28 --- /dev/null +++ b/services-geocoding/src/main/java/com/mapbox/api/geocoding/v6/V6GeocodingService.java @@ -0,0 +1,146 @@ +package com.mapbox.api.geocoding.v6; + +import com.mapbox.api.geocoding.v6.models.V6BatchResponse; +import com.mapbox.api.geocoding.v6.models.V6Response; + +import okhttp3.RequestBody; +import retrofit2.Call; +import retrofit2.http.Body; +import retrofit2.http.GET; +import retrofit2.http.Header; +import retrofit2.http.POST; +import retrofit2.http.Query; + +/** + * Forward geocoding HTTP interface. + */ +interface V6GeocodingService { + + /** + * Constructs the http call to the /forward endpoint. + * + * @param userAgent the user agent + * @param query {@link V6ForwardGeocodingRequestOptions#query()} + * @param accessToken {@link MapboxV6Geocoding#accessToken()} + * @param permanent {@link MapboxV6Geocoding#permanent()} + * @param autocomplete {@link V6ForwardGeocodingRequestOptions#autocomplete()} + * @param bbox {@link V6ForwardGeocodingRequestOptions#bbox()} + * @param country {@link V6ForwardGeocodingRequestOptions#country()} + * @param language {@link V6ForwardGeocodingRequestOptions#language()} + * @param limit {@link V6ForwardGeocodingRequestOptions#limit()} + * @param proximity {@link V6ForwardGeocodingRequestOptions#proximity()} + * @param types {@link V6ForwardGeocodingRequestOptions#types()} + * @param worldview {@link V6ForwardGeocodingRequestOptions#worldview()} + * @return A retrofit Call object + */ + @GET("/search/geocode/v6/forward") + Call forwardGeocoding( + @Header("User-Agent") String userAgent, + @Query("q") String query, + @Query("access_token") String accessToken, + @Query("permanent") Boolean permanent, + @Query("autocomplete") Boolean autocomplete, + @Query("bbox") String bbox, + @Query("country") String country, + @Query("language") String language, + @Query("limit") Integer limit, + @Query("proximity") String proximity, + @Query("types") String types, + @Query("worldview") String worldview + ); + + /** + * Constructs the http call to the /forward endpoint. + * + * @param userAgent the user agent + * @param accessToken {@link MapboxV6Geocoding#accessToken()} + * @param addressLine1 {@link V6StructuredInputQuery#addressLine1()} + * @param addressNumber {@link V6StructuredInputQuery#addressNumber()} + * @param street {@link V6StructuredInputQuery#street()} + * @param block {@link V6StructuredInputQuery#block()} + * @param place {@link V6StructuredInputQuery#place()} + * @param region {@link V6StructuredInputQuery#region()} + * @param postcode {@link V6StructuredInputQuery#postcode()} + * @param locality {@link V6StructuredInputQuery#locality()} + * @param neighborhood {@link V6StructuredInputQuery#neighborhood()} + * @param permanent {@link MapboxV6Geocoding#permanent()} + * @param autocomplete {@link V6ForwardGeocodingRequestOptions#autocomplete()} + * @param bbox {@link V6ForwardGeocodingRequestOptions#bbox()} + * @param country {@link V6ForwardGeocodingRequestOptions#country()} + * @param language {@link V6ForwardGeocodingRequestOptions#language()} + * @param limit {@link V6ForwardGeocodingRequestOptions#limit()} + * @param proximity {@link V6ForwardGeocodingRequestOptions#proximity()} + * @param types {@link V6ForwardGeocodingRequestOptions#types()} + * @param worldview {@link V6ForwardGeocodingRequestOptions#worldview()} + * @return A retrofit Call object + */ + @GET("/search/geocode/v6/forward") + Call structureInputForwardGeocoding( + @Header("User-Agent") String userAgent, + @Query("access_token") String accessToken, + @Query("address_line1") String addressLine1, + @Query("address_number") String addressNumber, + @Query("street") String street, + @Query("block") String block, + @Query("place") String place, + @Query("region") String region, + @Query("postcode") String postcode, + @Query("locality") String locality, + @Query("neighborhood") String neighborhood, + @Query("permanent") Boolean permanent, + @Query("autocomplete") Boolean autocomplete, + @Query("bbox") String bbox, + @Query("country") String country, + @Query("language") String language, + @Query("limit") Integer limit, + @Query("proximity") String proximity, + @Query("types") String types, + @Query("worldview") String worldview + ); + + /** + * Constructs the html call to the /reverse endpoint. + * + * @param userAgent the user agent + * @param accessToken {@link MapboxV6Geocoding#accessToken()} + * @param longitude {@link V6ReverseGeocodingRequestOptions#longitude()} + * @param latitude {@link V6ReverseGeocodingRequestOptions#latitude()} + * @param permanent {@link MapboxV6Geocoding#permanent()} + * @param country {@link V6ReverseGeocodingRequestOptions#country()} + * @param language {@link V6ReverseGeocodingRequestOptions#language()} + * @param limit {@link V6ReverseGeocodingRequestOptions#limit()} + * @param types {@link V6ReverseGeocodingRequestOptions#types()} + * @param worldview {@link V6ReverseGeocodingRequestOptions#worldview()} + * @return A retrofit Call object + */ + @GET("/search/geocode/v6/reverse") + Call reverseGeocoding( + @Header("User-Agent") String userAgent, + @Query("access_token") String accessToken, + @Query("longitude") Double longitude, + @Query("latitude") Double latitude, + @Query("permanent") Boolean permanent, + @Query("country") String country, + @Query("language") String language, + @Query("limit") Integer limit, + @Query("types") String types, + @Query("worldview") String worldview + ); + + /** + * Constructs the html call to the /batch endpoint. + * + * @param userAgent the user agent + * @param accessToken {@link MapboxV6BatchGeocoding#accessToken()} + * @param permanent {@link MapboxV6BatchGeocoding#permanent()} + * @param body json serialized {@link MapboxV6BatchGeocoding#requestOptions()} + * @return A retrofit Call object + */ + @POST("/search/geocode/v6/batch") + Call batchGeocoding( + @Header("User-Agent") String userAgent, + @Query("access_token") String accessToken, + @Query("permanent") Boolean permanent, + @Body RequestBody body + ); +} diff --git a/services-geocoding/src/main/java/com/mapbox/api/geocoding/v6/V6GeocodingUtils.java b/services-geocoding/src/main/java/com/mapbox/api/geocoding/v6/V6GeocodingUtils.java new file mode 100644 index 000000000..3601a27ab --- /dev/null +++ b/services-geocoding/src/main/java/com/mapbox/api/geocoding/v6/V6GeocodingUtils.java @@ -0,0 +1,27 @@ +package com.mapbox.api.geocoding.v6; + +import androidx.annotation.NonNull; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.mapbox.geojson.GeometryAdapterFactory; + +import java.util.Arrays; +import java.util.Collection; + +class V6GeocodingUtils { + + @NonNull + public static String serialize(@NonNull V6RequestOptions... requestOptions) { + return serialize(Arrays.asList(requestOptions)); + } + + @NonNull + public static String serialize(@NonNull Collection requestOptions) { + final Gson gson = new GsonBuilder() + .registerTypeAdapterFactory(GeometryAdapterFactory.create()) + .registerTypeAdapterFactory(V6GeocodingAdapterFactory.create()) + .create(); + return gson.toJson(requestOptions); + } +} diff --git a/services-geocoding/src/main/java/com/mapbox/api/geocoding/v6/V6RequestOptions.java b/services-geocoding/src/main/java/com/mapbox/api/geocoding/v6/V6RequestOptions.java new file mode 100644 index 000000000..cbe4569ef --- /dev/null +++ b/services-geocoding/src/main/java/com/mapbox/api/geocoding/v6/V6RequestOptions.java @@ -0,0 +1,7 @@ +package com.mapbox.api.geocoding.v6; + +/** + * Marker interface for V6 request options. + */ +public interface V6RequestOptions { +} diff --git a/services-geocoding/src/main/java/com/mapbox/api/geocoding/v6/V6ReverseGeocodingRequestOptions.java b/services-geocoding/src/main/java/com/mapbox/api/geocoding/v6/V6ReverseGeocodingRequestOptions.java new file mode 100644 index 000000000..8fc699edb --- /dev/null +++ b/services-geocoding/src/main/java/com/mapbox/api/geocoding/v6/V6ReverseGeocodingRequestOptions.java @@ -0,0 +1,211 @@ +package com.mapbox.api.geocoding.v6; + +import androidx.annotation.IntRange; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.google.auto.value.AutoValue; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.TypeAdapter; +import com.google.gson.annotations.SerializedName; +import com.mapbox.api.geocoding.v6.models.V6FeatureType; +import com.mapbox.api.geocoding.v6.models.V6Worldview; +import com.mapbox.core.utils.TextUtils; +import com.mapbox.geojson.GeometryAdapterFactory; +import com.mapbox.geojson.Point; + +import java.util.Arrays; +import java.util.List; +import java.util.Locale; + +/** + * Request parameters used to refine the results of a V6 a reverse geocoding query. + * + * @see Reverse geocoding + */ +@AutoValue +public abstract class V6ReverseGeocodingRequestOptions implements V6RequestOptions { + + @SerializedName("longitude") + @NonNull + abstract Double longitude(); + + @SerializedName("latitude") + @NonNull + abstract Double latitude(); + + @SerializedName("country") + @Nullable + abstract String country(); + + @SerializedName("language") + @Nullable + abstract String language(); + + @SerializedName("limit") + @Nullable + abstract Integer limit(); + + @SerializedName("types") + @Nullable + abstract List types(); + + @Nullable + String apiFormattedTypes() { + final List types = types(); + if (types != null) { + return TextUtils.join(",", types.toArray()); + } else { + return null; + } + } + + @SerializedName("worldview") + @Nullable + abstract String worldview(); + + /** + * Creates a new {@link Builder} object. + * + * @param point geographic coordinate for the location being queried + * @return Builder object. + */ + public static Builder builder(@NonNull Point point) { + return new $AutoValue_V6ReverseGeocodingRequestOptions.Builder() + .longitude(point.longitude()) + .latitude(point.latitude()); + } + + /** + * 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 typeAdapter(Gson gson) { + return new AutoValue_V6ReverseGeocodingRequestOptions.GsonTypeAdapter(gson); + } + + @NonNull + String toJson() { + final Gson gson = new GsonBuilder() + .registerTypeAdapterFactory(GeometryAdapterFactory.create()) + .registerTypeAdapterFactory(V6GeocodingAdapterFactory.create()) + .create(); + return gson.toJson(this, V6ReverseGeocodingRequestOptions.class); + } + + /** + * This builder is used to create a new instance that holds request options + * for the reverse geocoding request. + */ + @AutoValue.Builder + public abstract static class Builder { + + abstract Builder longitude(@NonNull Double longitude); + + abstract Builder latitude(@NonNull Double latitude); + + /** + * Limit results to one or more country. + * Permitted values are + * ISO 3166 alpha 2 country codes. + * + * @param country limit geocoding results to one or more country + * @return this builder for chaining options together + */ + public Builder country(@NonNull String... country) { + return country(TextUtils.join(",", country)); + } + + abstract Builder country(String country); + + /** + * Set the language of the text supplied in responses. Also affects result scoring, + * with results matching the user's query in the requested language being preferred over + * results that match in another language. For example, an autocomplete query for things + * that start with Frank might return Frankfurt as the first result with an English (en) + * language parameter, but Frankreich ("France") with a German (de) language parameter. + * + * Options are IETF language tags + * comprised of a mandatory + * ISO 639-1 language code + * and, optionally, one or more IETF subtags for country or script. + * + * @param language language of the text supplied in responses + * @return this builder for chaining options together + */ + public Builder language(Locale language) { + return language(language.getLanguage()); + } + + /** + * Set the language of the text supplied in responses. Also affects result scoring, + * with results matching the user's query in the requested language being preferred over + * results that match in another language. For example, an autocomplete query for things + * that start with Frank might return Frankfurt as the first result with an English (en) + * language parameter, but Frankreich ("France") with a German (de) language parameter. + * + * Options are IETF language tags + * comprised of a mandatory + * ISO 639-1 language code + * and, optionally, one or more IETF subtags for country or script. + * + * @param language language of the text supplied in responses + * @return this builder for chaining options together + */ + public abstract Builder language(String language); + + /** + * Specify the maximum number of results to return. + * The default is 1 and the maximum supported is 5. + * + * The default behavior in reverse geocoding is to return at most one feature at each + * of the multiple levels of the administrative hierarchy + * (for example, one address, one region, one country). + * + * Increasing the limit allows returning multiple features of the same type, + * but only for one type (for example, multiple address results). Consequently, setting + * limit to a higher-than-default value requires specifying exactly one types parameter. + * + * @param limit the number of returned results + * @return this builder for chaining options together + */ + public abstract Builder limit(@IntRange(from = 1, to = 5) Integer limit); + + /** + * Filter results to include only a subset (one or more) of the available feature types. + * Only values defined in {@link V6FeatureType.FeatureType} are allowed. + * + * @param types filter results to include only a subset of the available feature types + * @return this builder for chaining options together + * @see Geographic Feature Types + */ + public Builder types(@NonNull @V6FeatureType.FeatureType String... types) { + return types(Arrays.asList(types)); + } + + abstract Builder types(List types); + + /** + * Returns features that are defined differently by audiences that belong to various regional, + * cultural, or political groups. + * + * Available worldviews are defined in {@link com.mapbox.api.geocoding.v6.models.V6Worldview}. + * If worldview is not set, the us worldview boundaries are returned by default. + * + * @param worldview worldview code + * @return this builder for chaining options together + * @see Worldviews + */ + public abstract Builder worldview(@V6Worldview.Worldview @NonNull String worldview); + + /** + * Build a new {@link V6ReverseGeocodingRequestOptions} object. + * + * @return a new {@link V6ReverseGeocodingRequestOptions} + */ + public abstract V6ReverseGeocodingRequestOptions build(); + } +} diff --git a/services-geocoding/src/main/java/com/mapbox/api/geocoding/v6/V6StructuredInputQuery.java b/services-geocoding/src/main/java/com/mapbox/api/geocoding/v6/V6StructuredInputQuery.java new file mode 100644 index 000000000..cbabfcb00 --- /dev/null +++ b/services-geocoding/src/main/java/com/mapbox/api/geocoding/v6/V6StructuredInputQuery.java @@ -0,0 +1,179 @@ +package com.mapbox.api.geocoding.v6; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.google.auto.value.AutoValue; +import com.google.gson.annotations.SerializedName; +import com.mapbox.core.exceptions.ServicesException; + +/** + * Structured Input is a type of Forward geocoding search that allows you to define the + * feature type of each element of the search query by type. This can increase the accuracy of + * results for well-formed datasets. + * + * Structured input replaces string query parameter in the forward geocoding. + * + * @see Structured input + */ +@AutoValue +public abstract class V6StructuredInputQuery { + + @SerializedName("address_line1") + @Nullable + abstract String addressLine1(); + + @SerializedName("address_number") + @Nullable + abstract String addressNumber(); + + @SerializedName("street") + @Nullable + abstract String street(); + + @SerializedName("block") + @Nullable + abstract String block(); + + @SerializedName("place") + @Nullable + abstract String place(); + + @SerializedName("region") + @Nullable + abstract String region(); + + @SerializedName("postcode") + @Nullable + abstract String postcode(); + + @SerializedName("locality") + @Nullable + abstract String locality(); + + @SerializedName("neighborhood") + @Nullable + abstract String neighborhood(); + + /** + * Creates a new {@link V6StructuredInputQuery.Builder} object. + * @return Builder object. + */ + public static V6StructuredInputQuery.Builder builder() { + return new AutoValue_V6StructuredInputQuery.Builder(); + } + + /** + * This builder is used to create a new instance that holds structured input request options. + */ + @AutoValue.Builder + public abstract static class Builder { + + /** + * A string including address_number and street. These values can be provided as separate + * parameters address_number and street listed below. + * + * @param addressLine1 structured input component. + * @return this builder for chaining options together + */ + public abstract Builder addressLine1(@NonNull String addressLine1); + + /** + * The number associated with the house. + * + * @param addressNumber structured input component. + * @return this builder for chaining options together + */ + public abstract Builder addressNumber(@NonNull String addressNumber); + + /** + * The name of the street in the address. + * + * @param street structured input component. + * @return this builder for chaining options together + */ + public abstract Builder street(@NonNull String street); + + /** + * In some countries like Japan, the block is a component in the address. + * + * @param block structured input component. + * @return this builder for chaining options together + */ + public abstract Builder block(@NonNull String block); + + /** + * Typically these are cities, villages, municipalities, etc. + * They are usually features used in postal addressing, and are suitable for display in ambient + * end-user applications where current-location context is needed + * (for example, in weather displays). + * + * @param place structured input component. + * @return this builder for chaining options together + */ + public abstract Builder place(@NonNull String place); + + /** + * Top-level sub-national administrative features, such as states in the United States + * or provinces in Canada or China. + * + * @param region structured input component. + * @return this builder for chaining options together + */ + public abstract Builder region(@NonNull String region); + + /** + * Postal codes used in country-specific national addressing systems. + * + * @param postcode structured input component. + * @return this builder for chaining options together + */ + public abstract Builder postcode(@NonNull String postcode); + + /** + * Official sub-city features present in countries where such an additional administrative + * layer is used in postal addressing, or where such features are commonly referred to in local + * parlance. Examples include city districts in Brazil and Chile and arrondissements in France. + * + * @param locality structured input component. + * @return this builder for chaining options together + */ + public abstract Builder locality(@NonNull String locality); + + /** + * Colloquial sub-city features often referred to in local parlance. Unlike locality features, + * these typically lack official status and may lack universally agreed-upon boundaries. + * + * @param neighborhood structured input component. + * @return this builder for chaining options together + */ + public abstract Builder neighborhood(@NonNull String neighborhood); + + abstract V6StructuredInputQuery autoBuild(); + + /** + * Build a new {@link V6StructuredInputQuery} object. + * + * @return a new {@link V6StructuredInputQuery} using the provided values in this builder + * @throws ServicesException if all components are null. + */ + public V6StructuredInputQuery build() { + final V6StructuredInputQuery query = autoBuild(); + + if (query.addressLine1() == null + && query.addressNumber() == null + && query.street() == null + && query.block() == null + && query.place() == null + && query.region() == null + && query.postcode() == null + && query.locality() == null + && query.neighborhood() == null + ) { + throw new ServicesException("At least one component must be non null"); + } + + return query; + } + } +} diff --git a/services-geocoding/src/main/java/com/mapbox/api/geocoding/v6/models/Utils.java b/services-geocoding/src/main/java/com/mapbox/api/geocoding/v6/models/Utils.java new file mode 100644 index 000000000..7dee09be3 --- /dev/null +++ b/services-geocoding/src/main/java/com/mapbox/api/geocoding/v6/models/Utils.java @@ -0,0 +1,44 @@ +package com.mapbox.api.geocoding.v6.models; + +import androidx.annotation.Nullable; + +import com.google.gson.JsonElement; +import com.mapbox.auto.value.gson.SerializableJsonElement; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +class Utils { + + @Nullable + static Map fromSerializableProperties( + @Nullable Map unrecognizedProperties + ) { + if (unrecognizedProperties == null) { + return null; + } + + final Map result = new HashMap<>(unrecognizedProperties.size()); + for (Map.Entry entry : unrecognizedProperties.entrySet()) { + result.put(entry.getKey(), entry.getValue().getElement()); + } + return Collections.unmodifiableMap(result); + } + + @Nullable + static Map toSerializableProperties( + @Nullable Map unrecognizedProperties + ) { + if (unrecognizedProperties == null) { + return null; + } + + final Map result = + new HashMap<>(unrecognizedProperties.size()); + for (Map.Entry entry : unrecognizedProperties.entrySet()) { + result.put(entry.getKey(), new SerializableJsonElement(entry.getValue())); + } + return Collections.unmodifiableMap(result); + } +} diff --git a/services-geocoding/src/main/java/com/mapbox/api/geocoding/v6/models/V6BatchResponse.java b/services-geocoding/src/main/java/com/mapbox/api/geocoding/v6/models/V6BatchResponse.java new file mode 100644 index 000000000..b7421358b --- /dev/null +++ b/services-geocoding/src/main/java/com/mapbox/api/geocoding/v6/models/V6BatchResponse.java @@ -0,0 +1,58 @@ +package com.mapbox.api.geocoding.v6.models; + +import androidx.annotation.NonNull; + +import com.google.auto.value.AutoValue; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.TypeAdapter; +import com.google.gson.annotations.SerializedName; +import com.mapbox.api.geocoding.v6.V6GeocodingAdapterFactory; +import com.mapbox.geojson.GeometryAdapterFactory; + +import java.io.Serializable; +import java.util.List; + +/** + * This is the initial object which gets returned when the batch geocoding request + * receives a result. + */ +@AutoValue +public abstract class V6BatchResponse implements Serializable { + + /** + * A list of {@link V6Response}, one {@link V6Response} for each of + * {@link com.mapbox.api.geocoding.v6.V6RequestOptions}. + * + * @return a list of {@link V6Response}s + */ + @SerializedName("batch") + @NonNull + public abstract List responses(); + + /** + * Create a new instance of this class by passing in a formatted valid JSON String. + * + * @param json a formatted valid JSON string defining a GeoJson Geocoding Response + * @return a new instance of this class defined by the values passed inside this static factory + * method + */ + @NonNull + public static V6BatchResponse fromJson(@NonNull String json) { + final Gson gson = new GsonBuilder() + .registerTypeAdapterFactory(GeometryAdapterFactory.create()) + .registerTypeAdapterFactory(V6GeocodingAdapterFactory.create()) + .create(); + return gson.fromJson(json, V6BatchResponse.class); + } + + /** + * 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 typeAdapter(Gson gson) { + return new AutoValue_V6BatchResponse.GsonTypeAdapter(gson); + } +} diff --git a/services-geocoding/src/main/java/com/mapbox/api/geocoding/v6/models/V6Context.java b/services-geocoding/src/main/java/com/mapbox/api/geocoding/v6/models/V6Context.java new file mode 100644 index 000000000..46ddca548 --- /dev/null +++ b/services-geocoding/src/main/java/com/mapbox/api/geocoding/v6/models/V6Context.java @@ -0,0 +1,113 @@ +package com.mapbox.api.geocoding.v6.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; + +/** + * V6 response's piece of data which breaks out the complete geographical hierarchy + * for a given address or place. It is both a reliable way to access the named values of each + * component part of an address, plus contains feature-specific data such as the Wikidata id and + * 3-letter alpha country code. + * + * @see The Context Object + */ +@AutoValue +public abstract class V6Context extends V6JsonObject { + + /** + * Address context element. + * + * @return address context element + */ + @SerializedName("address") + @Nullable + public abstract V6ContextAddress address(); + + /** + * Street context element. + * + * @return street context element + */ + @SerializedName("street") + @Nullable + public abstract V6ContextElement street(); + + /** + * Neighborhood context element. + * + * @return neighborhood context element + */ + @SerializedName("neighborhood") + @Nullable + public abstract V6ContextElement neighborhood(); + + /** + * Place context element. + * + * @return place context element + */ + @SerializedName("place") + @Nullable + public abstract V6ContextElement place(); + + /** + * Postcode context element. + * + * @return postcode context element + */ + @SerializedName("postcode") + @Nullable + public abstract V6ContextElement postcode(); + + /** + * Region context element. + * + * @return region context element + */ + @SerializedName("region") + @Nullable + public abstract V6ContextElement region(); + + /** + * Country context element. + * + * @return country context element + */ + @SerializedName("country") + @Nullable + public abstract V6ContextElement country(); + + /** + * 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 typeAdapter(Gson gson) { + return new AutoValue_V6Context.GsonTypeAdapter(gson); + } + + @AutoValue.Builder + abstract static class Builder extends BaseBuilder { + + abstract Builder address(V6ContextAddress address); + + abstract Builder street(V6ContextElement street); + + abstract Builder neighborhood(V6ContextElement neighborhood); + + abstract Builder place(V6ContextElement place); + + abstract Builder postcode(V6ContextElement postcode); + + abstract Builder region(V6ContextElement region); + + abstract Builder country(V6ContextElement country); + + abstract V6Context build(); + } +} diff --git a/services-geocoding/src/main/java/com/mapbox/api/geocoding/v6/models/V6ContextAddress.java b/services-geocoding/src/main/java/com/mapbox/api/geocoding/v6/models/V6ContextAddress.java new file mode 100644 index 000000000..3a3eb8702 --- /dev/null +++ b/services-geocoding/src/main/java/com/mapbox/api/geocoding/v6/models/V6ContextAddress.java @@ -0,0 +1,78 @@ +package com.mapbox.api.geocoding.v6.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; + +/** + * Element of the {@link V6Context} which holds complete geographical hierarchy for a given address. + * + * @see The Context Object + */ +@AutoValue +public abstract class V6ContextAddress extends V6JsonObject { + + /** + * Element id. This id can be queried directly via a forward geocoding search to traverse into + * a different geographical layer. + * + * @return element id + */ + @Nullable + @SerializedName("mapbox_id") + public abstract String mapboxId(); + + /** + * Element name. Depending on geographic features stores information like street name. + * + * @return element name + */ + @Nullable + @SerializedName("name") + public abstract String name(); + + /** + * Element address_number. The address number for the address this is holding. + * + * @return element address_number + */ + @Nullable + @SerializedName("address_number") + public abstract String addressNumber(); + + /** + * Element street_name. The street name for the address this is holding. + * + * @return element name + */ + @Nullable + @SerializedName("street_name") + public abstract String streetName(); + + /** + * 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 typeAdapter(Gson gson) { + return new AutoValue_V6ContextAddress.GsonTypeAdapter(gson); + } + + @AutoValue.Builder + abstract static class Builder extends BaseBuilder { + + public abstract Builder mapboxId(String mapboxId); + + public abstract Builder name(String name); + + public abstract Builder addressNumber(String addressNumber); + + public abstract Builder streetName(String streetName); + + abstract V6ContextAddress build(); + } +} diff --git a/services-geocoding/src/main/java/com/mapbox/api/geocoding/v6/models/V6ContextElement.java b/services-geocoding/src/main/java/com/mapbox/api/geocoding/v6/models/V6ContextElement.java new file mode 100644 index 000000000..fb6351d8b --- /dev/null +++ b/services-geocoding/src/main/java/com/mapbox/api/geocoding/v6/models/V6ContextElement.java @@ -0,0 +1,57 @@ +package com.mapbox.api.geocoding.v6.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; + +/** + * Element of the {@link V6Context} which holds complete geographical hierarchy for a given address + * or place. + * + * @see The Context Object + */ +@AutoValue +public abstract class V6ContextElement extends V6JsonObject { + + /** + * Element id. This id can be queried directly via a forward geocoding search to traverse into + * a different geographical layer. + * + * @return element id + */ + @Nullable + @SerializedName("mapbox_id") + public abstract String mapboxId(); + + /** + * Element name. Depending on geographic features stores information like street name. + * + * @return element name + */ + @Nullable + @SerializedName("name") + public abstract String name(); + + /** + * 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 typeAdapter(Gson gson) { + return new AutoValue_V6ContextElement.GsonTypeAdapter(gson); + } + + @AutoValue.Builder + abstract static class Builder extends BaseBuilder { + + public abstract Builder mapboxId(String mapboxId); + + public abstract Builder name(String name); + + abstract V6ContextElement build(); + } +} diff --git a/services-geocoding/src/main/java/com/mapbox/api/geocoding/v6/models/V6Coordinates.java b/services-geocoding/src/main/java/com/mapbox/api/geocoding/v6/models/V6Coordinates.java new file mode 100644 index 000000000..1aa42ae2e --- /dev/null +++ b/services-geocoding/src/main/java/com/mapbox/api/geocoding/v6/models/V6Coordinates.java @@ -0,0 +1,77 @@ +package com.mapbox.api.geocoding.v6.models; + +import androidx.annotation.NonNull; +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; + +import java.io.Serializable; +import java.util.List; + +/** + * Object containing coordinate parameters (lat, long) and accuracy. + */ +@AutoValue +public abstract class V6Coordinates implements Serializable { + + /** + * Longitude of result. + * + * @return longitude of result + */ + @NonNull + @SerializedName("longitude") + public abstract Double longitude(); + + /** + * Latitude of result. + * + * @return latitude of result + */ + @NonNull + @SerializedName("latitude") + public abstract Double latitude(); + + /** + * Result coordinate as a {@code Point}. + * + * @return Result coordinate as a {@code Point}. + */ + @NonNull + public Point point() { + return Point.fromLngLat(longitude(), latitude()); + } + + /** + * Point accuracy metric for the returned address feature. + * + * @return accuracy metric for the returned address feature + * @see Point accuracy for address features + */ + @Nullable + @SerializedName("accuracy") + public abstract String accuracy(); + + /** + * A list of routable points for the feature, or null if no points were found. + * + * @return list of routable points for the feature + */ + @Nullable + @SerializedName("routable_points") + public abstract List routablePoints(); + + /** + * 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 typeAdapter(Gson gson) { + return new AutoValue_V6Coordinates.GsonTypeAdapter(gson); + } +} diff --git a/services-geocoding/src/main/java/com/mapbox/api/geocoding/v6/models/V6Feature.java b/services-geocoding/src/main/java/com/mapbox/api/geocoding/v6/models/V6Feature.java new file mode 100644 index 000000000..4e16bb050 --- /dev/null +++ b/services-geocoding/src/main/java/com/mapbox/api/geocoding/v6/models/V6Feature.java @@ -0,0 +1,78 @@ +package com.mapbox.api.geocoding.v6.models; + +import androidx.annotation.NonNull; + +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.Geometry; + +/** + * A type which contains a description for returned by the Geocoding V6 API object. + */ +@AutoValue +public abstract class V6Feature extends V6JsonObject { + + /** + * Feature id. + * + * @return feature id + */ + @NonNull + @SerializedName("id") + public abstract String id(); + + /** + * "Feature", a GeoJSON type from the + * GeoJSON specification. + * + * @return feature type + */ + @NonNull + @SerializedName("type") + public abstract String type(); + + /** + * An object describing the spatial geometry of the returned feature. + * + * @return spatial geometry of the feature + */ + @NonNull + @SerializedName("geometry") + public abstract Geometry geometry(); + + /** + * Feature properties object which contains feature attributes. + * + * @return properties object + */ + @NonNull + @SerializedName("properties") + public abstract V6Properties properties(); + + /** + * Gson type adapter for parsing Gson to this class. + * + * @param gson the built {@link Gson} object + * @return the type adapter for this class + */ + @NonNull + public static TypeAdapter typeAdapter(Gson gson) { + return new AutoValue_V6Feature.GsonTypeAdapter(gson); + } + + @AutoValue.Builder + abstract static class Builder extends BaseBuilder { + + abstract Builder id(String id); + + abstract Builder type(String type); + + abstract Builder geometry(Geometry geometry); + + abstract Builder properties(V6Properties properties); + + abstract V6Feature build(); + } +} diff --git a/services-geocoding/src/main/java/com/mapbox/api/geocoding/v6/models/V6FeatureType.java b/services-geocoding/src/main/java/com/mapbox/api/geocoding/v6/models/V6FeatureType.java new file mode 100644 index 000000000..e64c4c43a --- /dev/null +++ b/services-geocoding/src/main/java/com/mapbox/api/geocoding/v6/models/V6FeatureType.java @@ -0,0 +1,111 @@ +package com.mapbox.api.geocoding.v6.models; + +import androidx.annotation.StringDef; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Geographic Feature Types. + *

+ * Various types of geographic features are available in the Mapbox geocoder. + * Any type might appear as a top-level response, as context in a top-level response, + * or as a filtering option using the types parameter. + * Not all features are available or relevant in all parts of the world. + * New types are occasionally added as necessary to correctly capture global administrative + * hierarchies. + *

+ * Learn more about Feature Types + * here. + */ +public class V6FeatureType { + + private V6FeatureType() { + // private constructor to prevent the class being instantiated + } + + /** + * Generally recognized countries or, in some cases like Hong Kong, + * an area of quasi-national administrative status that has been given a designated country code + * under ISO 3166-1. + */ + public static final String COUNTRY = "country"; + + /** + * Top-level sub-national administrative features, + * such as states in the United States or provinces in Canada or China. + */ + public static final String REGION = "region"; + + /** + * Postal codes used in country-specific national addressing systems. + */ + public static final String POSTCODE = "postcode"; + + /** + * Features that are smaller than top-level administrative features but typically + * larger than cities, in countries that use such an additional layer in postal addressing + * (for example, prefectures in China). + */ + public static final String DISTRICT = "district"; + + /** + * Typically these are cities, villages, municipalities, etc. They are usually features used in + * postal addressing, and are suitable for display in ambient end-user applications where + * current-location context is needed (for example, in weather displays). + */ + public static final String PLACE = "place"; + + /** + * Official sub-city features present in countries where such an additional administrative layer + * is used in postal addressing, or where such features are commonly referred to in local + * parlance. Examples include city districts in Brazil and Chile and arrondissements in France. + */ + public static final String LOCALITY = "locality"; + + /** + * Colloquial sub-city features often referred to in local parlance. Unlike locality features, + * these typically lack official status and may lack universally agreed-upon boundaries. + */ + public static final String NEIGHBORHOOD = "neighborhood"; + + /** + * Street features which host one or more addresses. + */ + public static final String STREET = "street"; + + /** + * Special feature type reserved for Japanese addresses. + */ + public static final String BLOCK = "block"; + + /** + * Individual residential or business addresses. + */ + public static final String ADDRESS = "address"; + + /** + * Sub-unit, suite, or lot within a single parent address. Currently available in the US only. + */ + public static final String SECONDARY_ADDRESS = "secondary_address"; + + /** + * Retention policy for the types. + */ + @Retention(RetentionPolicy.CLASS) + @StringDef({ + COUNTRY, + REGION, + POSTCODE, + DISTRICT, + PLACE, + LOCALITY, + NEIGHBORHOOD, + STREET, + BLOCK, + ADDRESS, + SECONDARY_ADDRESS + }) + public @interface FeatureType { + } +} diff --git a/services-geocoding/src/main/java/com/mapbox/api/geocoding/v6/models/V6JsonObject.java b/services-geocoding/src/main/java/com/mapbox/api/geocoding/v6/models/V6JsonObject.java new file mode 100644 index 000000000..1b37edefa --- /dev/null +++ b/services-geocoding/src/main/java/com/mapbox/api/geocoding/v6/models/V6JsonObject.java @@ -0,0 +1,45 @@ +package com.mapbox.api.geocoding.v6.models; + +import androidx.annotation.Nullable; + +import com.google.gson.JsonElement; +import com.mapbox.auto.value.gson.SerializableJsonElement; +import com.mapbox.auto.value.gson.UnrecognizedJsonProperties; + +import java.io.Serializable; +import java.util.Map; + +/** + * Base class for V6 types. + */ +public abstract class V6JsonObject implements Serializable { + + /** + * Use this method to get JSON properties that weren't recognized during JSON + * serialization by the model. This may be useful to access experimental API properties. + * When an experimental API property becomes stable, + * it will eventually have static field in a model introduced + * and it won't be available via this dynamic method anymore. + * + * @return unrecognized JSON properties + */ + @Nullable + @SuppressWarnings("unused") + public final Map getUnrecognizedJsonProperties() { + return Utils.fromSerializableProperties(unrecognized()); + } + + @Nullable + @UnrecognizedJsonProperties + abstract Map unrecognized(); + + abstract static class BaseBuilder { + + abstract T unrecognized(@Nullable Map value); + + @SuppressWarnings("unused") + public T unrecognizedJsonProperties(@Nullable Map unrecognizedProperties) { + return unrecognized(Utils.toSerializableProperties(unrecognizedProperties)); + } + } +} diff --git a/services-geocoding/src/main/java/com/mapbox/api/geocoding/v6/models/V6MatchCode.java b/services-geocoding/src/main/java/com/mapbox/api/geocoding/v6/models/V6MatchCode.java new file mode 100644 index 000000000..bacd104e1 --- /dev/null +++ b/services-geocoding/src/main/java/com/mapbox/api/geocoding/v6/models/V6MatchCode.java @@ -0,0 +1,147 @@ +package com.mapbox.api.geocoding.v6.models; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.google.auto.value.AutoValue; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.TypeAdapter; +import com.google.gson.annotations.SerializedName; +import com.mapbox.api.geocoding.v6.V6GeocodingAdapterFactory; + +/** + * The {@link V6MatchCode} object in the Geocoding API helps you understand + * how the resulting address feature aligns with the query submitted. + * Available only for address-type features, the {@link V6MatchCode} provides a breakdown + * of how each element of the result matches with the query, plus an overall Confidence score, + * based on how well it matches. This can help you make decisions about what results to keep + * or throw out based on your application's tolerance for fuzzy matching on the query. + *

+ * Smart Address Match is available for all forward geocoding requests that return + * an address type feature. It works best when using Structured Input forward queries, + * as the request components must be typed explicitly. + * + * @see Smart Address Match + */ +@AutoValue +public abstract class V6MatchCode extends V6JsonObject { + + /** + * Match code for address number. + * + * @return match code for address number + */ + @Nullable + @SerializedName("address_number") + public abstract String addressNumber(); + + /** + * Match code for street. + * + * @return match code for street + */ + @Nullable + @SerializedName("street") + public abstract String street(); + + /** + * Match code for locality. + * + * @return match code for locality + */ + @Nullable + @SerializedName("locality") + public abstract String locality(); + + /** + * Match code for place. + * + * @return match code for place + */ + @Nullable + @SerializedName("place") + public abstract String place(); + + /** + * Match code for postcode. + * + * @return match code for postcode + */ + @Nullable + @SerializedName("postcode") + public abstract String postcode(); + + /** + * Match code for region. + * + * @return match code for region + */ + @Nullable + @SerializedName("region") + public abstract String region(); + + /** + * Match code for country. + * + * @return match code for country + */ + @Nullable + @SerializedName("country") + public abstract String country(); + + /** + * Confidence score, which indicates how well the result matches the input query. + * + * @return confidence score + */ + @Nullable + @SerializedName("confidence") + public abstract String confidence(); + + /** + * Create a V6MatchCode object from JSON. + * + * @param json string of JSON making up a carmen context + * @return this class using the defined information in the provided JSON string + */ + @SuppressWarnings("unused") + public static V6MatchCode fromJson(@NonNull String json) { + final Gson gson = new GsonBuilder() + .registerTypeAdapterFactory(V6GeocodingAdapterFactory.create()) + .create(); + return gson.fromJson(json, V6MatchCode.class); + } + + /** + * 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 typeAdapter(Gson gson) { + return new AutoValue_V6MatchCode.GsonTypeAdapter(gson); + } + + @AutoValue.Builder + abstract static class Builder extends BaseBuilder { + + abstract Builder addressNumber(String addressNumber); + + abstract Builder street(String street); + + abstract Builder locality(String locality); + + abstract Builder place(String place); + + abstract Builder postcode(String postcode); + + abstract Builder region(String region); + + abstract Builder country(String country); + + abstract Builder confidence(String confidence); + + abstract V6MatchCode build(); + } +} diff --git a/services-geocoding/src/main/java/com/mapbox/api/geocoding/v6/models/V6Properties.java b/services-geocoding/src/main/java/com/mapbox/api/geocoding/v6/models/V6Properties.java new file mode 100644 index 000000000..f41512521 --- /dev/null +++ b/services-geocoding/src/main/java/com/mapbox/api/geocoding/v6/models/V6Properties.java @@ -0,0 +1,171 @@ +package com.mapbox.api.geocoding.v6.models; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.google.auto.value.AutoValue; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.TypeAdapter; +import com.google.gson.annotations.SerializedName; +import com.mapbox.api.geocoding.v6.V6GeocodingAdapterFactory; + +import java.util.List; + +/** + * A type which contains the majority of information returned by the Geocoding V6 API. + */ +@AutoValue +public abstract class V6Properties extends V6JsonObject { + + /** + * Feature id. + * + * @return feature id + */ + @NonNull + @SerializedName("mapbox_id") + public abstract String mapboxId(); + + /** + * A string describing the type of the feature. + * + * @return a string describing the type of the feature + */ + @NonNull + @V6FeatureType.FeatureType + @SerializedName("feature_type") + public abstract String featureType(); + + /** + * Formatted name. + * + * @return formatted name + */ + @Nullable + @SerializedName("name") + public abstract String name(); + + /** + * The canonical or otherwise more common alias for the feature name. + * For example, searching for "America" will return "America" as the name, + * and "United States" as name_preferred. + * + * @return canonical or otherwise more common alias for the feature name + */ + @Nullable + @SerializedName("name_preferred") + public abstract String namePreferred(); + + /** + * Formatted string of result context: place region country postcode. + * The part of the result which comes after {@link V6Properties#name()}. + * + * @return formatted string of result context: place region country postcode + */ + @Nullable + @SerializedName("place_formatted") + public abstract String placeFormatted(); + + /** + * Full formatted string of the feature, combining name_preferred + * and place_formatted. + * + * @return full formatted string of the feature + */ + @Nullable + @SerializedName("full_address") + public abstract String fullAddress(); + + /** + * An object representing the hierarchy of encompassing parent features. + * Addresses will also include an address context object, + * which contains the address street and address number properties broken out separately. + * + * @return an object representing the hierarchy of encompassing parent features + */ + @Nullable + @SerializedName("context") + public abstract V6Context context(); + + /** + * Object containing coordinate parameters (lat, long), accuracy and + * routable points. + * + * @return object containing coordinate parameters (lat, long) and accuracy + */ + @Nullable + @SerializedName("coordinates") + public abstract V6Coordinates coordinates(); + + /** + * The bounding box of the feature in minLon,minLat,maxLon,maxLat order. + * This property is only provided with features of type country, region, + * postcode, district, place, locality, or neighborhood. + * + * @return bounding box of the feature + */ + @Nullable + @SerializedName("bbox") + abstract List bbox(); + + /** + * Additional metadata indicating how the result components match to the input query. + * + * @return additional metadata indicating how the result components match to the input query + * @see Smart Address Match + */ + @Nullable + @SerializedName("match_code") + public abstract V6MatchCode matchCode(); + + /** + * Create a V6Property object from JSON. + * + * @param json string of JSON making up a carmen context + * @return this class using the defined information in the provided JSON string + */ + @SuppressWarnings("unused") + public static V6Properties fromJson(@NonNull String json) { + final Gson gson = new GsonBuilder() + .registerTypeAdapterFactory(V6GeocodingAdapterFactory.create()) + .create(); + return gson.fromJson(json, V6Properties.class); + } + + /** + * 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 typeAdapter(Gson gson) { + return new AutoValue_V6Properties.GsonTypeAdapter(gson); + } + + @AutoValue.Builder + abstract static class Builder extends BaseBuilder { + + abstract Builder mapboxId(String mapboxId); + + abstract Builder featureType(String featureType); + + abstract Builder name(String name); + + abstract Builder namePreferred(String namePreferred); + + abstract Builder placeFormatted(String placeFormatted); + + abstract Builder fullAddress(String fullAddress); + + abstract Builder context(V6Context context); + + abstract Builder coordinates(V6Coordinates coordinates); + + abstract Builder bbox(List bbox); + + abstract Builder matchCode(V6MatchCode matchCode); + + abstract V6Properties build(); + } +} diff --git a/services-geocoding/src/main/java/com/mapbox/api/geocoding/v6/models/V6Response.java b/services-geocoding/src/main/java/com/mapbox/api/geocoding/v6/models/V6Response.java new file mode 100644 index 000000000..56334ac0f --- /dev/null +++ b/services-geocoding/src/main/java/com/mapbox/api/geocoding/v6/models/V6Response.java @@ -0,0 +1,78 @@ +package com.mapbox.api.geocoding.v6.models; + +import androidx.annotation.NonNull; + +import com.google.auto.value.AutoValue; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.TypeAdapter; +import com.mapbox.api.geocoding.v6.V6GeocodingAdapterFactory; +import com.mapbox.geojson.GeometryAdapterFactory; + +import java.io.Serializable; +import java.util.List; + +/** + * This is the initial object which gets returned when the geocoding request receives a result. + */ +@AutoValue +public abstract class V6Response implements Serializable { + + /** + * "FeatureCollection", + * a GeoJSON type from the GeoJSON specification. + * + * @return the type of GeoJSON + */ + @NonNull + public abstract String type(); + + /** + * A list of feature objects. + * Forward geocodes: Returned features are ordered by relevance. + * Reverse geocodes: Returned features are ordered by index hierarchy, + * from most specific features to least specific features that overlap the queried coordinates. + *

+ * Read the + * Search result prioritization + * guide to learn more about how returned features are organized in the Geocoding API response. + * + * @return a list of {@link V6Feature}s + */ + @NonNull + public abstract List features(); + + /** + * Attributes the results of the Mapbox Geocoding API to Mapbox. + * + * @return information about Mapbox's terms of service and the data sources + */ + @NonNull + public abstract String attribution(); + + /** + * Create a new instance of this class by passing in a formatted valid JSON String. + * + * @param json a formatted valid JSON string defining a GeoJson Geocoding Response + * @return a new instance of this class defined by the values passed inside this static factory + * method + */ + @NonNull + public static V6Response fromJson(@NonNull String json) { + final Gson gson = new GsonBuilder() + .registerTypeAdapterFactory(GeometryAdapterFactory.create()) + .registerTypeAdapterFactory(V6GeocodingAdapterFactory.create()) + .create(); + return gson.fromJson(json, V6Response.class); + } + + /** + * 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 typeAdapter(Gson gson) { + return new AutoValue_V6Response.GsonTypeAdapter(gson); + } +} diff --git a/services-geocoding/src/main/java/com/mapbox/api/geocoding/v6/models/V6RoutablePoint.java b/services-geocoding/src/main/java/com/mapbox/api/geocoding/v6/models/V6RoutablePoint.java new file mode 100644 index 000000000..75588e447 --- /dev/null +++ b/services-geocoding/src/main/java/com/mapbox/api/geocoding/v6/models/V6RoutablePoint.java @@ -0,0 +1,65 @@ +package com.mapbox.api.geocoding.v6.models; + +import androidx.annotation.NonNull; +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; + +import java.io.Serializable; + +/** + * Object containing routable point (lat, long) and name. + */ +@AutoValue +public abstract class V6RoutablePoint implements Serializable { + + /** + * Longitude of result. + * + * @return longitude of result + */ + @NonNull + @SerializedName("longitude") + public abstract Double longitude(); + + /** + * Latitude of result. + * + * @return latitude of result + */ + @NonNull + @SerializedName("latitude") + public abstract Double latitude(); + + /** + * Result coordinate as a {@code Point}. + * + * @return Result coordinate as a {@code Point}. + */ + @NonNull + public Point point() { + return Point.fromLngLat(longitude(), latitude()); + } + + /** + * The routable point name. + * + * @return the routable point name + */ + @Nullable + @SerializedName("name") + public abstract String name(); + + /** + * 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 typeAdapter(Gson gson) { + return new AutoValue_V6RoutablePoint.GsonTypeAdapter(gson); + } +} diff --git a/services-geocoding/src/main/java/com/mapbox/api/geocoding/v6/models/V6Worldview.java b/services-geocoding/src/main/java/com/mapbox/api/geocoding/v6/models/V6Worldview.java new file mode 100644 index 000000000..5cf7f414f --- /dev/null +++ b/services-geocoding/src/main/java/com/mapbox/api/geocoding/v6/models/V6Worldview.java @@ -0,0 +1,81 @@ +package com.mapbox.api.geocoding.v6.models; + +import androidx.annotation.StringDef; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Worldview parameters are used to identify geographic features whose characteristics + * are defined differently by audiences belonging to various regional, cultural, + * or political groups. + * + * The Geocoding API only supports worldviews for the country and region data types, + * but may expand to additional data types in the future. + * + * Learn more about worldviews + * here. + */ +public class V6Worldview { + + /** + * Features for an Argentinian audience. + */ + public static final String ARGENTINA = "ar"; + + /** + * Features for a mainland Chinese audience. + */ + public static final String CHINA = "cn"; + + /** + * Features for an Indian audience. + */ + public static final String INDIA = "in"; + + /** + * Features for a Japanese audience. + */ + public static final String JAPAN = "jp"; + + /** + * Features for a Moroccan audience. + */ + public static final String MOROCCO = "ma"; + + /** + * Features for a Russian audience. + */ + public static final String RUSSIA = "ru"; + + /** + * Features for a Turkish audience. + */ + public static final String TURKEY = "tr"; + + /** + * Features for an American audience. + */ + public static final String USA = "us"; + + private V6Worldview() { + // private constructor to prevent the class being instantiated + } + + /** + * Retention policy for the worldviews. + */ + @Retention(RetentionPolicy.CLASS) + @StringDef( { + ARGENTINA, + CHINA, + INDIA, + JAPAN, + MOROCCO, + RUSSIA, + TURKEY, + USA, + }) + public @interface Worldview { + } +} diff --git a/services-geocoding/src/main/java/com/mapbox/api/geocoding/v6/models/package-info.java b/services-geocoding/src/main/java/com/mapbox/api/geocoding/v6/models/package-info.java new file mode 100644 index 000000000..8c3b178c0 --- /dev/null +++ b/services-geocoding/src/main/java/com/mapbox/api/geocoding/v6/models/package-info.java @@ -0,0 +1,5 @@ +/** + * Contains the V6 Geocoding Response model classes which are useful when working with the + * requested results. + */ +package com.mapbox.api.geocoding.v6.models; diff --git a/services-geocoding/src/main/java/com/mapbox/api/geocoding/v6/package-info.java b/services-geocoding/src/main/java/com/mapbox/api/geocoding/v6/package-info.java new file mode 100644 index 000000000..9b0727551 --- /dev/null +++ b/services-geocoding/src/main/java/com/mapbox/api/geocoding/v6/package-info.java @@ -0,0 +1,4 @@ +/** + * Contains the Mapbox Java Services classes related to the Mapbox V6 Geocoding API. + */ +package com.mapbox.api.geocoding.v6; diff --git a/services-geocoding/src/test/java/com/mapbox/api/geocoding/v6/MapboxV6BatchGeocodingTest.java b/services-geocoding/src/test/java/com/mapbox/api/geocoding/v6/MapboxV6BatchGeocodingTest.java new file mode 100644 index 000000000..0ade358e8 --- /dev/null +++ b/services-geocoding/src/test/java/com/mapbox/api/geocoding/v6/MapboxV6BatchGeocodingTest.java @@ -0,0 +1,126 @@ +package com.mapbox.api.geocoding.v6; + +import com.mapbox.api.geocoding.v6.models.V6BatchResponse; +import com.mapbox.api.geocoding.v6.models.V6Response; + +import org.junit.Test; + +import java.io.IOException; +import java.nio.charset.Charset; +import java.util.List; +import java.util.Objects; + +import okhttp3.HttpUrl; +import okhttp3.mockwebserver.MockResponse; +import okhttp3.mockwebserver.RecordedRequest; +import okio.Buffer; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import retrofit2.Response; + +public class MapboxV6BatchGeocodingTest extends V6GeocodingTestUtils { + + @Test + public void sanity() throws Exception { + server.enqueue(createMockResponse("v6/batch_response_non_empty_valid.json")); + + final MapboxV6BatchGeocoding geocoding = MapboxV6BatchGeocoding + .builder(ACCESS_TOKEN, TEST_COMPLETE_FORWARD_OPTIONS, TEST_COMPLETE_REVERSE_OPTIONS) + .baseUrl(mockUrl.toString()) + .build(); + + assertNotNull(geocoding); + + final Response response = geocoding.executeCall(); + assertEquals(200, response.code()); + + final V6BatchResponse body = response.body(); + assertNotNull(body); + + final List responses = body.responses(); + assertNotNull(responses); + assertEquals(3, responses.size()); + + assertEquals(2, responses.get(0).features().size()); + assertEquals(1, responses.get(1).features().size()); + assertEquals(0, responses.get(2).features().size()); + } + + @Test + public void errorMessageTest() throws Exception { + final MockResponse mockResponse = new MockResponse() + .setBody(loadJsonFixture("v6/error_message.json")) + .setResponseCode(400); + + server.enqueue(mockResponse); + + final MapboxV6BatchGeocoding geocoding = MapboxV6BatchGeocoding + .builder(ACCESS_TOKEN, TEST_COMPLETE_FORWARD_OPTIONS, TEST_COMPLETE_REVERSE_OPTIONS) + .baseUrl(mockUrl.toString()) + .build(); + + final Response response = geocoding.executeCall(); + assertNotNull(response); + assertEquals(400, response.code()); + } + + @Test + public void testRequestParameters() throws InterruptedException, IOException { + server.enqueue(createErrorMockResponse(500)); + + final MapboxV6BatchGeocoding geocoding = MapboxV6BatchGeocoding + .builder(ACCESS_TOKEN, TEST_COMPLETE_FORWARD_OPTIONS) + .permanent(true) + .clientAppName("test-client-app") + .baseUrl(mockUrl.toString()) + .build(); + + geocoding.executeCall(); + + final RecordedRequest request = server.takeRequest(); + + assertEquals("POST", request.getMethod()); + + assertTrue( + Objects.requireNonNull(request.getHeader("User-Agent")).contains( + Objects.requireNonNull(geocoding.clientAppName()) + ) + ); + + final HttpUrl url = request.getRequestUrl(); + assertEquals("/search/geocode/v6/batch", Objects.requireNonNull(url).encodedPath()); + assertEquals(geocoding.accessToken(), url.queryParameter("access_token")); + assertEquals( + Objects.requireNonNull(geocoding.permanent()).toString(), + url.queryParameter("permanent") + ); + + final Buffer body = request.getBody(); + assertNotNull(body); + + final String json = new String(body.readByteArray(), Charset.defaultCharset()); + + assertEquals( + V6GeocodingUtils.serialize(TEST_COMPLETE_FORWARD_OPTIONS), + json + ); + } + + @Test + public void testDefaultRequestParameters() throws InterruptedException, IOException { + server.enqueue(createErrorMockResponse(500)); + + final MapboxV6BatchGeocoding geocoding = MapboxV6BatchGeocoding + .builder(ACCESS_TOKEN, TEST_COMPLETE_FORWARD_OPTIONS) + .baseUrl(mockUrl.toString()) + .build(); + + geocoding.executeCall(); + + final RecordedRequest request = server.takeRequest(); + final HttpUrl url = request.getRequestUrl(); + assertNull(Objects.requireNonNull(url).queryParameter("permanent")); + } +} diff --git a/services-geocoding/src/test/java/com/mapbox/api/geocoding/v6/MapboxV6GeocodingTest.java b/services-geocoding/src/test/java/com/mapbox/api/geocoding/v6/MapboxV6GeocodingTest.java new file mode 100644 index 000000000..cdfbc22eb --- /dev/null +++ b/services-geocoding/src/test/java/com/mapbox/api/geocoding/v6/MapboxV6GeocodingTest.java @@ -0,0 +1,276 @@ +package com.mapbox.api.geocoding.v6; + +import com.mapbox.api.geocoding.v6.models.V6Response; + +import org.junit.Test; + +import java.io.IOException; +import java.util.Objects; + +import okhttp3.HttpUrl; +import okhttp3.mockwebserver.MockResponse; +import okhttp3.mockwebserver.RecordedRequest; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import retrofit2.Response; + +public class MapboxV6GeocodingTest extends V6GeocodingTestUtils { + + @Test + public void sanity() throws Exception { + server.enqueue(createMockResponse("v6/response_non_empty_valid.json")); + + final V6ForwardGeocodingRequestOptions requestOptions = V6ForwardGeocodingRequestOptions + .builder("740 15th St NW, Washington, DC 20005") + .build(); + + final MapboxV6Geocoding geocoding = MapboxV6Geocoding + .builder(ACCESS_TOKEN, requestOptions) + .baseUrl(mockUrl.toString()) + .build(); + + assertNotNull(geocoding); + + final Response response = geocoding.executeCall(); + assertEquals(200, response.code()); + + final V6Response body = response.body(); + assertNotNull(body); + assertEquals(2, body.features().size()); + } + + @Test + public void errorMessageTest() throws Exception { + final MockResponse mockResponse = new MockResponse() + .setBody(loadJsonFixture("v6/error_message.json")) + .setResponseCode(400); + + server.enqueue(mockResponse); + + final V6ForwardGeocodingRequestOptions requestOptions = V6ForwardGeocodingRequestOptions + .builder("740 15th St NW, Washington, DC 20005") + .build(); + + final MapboxV6Geocoding geocoding = MapboxV6Geocoding + .builder(ACCESS_TOKEN, requestOptions) + .baseUrl(mockUrl.toString()) + .build(); + + final Response response = geocoding.executeCall(); + assertNotNull(response); + assertEquals(400, response.code()); + } + + @Test + public void testForwardGeocodingRequestParameters() throws InterruptedException, IOException { + server.enqueue(createErrorMockResponse(500)); + + final V6ForwardGeocodingRequestOptions options = TEST_COMPLETE_FORWARD_OPTIONS; + + final MapboxV6Geocoding geocoding = MapboxV6Geocoding + .builder(ACCESS_TOKEN, options) + .permanent(true) + .clientAppName("test-client-app") + .baseUrl(mockUrl.toString()) + .build(); + + geocoding.executeCall(); + + final RecordedRequest request = server.takeRequest(); + + assertEquals("GET", request.getMethod()); + + assertTrue( + Objects.requireNonNull(request.getHeader("User-Agent")).contains( + Objects.requireNonNull(geocoding.clientAppName()) + ) + ); + + final HttpUrl url = request.getRequestUrl(); + assertEquals("/search/geocode/v6/forward", Objects.requireNonNull(url).encodedPath()); + assertEquals(geocoding.accessToken(), url.queryParameter("access_token")); + assertEquals( + Objects.requireNonNull(geocoding.permanent()).toString(), + url.queryParameter("permanent") + ); + assertEquals(options.query(), url.queryParameter("q")); + assertEquals( + Objects.requireNonNull(options.autocomplete()).toString(), + url.queryParameter("autocomplete") + ); + assertEquals(options.apiFormattedBbox(), url.queryParameter("bbox")); + assertEquals(options.country(), url.queryParameter("country")); + assertEquals(options.language(), url.queryParameter("language")); + assertEquals(String.valueOf(options.limit()), url.queryParameter("limit")); + assertEquals(options.proximity(), url.queryParameter("proximity")); + assertEquals(options.apiFormattedTypes(), url.queryParameter("types")); + assertEquals(options.worldview(), url.queryParameter("worldview")); + } + + @Test + public void testForwardGeocodingDefaultRequestParameters() throws InterruptedException, IOException { + server.enqueue(createErrorMockResponse(500)); + + final V6ForwardGeocodingRequestOptions options = V6ForwardGeocodingRequestOptions + .builder(TEST_QUERY).build(); + + final MapboxV6Geocoding geocoding = MapboxV6Geocoding + .builder(ACCESS_TOKEN, options) + .baseUrl(mockUrl.toString()) + .build(); + + geocoding.executeCall(); + + final RecordedRequest request = server.takeRequest(); + + final HttpUrl url = request.getRequestUrl(); + assertEquals("/search/geocode/v6/forward", Objects.requireNonNull(url).encodedPath()); + assertEquals(geocoding.accessToken(), url.queryParameter("access_token")); + assertEquals(TEST_QUERY, url.queryParameter("q")); + assertNull(url.queryParameter("permanent")); + assertNull(url.queryParameter("autocomplete")); + assertNull(url.queryParameter("bbox")); + assertNull(url.queryParameter("country")); + assertNull(url.queryParameter("language")); + assertNull(url.queryParameter("limit")); + assertNull(url.queryParameter("proximity")); + assertNull(url.queryParameter("types")); + assertNull(url.queryParameter("worldview")); + } + + @Test + public void testStructuredInputRequestParameters() throws InterruptedException, IOException { + server.enqueue(createErrorMockResponse(500)); + + final V6ForwardGeocodingRequestOptions options = TEST_COMPLETE_STRUCTURED_INPUT_OPTIONS; + + final MapboxV6Geocoding geocoding = MapboxV6Geocoding + .builder(ACCESS_TOKEN, options) + .baseUrl(mockUrl.toString()) + .build(); + + geocoding.executeCall(); + + final RecordedRequest request = server.takeRequest(); + + final HttpUrl url = request.getRequestUrl(); + assertEquals("/search/geocode/v6/forward", Objects.requireNonNull(url).encodedPath()); + assertEquals(options.addressLine1(), url.queryParameter("address_line1")); + assertEquals(options.addressNumber(), url.queryParameter("address_number")); + assertEquals(options.street(), url.queryParameter("street")); + assertEquals(options.block(), url.queryParameter("block")); + assertEquals(options.place(), url.queryParameter("place")); + assertEquals(options.region(), url.queryParameter("region")); + assertEquals(options.postcode(), url.queryParameter("postcode")); + assertEquals(options.locality(), url.queryParameter("locality")); + assertEquals(options.neighborhood(), url.queryParameter("neighborhood")); + } + + @Test + public void testDefaultStructuredInputRequestParameters() throws InterruptedException, IOException { + server.enqueue(createErrorMockResponse(500)); + + final V6StructuredInputQuery structuredInputQuery = V6StructuredInputQuery.builder() + .addressNumber("test-address-number") + .build(); + + final V6ForwardGeocodingRequestOptions options = V6ForwardGeocodingRequestOptions + .builder(structuredInputQuery).build(); + + final MapboxV6Geocoding geocoding = MapboxV6Geocoding + .builder(ACCESS_TOKEN, options) + .baseUrl(mockUrl.toString()) + .build(); + + geocoding.executeCall(); + + final RecordedRequest request = server.takeRequest(); + + final HttpUrl url = request.getRequestUrl(); + assertEquals("/search/geocode/v6/forward", Objects.requireNonNull(url).encodedPath()); + assertEquals(geocoding.accessToken(), url.queryParameter("access_token")); + assertNull(url.queryParameter("q")); + assertEquals(structuredInputQuery.addressNumber(), url.queryParameter("address_number")); + assertNull(url.queryParameter("address_line1")); + assertNull(url.queryParameter("street")); + assertNull(url.queryParameter("block")); + assertNull(url.queryParameter("place")); + assertNull(url.queryParameter("region")); + assertNull(url.queryParameter("postcode")); + assertNull(url.queryParameter("locality")); + assertNull(url.queryParameter("neighborhood")); + } + + @Test + public void testReverseGeocodingRequestParameters() throws InterruptedException, IOException { + server.enqueue(createErrorMockResponse(500)); + + final V6ReverseGeocodingRequestOptions options = TEST_COMPLETE_REVERSE_OPTIONS; + + final MapboxV6Geocoding geocoding = MapboxV6Geocoding + .builder(ACCESS_TOKEN, options) + .permanent(true) + .clientAppName("test-client-app") + .baseUrl(mockUrl.toString()) + .build(); + + geocoding.executeCall(); + + final RecordedRequest request = server.takeRequest(); + + assertEquals("GET", request.getMethod()); + + assertTrue( + Objects.requireNonNull(request.getHeader("User-Agent")).contains( + Objects.requireNonNull(geocoding.clientAppName()) + ) + ); + + final HttpUrl url = request.getRequestUrl(); + assertEquals("/search/geocode/v6/reverse", Objects.requireNonNull(url).encodedPath()); + assertEquals(geocoding.accessToken(), url.queryParameter("access_token")); + assertEquals( + Objects.requireNonNull(geocoding.permanent()).toString(), + url.queryParameter("permanent") + ); + assertEquals(options.longitude().toString(), url.queryParameter("longitude")); + assertEquals(options.latitude().toString(), url.queryParameter("latitude")); + assertEquals(options.country(), url.queryParameter("country")); + assertEquals(options.language(), url.queryParameter("language")); + assertEquals(String.valueOf(options.limit()), url.queryParameter("limit")); + assertEquals(options.apiFormattedTypes(), url.queryParameter("types")); + assertEquals(options.worldview(), url.queryParameter("worldview")); + } + + @Test + public void testReverseGeocodingDefaultRequestParameters() throws InterruptedException, IOException { + server.enqueue(createErrorMockResponse(500)); + + final V6ReverseGeocodingRequestOptions options = V6ReverseGeocodingRequestOptions + .builder(TEST_POINT) + .build(); + + final MapboxV6Geocoding geocoding = MapboxV6Geocoding + .builder(ACCESS_TOKEN, options) + .baseUrl(mockUrl.toString()) + .build(); + + geocoding.executeCall(); + + final RecordedRequest request = server.takeRequest(); + + final HttpUrl url = request.getRequestUrl(); + assertEquals("/search/geocode/v6/reverse", Objects.requireNonNull(url).encodedPath()); + assertEquals(geocoding.accessToken(), url.queryParameter("access_token")); + assertEquals(String.valueOf(TEST_POINT.longitude()), url.queryParameter("longitude")); + assertEquals(String.valueOf(TEST_POINT.latitude()), url.queryParameter("latitude")); + assertNull(url.queryParameter("permanent")); + assertNull(url.queryParameter("country")); + assertNull(url.queryParameter("language")); + assertNull(url.queryParameter("limit")); + assertNull(url.queryParameter("types")); + assertNull(url.queryParameter("worldview")); + } +} diff --git a/services-geocoding/src/test/java/com/mapbox/api/geocoding/v6/V6ForwardGeocodingRequestOptionsTest.java b/services-geocoding/src/test/java/com/mapbox/api/geocoding/v6/V6ForwardGeocodingRequestOptionsTest.java new file mode 100644 index 000000000..2d477cb78 --- /dev/null +++ b/services-geocoding/src/test/java/com/mapbox/api/geocoding/v6/V6ForwardGeocodingRequestOptionsTest.java @@ -0,0 +1,199 @@ +package com.mapbox.api.geocoding.v6; + +import com.mapbox.api.geocoding.v6.models.V6FeatureType; +import com.mapbox.api.geocoding.v6.models.V6Worldview; +import com.mapbox.core.exceptions.ServicesException; +import com.mapbox.geojson.Point; + +import org.junit.Test; +import org.junit.function.ThrowingRunnable; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Locale; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertThrows; +import static org.junit.Assert.assertTrue; + +public class V6ForwardGeocodingRequestOptionsTest extends V6GeocodingTestUtils { + + @Test + public void testEmptyQuery() { + final Exception e = assertThrows(ServicesException.class, new ThrowingRunnable() { + @Override + public void run() { + V6ForwardGeocodingRequestOptions.builder(""); + } + }); + + assertTrue(e.getMessage().contains("Search query can't be empty")); + } + + @Test + public void testUnspecifiedParametersAreNull() { + final V6ForwardGeocodingRequestOptions options = V6ForwardGeocodingRequestOptions + .builder(TEST_QUERY) + .build(); + + assertNull(options.autocomplete()); + assertNull(options.bbox()); + assertNull(options.country()); + assertNull(options.language()); + assertNull(options.limit()); + assertNull(options.proximity()); + assertNull(options.types()); + assertNull(options.worldview()); + } + + @Test + public void testQuery() { + final V6ForwardGeocodingRequestOptions options = V6ForwardGeocodingRequestOptions + .builder(TEST_QUERY) + .build(); + + assertEquals(TEST_QUERY, options.query()); + } + + @Test + public void testAutocomplete() { + final V6ForwardGeocodingRequestOptions options = V6ForwardGeocodingRequestOptions + .builder(TEST_QUERY) + .autocomplete(false) + .build(); + + assertEquals(false, options.autocomplete()); + } + + @Test + public void testBoundingBox() { + final V6ForwardGeocodingRequestOptions options = V6ForwardGeocodingRequestOptions + .builder(TEST_QUERY) + .boundingBox(TEST_BBOX) + .build(); + + assertEquals("10.1,20.2,30.3,50.5", options.apiFormattedBbox()); + assertEquals(Arrays.asList(10.1, 20.2, 30.3, 50.5), options.bbox()); + } + + @Test + public void testCountry() { + final V6ForwardGeocodingRequestOptions options = V6ForwardGeocodingRequestOptions + .builder(TEST_QUERY) + .country(Locale.FRANCE.getCountry(), "Italy") + .build(); + + assertEquals("FR,Italy", options.country()); + } + + @Test + public void testLanguage() { + final V6ForwardGeocodingRequestOptions options = V6ForwardGeocodingRequestOptions + .builder(TEST_QUERY) + .language(Locale.FRANCE.getLanguage()) + .build(); + + assertEquals("fr", options.language()); + } + + @Test + public void testLimit() { + final V6ForwardGeocodingRequestOptions options = V6ForwardGeocodingRequestOptions + .builder(TEST_QUERY) + .limit(7) + .build(); + + assertEquals(Integer.valueOf(7), options.limit()); + } + + @Test + public void testProximityPoint() { + final V6ForwardGeocodingRequestOptions options = V6ForwardGeocodingRequestOptions + .builder(TEST_QUERY) + .proximity(Point.fromLngLat(10.1, 20.2)) + .build(); + + assertEquals("10.1,20.2", options.proximity()); + } + + @Test + public void testProximityIp() { + final V6ForwardGeocodingRequestOptions options = V6ForwardGeocodingRequestOptions + .builder(TEST_QUERY) + .withIpAsProximity() + .build(); + + assertEquals("ip", options.proximity()); + } + + @Test + public void testTypes() { + final V6ForwardGeocodingRequestOptions options = V6ForwardGeocodingRequestOptions + .builder(TEST_QUERY) + .types(V6FeatureType.COUNTRY, V6FeatureType.PLACE) + .build(); + + assertEquals("country,place", options.apiFormattedTypes()); + assertEquals(Arrays.asList("country", "place"), options.types()); + } + + @Test + public void testWorldview() { + final V6ForwardGeocodingRequestOptions options = V6ForwardGeocodingRequestOptions + .builder(TEST_QUERY) + .worldview(V6Worldview.USA) + .build(); + + assertEquals("us", options.worldview()); + } + + @Test + public void testRequestOptionsJsonSerialization() throws IOException { + final String json = TEST_COMPLETE_FORWARD_OPTIONS.toJson(); + assertEquals( + loadCompressedJson("v6/forward_request_options_serialised.json"), + json + ); + } + + @Test + public void testMinimalRequestOptionsJsonSerialization() throws IOException { + final V6ForwardGeocodingRequestOptions options = V6ForwardGeocodingRequestOptions + .builder(TEST_QUERY) + .build(); + + final String json = options.toJson(); + assertEquals( + loadCompressedJson("v6/forward_request_options_minimal_serialised.json"), + json + ); + } + + @Test + public void testStructuredInputRequestOptionsJsonSerialization() throws IOException { + final String json = TEST_COMPLETE_STRUCTURED_INPUT_OPTIONS.toJson(); + assertEquals( + loadCompressedJson("v6/forward_structured_input_request_options_serialised.json"), + json + ); + } + + @Test + public void testMinimalStructuredInputRequestOptionsJsonSerialization() throws IOException { + final V6StructuredInputQuery query = V6StructuredInputQuery + .builder() + .addressNumber("test-address-number") + .build(); + + final V6ForwardGeocodingRequestOptions options = V6ForwardGeocodingRequestOptions + .builder(query) + .build(); + + final String json = options.toJson(); + assertEquals( + loadCompressedJson("v6/forward_structured_input_request_options_minimal_serialised.json"), + json + ); + } +} diff --git a/services-geocoding/src/test/java/com/mapbox/api/geocoding/v6/V6GeocodingTestUtils.java b/services-geocoding/src/test/java/com/mapbox/api/geocoding/v6/V6GeocodingTestUtils.java new file mode 100644 index 000000000..244b8055f --- /dev/null +++ b/services-geocoding/src/test/java/com/mapbox/api/geocoding/v6/V6GeocodingTestUtils.java @@ -0,0 +1,122 @@ +package com.mapbox.api.geocoding.v6; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonElement; +import com.google.gson.JsonParser; +import com.mapbox.api.geocoding.v6.models.V6FeatureType; +import com.mapbox.api.geocoding.v6.models.V6Worldview; +import com.mapbox.core.TestUtils; +import com.mapbox.geojson.BoundingBox; +import com.mapbox.geojson.Point; + +import org.junit.After; +import org.junit.Before; + +import java.io.IOException; +import java.util.Locale; + +import okhttp3.HttpUrl; +import okhttp3.mockwebserver.MockResponse; +import okhttp3.mockwebserver.MockWebServer; + +public abstract class V6GeocodingTestUtils extends TestUtils { + + protected MockWebServer server; + protected HttpUrl mockUrl; + + @Before + public void setUp() throws Exception { + server = new MockWebServer(); + server.start(); + mockUrl = server.url(""); + } + + @After + public void tearDown() throws IOException { + server.shutdown(); + } + + public MockResponse createMockResponse(String fileName) throws IOException { + return new MockResponse().setBody(loadJsonFixture(fileName)); + } + + public MockResponse createErrorMockResponse(int httpCode) { + return new MockResponse().setResponseCode(httpCode); + } + + public String loadCompressedJson(String filename) throws IOException { + return removeJsonWhitespaces(loadJsonFixture(filename)); + } + + public static String removeJsonWhitespaces(String json) { + final Gson gson = new GsonBuilder().create(); + final JsonElement parsed = JsonParser.parseString(json); + return gson.toJson(parsed); + } + + public static final String TEST_QUERY = "test query"; + public static final Point TEST_POINT = Point.fromLngLat(10.10, 20.20); + + public static final BoundingBox TEST_BBOX = BoundingBox.fromPoints( + Point.fromLngLat(10.1, 20.2), + Point.fromLngLat(30.3, 50.5) + ); + + public static final V6ForwardGeocodingRequestOptions TEST_COMPLETE_FORWARD_OPTIONS = + createCompleteOptions(TEST_QUERY); + + public static final V6StructuredInputQuery TEST_COMPLETE_STRUCTURED_INPUT = + V6StructuredInputQuery.builder() + .addressLine1("test-address-line1") + .addressNumber("test-address-number") + .street("test-street") + .block("test-block") + .place("test-place") + .region("test-region") + .postcode("test-postcode") + .locality("test-locality") + .neighborhood("test-neighborhood") + .build(); + + + public static final V6ForwardGeocodingRequestOptions TEST_COMPLETE_STRUCTURED_INPUT_OPTIONS = + createCompleteOptions(TEST_COMPLETE_STRUCTURED_INPUT); + + public static final V6ReverseGeocodingRequestOptions TEST_COMPLETE_REVERSE_OPTIONS = + V6ReverseGeocodingRequestOptions.builder(TEST_POINT) + .country(Locale.FRANCE.getCountry(), Locale.ITALY.getCountry()) + .language(Locale.FRANCE.getLanguage()) + .limit(3) + .types(V6FeatureType.COUNTRY, V6FeatureType.PLACE) + .worldview(V6Worldview.USA) + .build(); + + private static V6ForwardGeocodingRequestOptions createCompleteOptions(V6StructuredInputQuery query) { + final V6ForwardGeocodingRequestOptions.Builder builder = V6ForwardGeocodingRequestOptions + .builder(query); + + return setTestParameters(builder).build(); + } + + private static V6ForwardGeocodingRequestOptions createCompleteOptions(String query) { + final V6ForwardGeocodingRequestOptions.Builder builder = V6ForwardGeocodingRequestOptions + .builder(query); + + return setTestParameters(builder).build(); + } + + private static V6ForwardGeocodingRequestOptions.Builder setTestParameters( + V6ForwardGeocodingRequestOptions.Builder builder + ) { + return builder + .autocomplete(false) + .boundingBox(TEST_BBOX) + .country(Locale.FRANCE.getCountry(), Locale.ITALY.getCountry()) + .language(Locale.FRANCE.getLanguage()) + .limit(7) + .proximity(Point.fromLngLat(10.1, 20.2)) + .types(V6FeatureType.COUNTRY, V6FeatureType.PLACE) + .worldview(V6Worldview.USA); + } +} diff --git a/services-geocoding/src/test/java/com/mapbox/api/geocoding/v6/V6GeocodingUtilsTest.java b/services-geocoding/src/test/java/com/mapbox/api/geocoding/v6/V6GeocodingUtilsTest.java new file mode 100644 index 000000000..541d0ba65 --- /dev/null +++ b/services-geocoding/src/test/java/com/mapbox/api/geocoding/v6/V6GeocodingUtilsTest.java @@ -0,0 +1,128 @@ +package com.mapbox.api.geocoding.v6; + +import com.mapbox.api.geocoding.v6.models.V6FeatureType; +import com.mapbox.geojson.BoundingBox; +import com.mapbox.geojson.Point; + +import org.junit.Test; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.Locale; +import java.util.Map; + +import static org.junit.Assert.assertEquals; + +public class V6GeocodingUtilsTest extends V6GeocodingTestUtils { + + @Test + public void testEmptyListSerialization() { + assertEquals( + "[]", + V6GeocodingUtils.serialize(Collections.emptyList()) + ); + } + + /** + * In this test we combine all predefined request options from tests + * {@link V6ForwardGeocodingRequestOptionsTest} and + * {@link V6ReverseGeocodingRequestOptionsTest} + */ + @Test + public void testOptionsSerialization() throws IOException { + final Map testData = new LinkedHashMap<>(); + + final V6ForwardGeocodingRequestOptions minimalStructuredInputOptions = + V6ForwardGeocodingRequestOptions + .builder(V6StructuredInputQuery.builder().addressNumber("test-address-number").build()) + .build(); + + testData.put( + minimalStructuredInputOptions, + loadCompressedJson("v6/forward_structured_input_request_options_minimal_serialised.json") + ); + + testData.put( + TEST_COMPLETE_STRUCTURED_INPUT_OPTIONS, + loadCompressedJson("v6/forward_structured_input_request_options_serialised.json") + ); + + final V6ForwardGeocodingRequestOptions minimalForwardOptions = V6ForwardGeocodingRequestOptions + .builder(TEST_QUERY) + .build(); + + testData.put( + minimalForwardOptions, + loadCompressedJson("v6/forward_request_options_minimal_serialised.json") + ); + + testData.put( + TEST_COMPLETE_FORWARD_OPTIONS, + loadCompressedJson("v6/forward_request_options_serialised.json") + ); + + final V6ReverseGeocodingRequestOptions minimalReverseOptions = V6ReverseGeocodingRequestOptions + .builder(TEST_POINT) + .build(); + + testData.put( + minimalReverseOptions, + loadCompressedJson("v6/reverse_request_options_minimal_serialised.json") + ); + + testData.put( + TEST_COMPLETE_REVERSE_OPTIONS, + loadCompressedJson("v6/reverse_request_options_serialised.json") + ); + + final String expectedJson = String.format( + Locale.US, + "[%s,%s,%s,%s,%s,%s]", + testData.values().toArray() + ); + + assertEquals( + removeJsonWhitespaces(expectedJson), + V6GeocodingUtils.serialize(testData.keySet()) + ); + } + + /** + * Test case from docs https://docs.mapbox.com/api/search/geocoding/#example-request-batch-geocoding + */ + @Test + public void testArbitraryOptionsSerialization() throws IOException { + final V6ForwardGeocodingRequestOptions forwardOptions = V6ForwardGeocodingRequestOptions + .builder("1600 Pennsylvania Avenue NW, Washington, DC 20500, United States") + .types(V6FeatureType.ADDRESS) + .limit(1) + .boundingBox(BoundingBox.fromLngLats(-80, 35, -70, 40)) + .build(); + + final V6StructuredInputQuery structuredInputQuery = V6StructuredInputQuery.builder() + .addressNumber("1600") + .street("Pennsylvania Avenue NW") + .postcode("20500") + .place("Washington, DC") + .build(); + + final V6ForwardGeocodingRequestOptions structuredInputOptions = V6ForwardGeocodingRequestOptions + .builder(structuredInputQuery) + .country("us") + .build(); + + final V6ReverseGeocodingRequestOptions reverseOptions = V6ReverseGeocodingRequestOptions + .builder(Point.fromLngLat(-73.986136, 40.748895)) + .types(V6FeatureType.ADDRESS) + .build(); + + assertEquals( + removeJsonWhitespaces(loadCompressedJson("v6/batch_request_options.json")), + V6GeocodingUtils.serialize( + Arrays.asList(forwardOptions, structuredInputOptions, reverseOptions) + ) + ); + } +} diff --git a/services-geocoding/src/test/java/com/mapbox/api/geocoding/v6/V6ReverseGeocodingRequestOptionsTest.java b/services-geocoding/src/test/java/com/mapbox/api/geocoding/v6/V6ReverseGeocodingRequestOptionsTest.java new file mode 100644 index 000000000..25970d9cd --- /dev/null +++ b/services-geocoding/src/test/java/com/mapbox/api/geocoding/v6/V6ReverseGeocodingRequestOptionsTest.java @@ -0,0 +1,112 @@ +package com.mapbox.api.geocoding.v6; + +import com.mapbox.api.geocoding.v6.models.V6FeatureType; +import com.mapbox.api.geocoding.v6.models.V6Worldview; + +import org.junit.Test; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Locale; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +public class V6ReverseGeocodingRequestOptionsTest extends V6GeocodingTestUtils { + + @Test + public void testUnspecifiedParametersAreNull() { + final V6ReverseGeocodingRequestOptions options = V6ReverseGeocodingRequestOptions + .builder(TEST_POINT) + .build(); + + assertNull(options.country()); + assertNull(options.language()); + assertNull(options.limit()); + assertNull(options.types()); + assertNull(options.worldview()); + } + + @Test + public void testPoint() { + final V6ReverseGeocodingRequestOptions options = V6ReverseGeocodingRequestOptions + .builder(TEST_POINT) + .build(); + + assertEquals(TEST_POINT.longitude(), options.longitude(), DELTA); + assertEquals(TEST_POINT.latitude(), options.latitude(), DELTA); + } + + @Test + public void testCountry() { + final V6ReverseGeocodingRequestOptions options = V6ReverseGeocodingRequestOptions + .builder(TEST_POINT) + .country(Locale.FRANCE.getCountry(), Locale.ITALY.getCountry()) + .build(); + + assertEquals("FR,IT", options.country()); + } + + @Test + public void testLanguage() { + final V6ReverseGeocodingRequestOptions options = V6ReverseGeocodingRequestOptions + .builder(TEST_POINT) + .language(Locale.FRANCE.getLanguage()) + .build(); + + assertEquals("fr", options.language()); + } + + @Test + public void testLimit() { + final V6ReverseGeocodingRequestOptions options = V6ReverseGeocodingRequestOptions + .builder(TEST_POINT) + .limit(3) + .build(); + + assertEquals(Integer.valueOf(3), options.limit()); + } + + @Test + public void testTypes() { + final V6ReverseGeocodingRequestOptions options = V6ReverseGeocodingRequestOptions + .builder(TEST_POINT) + .types(V6FeatureType.COUNTRY, V6FeatureType.PLACE) + .build(); + + assertEquals("country,place", options.apiFormattedTypes()); + assertEquals(Arrays.asList("country", "place"), options.types()); + } + + @Test + public void testWorldview() { + final V6ReverseGeocodingRequestOptions options = V6ReverseGeocodingRequestOptions + .builder(TEST_POINT) + .worldview(V6Worldview.USA) + .build(); + + assertEquals("us", options.worldview()); + } + + @Test + public void testCompleteReverseOptionsJsonSerialization() throws IOException { + final String json = TEST_COMPLETE_REVERSE_OPTIONS.toJson(); + assertEquals( + loadCompressedJson("v6/reverse_request_options_serialised.json"), + json + ); + } + + @Test + public void testMinimalReverseOptionsJsonSerialization() throws IOException { + final V6ReverseGeocodingRequestOptions options = V6ReverseGeocodingRequestOptions + .builder(TEST_POINT) + .build(); + + final String json = options.toJson(); + assertEquals( + loadCompressedJson("v6/reverse_request_options_minimal_serialised.json"), + json + ); + } +} diff --git a/services-geocoding/src/test/java/com/mapbox/api/geocoding/v6/V6StructuredInputQueryTest.java b/services-geocoding/src/test/java/com/mapbox/api/geocoding/v6/V6StructuredInputQueryTest.java new file mode 100644 index 000000000..3054250c4 --- /dev/null +++ b/services-geocoding/src/test/java/com/mapbox/api/geocoding/v6/V6StructuredInputQueryTest.java @@ -0,0 +1,80 @@ +package com.mapbox.api.geocoding.v6; + +import com.mapbox.core.exceptions.ServicesException; + +import org.junit.Test; +import org.junit.function.ThrowingRunnable; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertThrows; +import static org.junit.Assert.assertTrue; + +public class V6StructuredInputQueryTest { + + @Test + public void testEmptyStructuredInput() { + final Exception e = assertThrows(ServicesException.class, new ThrowingRunnable() { + @Override + public void run() { + V6StructuredInputQuery.builder().build(); + } + }); + + assertTrue(e.getMessage().contains("At least one component must be non null")); + } + + @Test + public void testStructuredInputWithAllValuesSet() { + final V6StructuredInputQuery query = V6StructuredInputQuery.builder() + .addressLine1("test-address-line1") + .addressNumber("test-address-number") + .street("test-street") + .block("test-block") + .place("test-place") + .region("test-region") + .postcode("test-postcode") + .locality("test-locality") + .neighborhood("test-neighborhood") + .build(); + + assertEquals("test-address-line1", query.addressLine1()); + assertEquals("test-address-number", query.addressNumber()); + assertEquals("test-street", query.street()); + assertEquals("test-block", query.block()); + assertEquals("test-place", query.place()); + assertEquals("test-region", query.region()); + assertEquals("test-postcode", query.postcode()); + assertEquals("test-locality", query.locality()); + assertEquals("test-neighborhood", query.neighborhood()); + } + + @Test + public void testUnspecifiedValuesAreNull() { + final V6StructuredInputQuery queryWithAddress = V6StructuredInputQuery.builder() + .addressNumber("test-address-number") + .build(); + + assertEquals("test-address-number", queryWithAddress.addressNumber()); + assertNull(queryWithAddress.street()); + assertNull(queryWithAddress.block()); + assertNull(queryWithAddress.place()); + assertNull(queryWithAddress.region()); + assertNull(queryWithAddress.postcode()); + assertNull(queryWithAddress.locality()); + assertNull(queryWithAddress.neighborhood()); + + final V6StructuredInputQuery queryWithAddressLine1 = V6StructuredInputQuery.builder() + .addressLine1("test-address-line1") + .build(); + + assertEquals("test-address-line1", queryWithAddressLine1.addressLine1()); + assertNull(queryWithAddressLine1.addressNumber()); + + final V6StructuredInputQuery queryWithStreet = V6StructuredInputQuery.builder() + .street("test-street") + .build(); + + assertNull(queryWithStreet.addressNumber()); + } +} diff --git a/services-geocoding/src/test/java/com/mapbox/api/geocoding/v6/models/ModelDataFactory.java b/services-geocoding/src/test/java/com/mapbox/api/geocoding/v6/models/ModelDataFactory.java new file mode 100644 index 000000000..476687ee7 --- /dev/null +++ b/services-geocoding/src/test/java/com/mapbox/api/geocoding/v6/models/ModelDataFactory.java @@ -0,0 +1,45 @@ +package com.mapbox.api.geocoding.v6.models; + +import java.util.List; + +public class ModelDataFactory { + + public static V6Coordinates createV6Coordinates( + Double longitude, + Double latitude, + String accuracy, + List routablePoints + ) { + return new AutoValue_V6Coordinates(longitude, latitude, accuracy, routablePoints); + } + + public static V6RoutablePoint createV6RoutablePoint( + Double longitude, + Double latitude, + String name + ) { + return new AutoValue_V6RoutablePoint(longitude, latitude, name); + } + + public static V6MatchCode createV6MatchCode( + String addressNumber, + String street, + String locality, + String place, + String postcode, + String region, + String country, + String confidence + ) { + return new AutoValue_V6MatchCode.Builder() + .addressNumber(addressNumber) + .street(street) + .locality(locality) + .place(place) + .postcode(postcode) + .region(region) + .country(country) + .confidence(confidence) + .build(); + } +} diff --git a/services-geocoding/src/test/java/com/mapbox/api/geocoding/v6/models/V6BatchResponseTest.java b/services-geocoding/src/test/java/com/mapbox/api/geocoding/v6/models/V6BatchResponseTest.java new file mode 100644 index 000000000..97aa62926 --- /dev/null +++ b/services-geocoding/src/test/java/com/mapbox/api/geocoding/v6/models/V6BatchResponseTest.java @@ -0,0 +1,120 @@ +package com.mapbox.api.geocoding.v6.models; + +import com.google.gson.JsonElement; +import com.mapbox.core.TestUtils; +import com.mapbox.geojson.Point; + +import org.junit.Test; + +import java.io.IOException; +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +public class V6BatchResponseTest extends TestUtils { + + @Test + public void responseParsingTest() throws IOException { + final String rawResponse = loadJsonFixture("v6/batch_response_non_empty_valid.json"); + + final V6BatchResponse response = V6BatchResponse.fromJson(rawResponse); + assertNotNull(response); + + final List responses = response.responses(); + assertNotNull(responses); + assertEquals(3, responses.size()); + + final V6Response firstResponse = responses.get(0); + assertEquals(2, responses.get(0).features().size()); + + final List features = firstResponse.features(); + assertEquals("FeatureCollection", firstResponse.type()); + assertEquals("Test attribution", firstResponse.attribution()); + assertEquals(2, features.size()); + + final V6Feature feature = features.get(0); + assertEquals(Point.fromLngLat(-77.03655, 38.89768), feature.geometry()); + assertEquals("dXJuOm1ieGFkcjo2YzdhYjM4Yi05YzM4LTQ3ZDItODFkMS1jYzZlYjg5YzliMWM", feature.id()); + assertEquals("Feature", feature.type()); + + final V6Properties properties = feature.properties(); + assertNotNull(properties); + + assertEquals( + "dXJuOm1ieGFkcjo2YzdhYjM4Yi05YzM4LTQ3ZDItODFkMS1jYzZlYjg5YzliMWM", + properties.mapboxId() + ); + assertEquals("address", properties.featureType()); + assertEquals("1600 Pennsylvania Avenue Northwest", properties.name()); + assertEquals( + "Washington, District of Columbia 20500, United States", + properties.placeFormatted() + ); + + assertEquals( + "Washington, D.C.", + properties.namePreferred() + ); + + assertEquals( + "1600 Pennsylvania Avenue Northwest, DC 20005, USA", + properties.fullAddress() + ); + + assertEquals(Arrays.asList(-81.0, 31.0, -71.0, 41.0), properties.bbox()); + + final V6Coordinates coordinates = ModelDataFactory.createV6Coordinates( + -77.03655, 38.89768, "rooftop", null + ); + assertEquals(coordinates, properties.coordinates()); + assertEquals(Point.fromLngLat(-77.03655, 38.89768), coordinates.point()); + + final V6MatchCode matchCode = ModelDataFactory.createV6MatchCode( + "matched", + "matched", + "not_applicable", + "matched", + "matched", + "matched", + "inferred", + "exact" + ); + + assertEquals(matchCode, properties.matchCode()); + + final V6Context context = properties.context(); + assertNotNull(context); + + final V6ContextElement neighborhoodContext = context.neighborhood(); + assertNotNull(neighborhoodContext); + + final V6ContextAddress addressContext = context.address(); + assertNotNull(addressContext); + assertEquals("1600", addressContext.addressNumber()); + assertEquals("Pennsylvania Avenue Northwest", addressContext.streetName()); + + + final Map unrecognized = + neighborhoodContext.getUnrecognizedJsonProperties(); + assertNotNull(unrecognized); + assertNotNull(unrecognized.get("translations")); + + assertEquals(1, responses.get(1).features().size()); + assertEquals(0, responses.get(2).features().size()); + } + + @Test + public void emptyResponseParsingTest() throws IOException { + final String rawResponse = loadJsonFixture("v6/batch_response_empty.json"); + + final V6BatchResponse response = V6BatchResponse.fromJson(rawResponse); + assertNotNull(response); + + final List responses = response.responses(); + assertNotNull(responses); + assertEquals(0, responses.size()); + } +} diff --git a/services-geocoding/src/test/java/com/mapbox/api/geocoding/v6/models/V6ResponseTest.java b/services-geocoding/src/test/java/com/mapbox/api/geocoding/v6/models/V6ResponseTest.java new file mode 100644 index 000000000..bc4dbeff5 --- /dev/null +++ b/services-geocoding/src/test/java/com/mapbox/api/geocoding/v6/models/V6ResponseTest.java @@ -0,0 +1,94 @@ +package com.mapbox.api.geocoding.v6.models; + +import com.mapbox.core.TestUtils; +import com.mapbox.geojson.Point; + +import org.junit.Test; + +import java.io.IOException; +import java.util.Arrays; +import java.util.List; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +public class V6ResponseTest extends TestUtils { + + @Test + public void responseParsingTest() throws IOException { + final String rawResponse = loadJsonFixture("v6/response_non_empty_valid.json"); + + final V6Response response = V6Response.fromJson(rawResponse); + assertNotNull(response); + + final List features = response.features(); + assertEquals("FeatureCollection", response.type()); + assertEquals("Test attribution", response.attribution()); + assertEquals(2, features.size()); + + final V6Feature feature = features.get(0); + assertEquals(Point.fromLngLat(-77.03394, 38.899929), feature.geometry()); + assertEquals("dXJuOm1ieGFkcjowMmVhNDY5OS0zY2IxLTRkOTctOGJhMi0yNWRkMDA3NDIzNGQ", feature.id()); + assertEquals("Feature", feature.type()); + + final V6Properties properties = feature.properties(); + assertNotNull(properties); + + assertEquals( + "dXJuOm1ieGFkcjowMmVhNDY5OS0zY2IxLTRkOTctOGJhMi0yNWRkMDA3NDIzNGQ", + properties.mapboxId() + ); + assertEquals("address", properties.featureType()); + assertEquals("740 15th Street Northwest", properties.name()); + assertEquals( + "Washington, District of Columbia 20005, United States", + properties.placeFormatted() + ); + + assertEquals( + "Washington, D.C.", + properties.namePreferred() + ); + + assertEquals( + "740 15th Street Northwest, DC 20005, USA", + properties.fullAddress() + ); + + final V6Coordinates coordinates = ModelDataFactory.createV6Coordinates( + -77.03394, 38.899929, "rooftop", + Arrays.asList(ModelDataFactory.createV6RoutablePoint(-81.5244, 39.3454, "default")) + ); + assertEquals(coordinates, properties.coordinates()); + assertEquals(Point.fromLngLat(-77.03394, 38.899929), coordinates.point()); + + assertEquals(Arrays.asList(-80.0, 35.0, -70.0, 40.0), properties.bbox()); + + final V6MatchCode matchCode = ModelDataFactory.createV6MatchCode( + "matched", + "matched", + "not_applicable", + "matched", + "matched", + "matched", + "inferred", + "exact" + ); + + assertEquals(matchCode, properties.matchCode()); + + final V6Context context = properties.context(); + assertNotNull(context); + } + + @Test + public void emptyResponseParsingTest() throws IOException { + final String rawResponse = loadJsonFixture("v6/response_empty.json"); + + final V6Response response = V6Response.fromJson(rawResponse); + assertNotNull(response); + + final List features = response.features(); + assertEquals(0, features.size()); + } +} diff --git a/services-geocoding/src/test/resources/v6/batch_request_options.json b/services-geocoding/src/test/resources/v6/batch_request_options.json new file mode 100644 index 000000000..9fa4a1cdc --- /dev/null +++ b/services-geocoding/src/test/resources/v6/batch_request_options.json @@ -0,0 +1,29 @@ +[ + { + "q": "1600 Pennsylvania Avenue NW, Washington, DC 20500, United States", + "bbox": [ + -80.0, + 35.0, + -70.0, + 40.0 + ], + "limit": 1, + "types": [ + "address" + ] + }, + { + "address_number": "1600", + "street": "Pennsylvania Avenue NW", + "place": "Washington, DC", + "postcode": "20500", + "country": "us" + }, + { + "longitude": -73.986136, + "latitude": 40.748895, + "types": [ + "address" + ] + } +] \ No newline at end of file diff --git a/services-geocoding/src/test/resources/v6/batch_response_empty.json b/services-geocoding/src/test/resources/v6/batch_response_empty.json new file mode 100644 index 000000000..1ce0a8eeb --- /dev/null +++ b/services-geocoding/src/test/resources/v6/batch_response_empty.json @@ -0,0 +1,4 @@ +{ + "batch": [ + ] +} \ No newline at end of file diff --git a/services-geocoding/src/test/resources/v6/batch_response_non_empty_valid.json b/services-geocoding/src/test/resources/v6/batch_response_non_empty_valid.json new file mode 100644 index 000000000..e282ed3af --- /dev/null +++ b/services-geocoding/src/test/resources/v6/batch_response_non_empty_valid.json @@ -0,0 +1,220 @@ +{ + "batch": [ + { + "type": "FeatureCollection", + "features": [ + { + "type": "Feature", + "id": "dXJuOm1ieGFkcjo2YzdhYjM4Yi05YzM4LTQ3ZDItODFkMS1jYzZlYjg5YzliMWM", + "geometry": { + "type": "Point", + "coordinates": [ + -77.03655, + 38.89768 + ] + }, + "properties": { + "mapbox_id": "dXJuOm1ieGFkcjo2YzdhYjM4Yi05YzM4LTQ3ZDItODFkMS1jYzZlYjg5YzliMWM", + "feature_type": "address", + "name": "1600 Pennsylvania Avenue Northwest", + "coordinates": { + "longitude": -77.03655, + "latitude": 38.89768, + "accuracy": "rooftop" + }, + "name_preferred": "Washington, D.C.", + "place_formatted": "Washington, District of Columbia 20500, United States", + "full_address": "1600 Pennsylvania Avenue Northwest, DC 20005, USA", + "bbox": [ + -81.0, + 31.0, + -71.0, + 41.0 + ], + "match_code": { + "address_number": "matched", + "street": "matched", + "postcode": "matched", + "place": "matched", + "region": "matched", + "locality": "not_applicable", + "country": "inferred", + "confidence": "exact" + }, + "context": { + "address": { + "mapbox_id": "dXJuOm1ieGFkcjo2YzdhYjM4Yi05YzM4LTQ3ZDItODFkMS1jYzZlYjg5YzliMWM", + "address_number": "1600", + "street_name": "Pennsylvania Avenue Northwest", + "name": "1600 Pennsylvania Avenue Northwest" + }, + "street": { + "mapbox_id": "dXJuOm1ieGFkcjo2YzdhYjM4Yi05YzM4LTQ3ZDItODFkMS1jYzZlYjg5YzliMWM", + "name": "Pennsylvania Avenue Northwest" + }, + "neighborhood": { + "mapbox_id": "dXJuOm1ieHBsYzpHYUVzN0E", + "name": "National Mall", + "alternate": { + "mapbox_id": "dXJuOm1ieHBsYzpEY1ZNN0E", + "name": "Franklin Mcpherson Square" + }, + "translations": { + "en": { + "language": "en", + "name": "National Mall" + }, + "ar": { + "language": "ar", + "name": "National Mall" + }, + "ja": { + "language": "ja", + "name": "National Mall" + } + } + }, + "postcode": { + "mapbox_id": "dXJuOm1ieHBsYzpBOEZPN0E", + "name": "20500" + }, + "place": { + "mapbox_id": "dXJuOm1ieHBsYzpGSmlvN0E", + "name": "Washington", + "wikidata_id": "Q61" + }, + "region": { + "mapbox_id": "dXJuOm1ieHBsYzpCUVRz", + "name": "District of Columbia", + "wikidata_id": "Q3551781", + "region_code": "DC", + "region_code_full": "US-DC" + }, + "country": { + "mapbox_id": "dXJuOm1ieHBsYzpJdXc", + "name": "United States", + "wikidata_id": "Q30", + "country_code": "US", + "country_code_alpha_3": "USA" + } + } + } + }, + { + "type": "Feature", + "id": "address.6017514948318094", + "geometry": { + "type": "Point", + "coordinates": [ + -77.0336269, + 38.8988057 + ] + }, + "properties": { + "mapbox_id": "address.6017514948318094", + "feature_type": "street", + "name": "Pennsylvania Ave Northwest", + "coordinates": { + "longitude": -77.0336269, + "latitude": 38.8988057 + }, + "name_preferred": "Washington, D.C.", + "place_formatted": "Washington, District of Columbia 20005, United States", + "full_address": "Pennsylvania Ave Northwest, DC 20005, USA", + "bbox": [ + -82.0, + 32.0, + -72.0, + 42.0 + ], + "context": { + "street": { + "mapbox_id": "address.6017514948318094", + "name": "Pennsylvania Ave Northwest" + }, + "neighborhood": { + "mapbox_id": "dXJuOm1ieHBsYzpDaXpzN0E", + "name": "Downtown", + "alternate": { + "mapbox_id": "dXJuOm1ieHBsYzpEY1ZNN0E", + "name": "Franklin Mcpherson Square" + } + }, + "postcode": { + "mapbox_id": "dXJuOm1ieHBsYzpBNTd1N0E", + "name": "20005" + }, + "place": { + "mapbox_id": "dXJuOm1ieHBsYzpGSmlvN0E", + "name": "Washington", + "wikidata_id": "Q61" + }, + "region": { + "mapbox_id": "dXJuOm1ieHBsYzpCUVRz", + "name": "District of Columbia", + "wikidata_id": "Q3551781", + "region_code": "DC", + "region_code_full": "US-DC" + }, + "country": { + "mapbox_id": "dXJuOm1ieHBsYzpJdXc", + "name": "United States", + "wikidata_id": "Q30", + "country_code": "US", + "country_code_alpha_3": "USA" + } + } + } + } + ], + "attribution": "Test attribution" + }, + { + "type": "FeatureCollection", + "features": [ + { + "type": "Feature", + "id": "dXJuOm1ieHBsYzpJdXc", + "geometry": { + "type": "Point", + "coordinates": [ + -97.9222112121185, + 39.3812661305678 + ] + }, + "properties": { + "mapbox_id": "dXJuOm1ieHBsYzpJdXc", + "feature_type": "country", + "name": "United States", + "coordinates": { + "longitude": -97.9222112121185, + "latitude": 39.3812661305678 + }, + "bbox": [ + -179.9, + 18.8164227, + -66.8847656, + 71.420291 + ], + "context": { + "country": { + "mapbox_id": "dXJuOm1ieHBsYzpJdXc", + "name": "United States", + "country_code": "US", + "country_code_alpha_3": "USA", + "wikidata_id": "Q30" + } + } + } + } + ], + "attribution": "NOTICE: © 2023 Mapbox and its suppliers. All rights reserved. Use of this data is subject to the Mapbox Terms of Service (https://www.mapbox.com/about/maps/). This response and the information it contains may not be retained." + }, + { + "type": "FeatureCollection", + "features": [ + ], + "attribution": "NOTICE: © 2023 Mapbox and its suppliers. All rights reserved. Use of this data is subject to the Mapbox Terms of Service (https://www.mapbox.com/about/maps/). This response and the information it contains may not be retained." + } + ] +} \ No newline at end of file diff --git a/services-geocoding/src/test/resources/v6/error_message.json b/services-geocoding/src/test/resources/v6/error_message.json new file mode 100644 index 000000000..fae2db378 --- /dev/null +++ b/services-geocoding/src/test/resources/v6/error_message.json @@ -0,0 +1,4 @@ +{ + "message": "Not Authorized - Invalid Token", + "error_code": "INVALID_TOKEN" +} \ No newline at end of file diff --git a/services-geocoding/src/test/resources/v6/forward_request_options_minimal_serialised.json b/services-geocoding/src/test/resources/v6/forward_request_options_minimal_serialised.json new file mode 100644 index 000000000..21a2e51cf --- /dev/null +++ b/services-geocoding/src/test/resources/v6/forward_request_options_minimal_serialised.json @@ -0,0 +1,3 @@ +{ + "q": "test query" +} \ No newline at end of file diff --git a/services-geocoding/src/test/resources/v6/forward_request_options_serialised.json b/services-geocoding/src/test/resources/v6/forward_request_options_serialised.json new file mode 100644 index 000000000..724c8be8a --- /dev/null +++ b/services-geocoding/src/test/resources/v6/forward_request_options_serialised.json @@ -0,0 +1,19 @@ +{ + "q": "test query", + "autocomplete": false, + "bbox": [ + 10.1, + 20.2, + 30.3, + 50.5 + ], + "country": "FR,IT", + "language": "fr", + "limit": 7, + "proximity": "10.1,20.2", + "types": [ + "country", + "place" + ], + "worldview": "us" +} \ No newline at end of file diff --git a/services-geocoding/src/test/resources/v6/forward_structured_input_request_options_minimal_serialised.json b/services-geocoding/src/test/resources/v6/forward_structured_input_request_options_minimal_serialised.json new file mode 100644 index 000000000..6a3ca2832 --- /dev/null +++ b/services-geocoding/src/test/resources/v6/forward_structured_input_request_options_minimal_serialised.json @@ -0,0 +1,3 @@ +{ + "address_number": "test-address-number" +} \ No newline at end of file diff --git a/services-geocoding/src/test/resources/v6/forward_structured_input_request_options_serialised.json b/services-geocoding/src/test/resources/v6/forward_structured_input_request_options_serialised.json new file mode 100644 index 000000000..72a90c5f1 --- /dev/null +++ b/services-geocoding/src/test/resources/v6/forward_structured_input_request_options_serialised.json @@ -0,0 +1,27 @@ +{ + "address_line1": "test-address-line1", + "address_number": "test-address-number", + "street": "test-street", + "block": "test-block", + "place": "test-place", + "region": "test-region", + "postcode": "test-postcode", + "locality": "test-locality", + "neighborhood": "test-neighborhood", + "autocomplete": false, + "bbox": [ + 10.1, + 20.2, + 30.3, + 50.5 + ], + "country": "FR,IT", + "language": "fr", + "limit": 7, + "proximity": "10.1,20.2", + "types": [ + "country", + "place" + ], + "worldview": "us" +} \ No newline at end of file diff --git a/services-geocoding/src/test/resources/v6/response_empty.json b/services-geocoding/src/test/resources/v6/response_empty.json new file mode 100644 index 000000000..9501f4a46 --- /dev/null +++ b/services-geocoding/src/test/resources/v6/response_empty.json @@ -0,0 +1,6 @@ +{ + "type": "FeatureCollection", + "features": [ + ], + "attribution": "Test attribution" +} \ No newline at end of file diff --git a/services-geocoding/src/test/resources/v6/response_non_empty_valid.json b/services-geocoding/src/test/resources/v6/response_non_empty_valid.json new file mode 100644 index 000000000..c9ebfbe71 --- /dev/null +++ b/services-geocoding/src/test/resources/v6/response_non_empty_valid.json @@ -0,0 +1,171 @@ +{ + "type": "FeatureCollection", + "features": [ + { + "type": "Feature", + "id": "dXJuOm1ieGFkcjowMmVhNDY5OS0zY2IxLTRkOTctOGJhMi0yNWRkMDA3NDIzNGQ", + "geometry": { + "type": "Point", + "coordinates": [ + -77.03394, + 38.899929 + ] + }, + "properties": { + "mapbox_id": "dXJuOm1ieGFkcjowMmVhNDY5OS0zY2IxLTRkOTctOGJhMi0yNWRkMDA3NDIzNGQ", + "feature_type": "address", + "name": "740 15th Street Northwest", + "coordinates": { + "longitude": -77.03394, + "latitude": 38.899929, + "accuracy": "rooftop", + "routable_points": [ + { + "name": "default", + "longitude": -81.5244, + "latitude": 39.3454 + } + ] + }, + "name_preferred": "Washington, D.C.", + "place_formatted": "Washington, District of Columbia 20005, United States", + "full_address": "740 15th Street Northwest, DC 20005, USA", + "bbox": [ + -80.0, + 35.0, + -70.0, + 40.0 + ], + "match_code": { + "address_number": "matched", + "street": "matched", + "postcode": "matched", + "place": "matched", + "region": "matched", + "locality": "not_applicable", + "country": "inferred", + "confidence": "exact" + }, + "context": { + "address": { + "mapbox_id": "dXJuOm1ieGFkcjowMmVhNDY5OS0zY2IxLTRkOTctOGJhMi0yNWRkMDA3NDIzNGQ", + "address_number": "740", + "street_name": "15th Street Northwest", + "name": "740 15th Street Northwest" + }, + "street": { + "mapbox_id": "dXJuOm1ieGFkcjowMmVhNDY5OS0zY2IxLTRkOTctOGJhMi0yNWRkMDA3NDIzNGQ", + "name": "15th Street Northwest" + }, + "neighborhood": { + "mapbox_id": "dXJuOm1ieHBsYzpDaXpzN0E", + "name": "Downtown", + "alternate": { + "mapbox_id": "dXJuOm1ieHBsYzpEY1ZNN0E", + "name": "Franklin Mcpherson Square" + } + }, + "postcode": { + "mapbox_id": "dXJuOm1ieHBsYzpBNTd1N0E", + "name": "20005" + }, + "place": { + "mapbox_id": "dXJuOm1ieHBsYzpGSmlvN0E", + "name": "Washington", + "wikidata_id": "Q61" + }, + "region": { + "mapbox_id": "dXJuOm1ieHBsYzpCUVRz", + "name": "District of Columbia", + "wikidata_id": "Q3551781", + "region_code": "DC", + "region_code_full": "US-DC" + }, + "country": { + "mapbox_id": "dXJuOm1ieHBsYzpJdXc", + "name": "United States", + "wikidata_id": "Q30", + "country_code": "US", + "country_code_alpha_3": "USA" + } + } + } + }, + { + "type": "Feature", + "id": "dXJuOm1ieGFkcjpkOGViMTNiMC0wMGYxLTRiZWYtODRkNC01YzNlZjE1ZDYzYzM", + "geometry": { + "type": "Point", + "coordinates": [ + -76.983637, + 38.899496 + ] + }, + "properties": { + "mapbox_id": "dXJuOm1ieGFkcjpkOGViMTNiMC0wMGYxLTRiZWYtODRkNC01YzNlZjE1ZDYzYzM", + "feature_type": "address", + "name": "740 15th Street Northeast", + "coordinates": { + "longitude": -76.983637, + "latitude": 38.899496, + "accuracy": "proximate" + }, + "place_formatted": "Washington, District of Columbia 20002, United States", + "match_code": { + "address_number": "plausible", + "street": "unmatched", + "postcode": "unmatched", + "place": "matched", + "region": "matched", + "locality": "not_applicable", + "country": "inferred", + "confidence": "low" + }, + "context": { + "address": { + "mapbox_id": "dXJuOm1ieGFkcjpkOGViMTNiMC0wMGYxLTRiZWYtODRkNC01YzNlZjE1ZDYzYzM", + "address_number": "740", + "street_name": "15th Street Northeast", + "name": "740 15th Street Northeast" + }, + "street": { + "mapbox_id": "dXJuOm1ieGFkcjpkOGViMTNiMC0wMGYxLTRiZWYtODRkNC01YzNlZjE1ZDYzYzM", + "name": "15th Street Northeast" + }, + "neighborhood": { + "mapbox_id": "dXJuOm1ieHBsYzpHaUlNN0E", + "name": "NoMa", + "alternate": { + "mapbox_id": "dXJuOm1ieHBsYzpJT3lzN0E", + "name": "Rosedale" + } + }, + "postcode": { + "mapbox_id": "dXJuOm1ieHBsYzpBNTZPN0E", + "name": "20002" + }, + "place": { + "mapbox_id": "dXJuOm1ieHBsYzpGSmlvN0E", + "name": "Washington", + "wikidata_id": "Q61" + }, + "region": { + "mapbox_id": "dXJuOm1ieHBsYzpCUVRz", + "name": "District of Columbia", + "wikidata_id": "Q3551781", + "region_code": "DC", + "region_code_full": "US-DC" + }, + "country": { + "mapbox_id": "dXJuOm1ieHBsYzpJdXc", + "name": "United States", + "wikidata_id": "Q30", + "country_code": "US", + "country_code_alpha_3": "USA" + } + } + } + } + ], + "attribution": "Test attribution" +} \ No newline at end of file diff --git a/services-geocoding/src/test/resources/v6/reverse_request_options_minimal_serialised.json b/services-geocoding/src/test/resources/v6/reverse_request_options_minimal_serialised.json new file mode 100644 index 000000000..3878e4d3d --- /dev/null +++ b/services-geocoding/src/test/resources/v6/reverse_request_options_minimal_serialised.json @@ -0,0 +1,4 @@ +{ + "longitude": 10.1, + "latitude": 20.2 +} \ No newline at end of file diff --git a/services-geocoding/src/test/resources/v6/reverse_request_options_serialised.json b/services-geocoding/src/test/resources/v6/reverse_request_options_serialised.json new file mode 100644 index 000000000..64e681488 --- /dev/null +++ b/services-geocoding/src/test/resources/v6/reverse_request_options_serialised.json @@ -0,0 +1,12 @@ +{ + "longitude": 10.1, + "latitude": 20.2, + "country": "FR,IT", + "language": "fr", + "limit": 3, + "types": [ + "country", + "place" + ], + "worldview": "us" +} \ No newline at end of file