diff --git a/Makefile b/Makefile index 8f4cf8ce0..0795e529d 100644 --- a/Makefile +++ b/Makefile @@ -118,19 +118,19 @@ directions-fixtures: curl "https://api.mapbox.com/directions/v5/mapbox/driving/-122.03067988107114,37.331808179989494;-122.03178702099605,37.3302383113533?voice_units=imperial&roundabout_exits=true&geometries=polyline&overview=full&steps=true&voice_instructions=true&banner_instructions=true&access_token=$(MAPBOX_ACCESS_TOKEN)" \ -o services-directions/src/test/resources/directions_v5_banner_text.json - # Directions: route with maxspeed + # Directions: route with maxspeed curl "https://api.mapbox.com/directions/v5/mapbox/driving-traffic/9.950072,52.150015;7.569915,52.916751?alternatives=true&geometries=polyline6&overview=full&steps=true&bearings=%3B&continue_straight=true&annotations=maxspeed&language=en&access_token=$(MAPBOX_ACCESS_TOKEN)" \ -o services-directions/src/test/resources/directions_v5_max_speed_annotation.json - # Directions: route with sub (and lane data) in BannerInstructions + # Directions: route with sub (and lane data) in BannerInstructions curl "https://api.mapbox.com/directions/v5/mapbox/driving/-122.403561,37.777689;-122.405786,37.770369.json?access_token=$(MAPBOX_ACCESS_TOKEN)&steps=true&geometries=polyline&banner_instructions=true" \ -o services-directions/src/test/resources/directions_v5_banner_instructions.json - # Directions: route with approaches in request + # Directions: route with approaches in request curl "https://api.mapbox.com/directions/v5/mapbox/driving/13.4301,52.5109;13.432507621760521,52.501725088556014?approaches=unrestricted;curb&access_token=$(MAPBOX_ACCESS_TOKEN)" \ -o services-directions/src/test/resources/directions_v5_approaches.json - # Directions: includes waypoint_names + # Directions: includes waypoint_names curl "https://api.mapbox.com/directions/v5/mapbox/cycling/-122.42,37.78;-77.03,38.91?steps=true&voice_instructions=true&banner_instructions=true&voice_units=imperial&waypoint_names=Home;Work&access_token=$(MAPBOX_ACCESS_TOKEN)" \ -o services-directions/src/test/resources/directions_v5_waypoint_names.json @@ -150,11 +150,11 @@ mapmatching-fixtures: curl "https://api.mapbox.com/matching/v5/mapbox/driving/2.344003915786743,48.85805170891599;2.346750497817993,48.85727523615161;2.348681688308716,48.85936462637049;2.349550724029541,48.86084691113991;2.349550724029541,48.8608892614883;2.349625825881958,48.86102337068847;2.34982967376709,48.86125629633996?steps=true&tidy=true&waypoints=0;6&waypoint_names=Home;Work&banner_instructions=true&access_token=$(MAPBOX_ACCESS_TOKEN)" \ -o services-matching/src/test/resources/mapmatching_v5_waypoint_names.json - # MapMatching with valid voiceLanguage + # MapMatching with valid voiceLanguage curl "https://api.mapbox.com/matching/v5/mapbox/driving/$(MAP_MATCHING_COORDINATES)?steps=true&overview=full&geometries=polyline6&roundabout_exits=true&voice_instructions=true&language=en&access_token=$(MAPBOX_ACCESS_TOKEN)" \ -o services-matching/src/test/resources/map_matching_v5_voice_language.json - # MapMatching with invalid voiceLanguage + # MapMatching with invalid voiceLanguage curl "https://api.mapbox.com/matching/v5/mapbox/driving/$(MAP_MATCHING_COORDINATES)?steps=true&overview=full&geometries=polyline6&roundabout_exits=true&voice_instructions=true&language=he&access_token=$(MAPBOX_ACCESS_TOKEN)" \ -o services-matching/src/test/resources/map_matching_v5_invalid_voice_language.json @@ -169,6 +169,18 @@ optimization-fixtures: curl "https://api.mapbox.com/optimized-trips/v1/mapbox/driving/13.388860,52.517037;13.397634,52.529407;13.428555,52.523219;13.418555,52.523215?roundtrip=true&distributions=3,1&access_token=$(MAPBOX_ACCESS_TOKEN)" \ -o services-optimization/src/test/resources/optimized_trip_distributions.json +tilequery-fixtures: + # Fetch features at Fort Mason, CA + curl "https://api.mapbox.com/v4/mapbox.mapbox-streets-v7/tilequery/-122.42901,37.806332.json?access_token=$(MAPBOX_ACCESS_TOKEN)" \ + -o services-tilequery/src/test/resources/tilequery.json + + # Fetch features at Fort Mason, CA + curl "https://api.mapbox.com/v4/mapbox.mapbox-streets-v7/tilequery/-122.42901,37.806332.json?access_token=$(MAPBOX_ACCESS_TOKEN)&" \ + -o services-tilequery/src/test/resources/tilequery-all-params.json + + # batch request for several National parks + curl "https://api.mapbox.com/v4/mapbox.mapbox-streets-v7/tilequery/-122.42901, 37.806332.json?access_token=$(MAPBOX_ACCESS_TOKEN)&radius=500&layers=poi_label,building&dedupe=true&geometry=point&limit=2" \ + -o services-tilequery/src/test/resources/tilequery_batch.json + clean: ./gradlew clean - diff --git a/samples/build.gradle b/samples/build.gradle index 051ecbb66..feccd6c29 100644 --- a/samples/build.gradle +++ b/samples/build.gradle @@ -5,19 +5,20 @@ sourceCompatibility = JavaVersion.VERSION_1_8 targetCompatibility = JavaVersion.VERSION_1_8 dependencies { - implementation project(":services-geocoding") - implementation project(":services-optimization") - implementation project(":services-geojson") - implementation project(":services-matching") - implementation project(":services-staticmap") - implementation project(":services-speech") + implementation project(":services-geocoding") + implementation project(":services-optimization") + implementation project(":services-geojson") + implementation project(":services-matching") + implementation project(":services-staticmap") + implementation project(":services-speech") + implementation project(":services-tilequery") } buildConfig { - packageName = 'com.mapbox.sample' - buildConfigField 'String', 'MAPBOX_ACCESS_TOKEN', System.getenv("MAPBOX_ACCESS_TOKEN") + packageName = 'com.mapbox.sample' + buildConfigField 'String', 'MAPBOX_ACCESS_TOKEN', System.getenv("MAPBOX_ACCESS_TOKEN") } sonarqube { - skipProject = true -} \ No newline at end of file + skipProject = true +} diff --git a/samples/src/main/java/com/mapbox/samples/BasicTilequery.java b/samples/src/main/java/com/mapbox/samples/BasicTilequery.java new file mode 100644 index 000000000..c07fdeafb --- /dev/null +++ b/samples/src/main/java/com/mapbox/samples/BasicTilequery.java @@ -0,0 +1,47 @@ +package com.mapbox.samples; + +import com.mapbox.api.tilequery.MapboxTilequery; +import com.mapbox.geojson.Feature; +import com.mapbox.geojson.FeatureCollection; +import com.mapbox.geojson.Point; +import com.mapbox.sample.BuildConfig; + +import retrofit2.Call; +import retrofit2.Callback; +import retrofit2.Response; + +public class BasicTilequery { + + public static void main(String[] args) { + MapboxTilequery tilequery = MapboxTilequery.builder() + .accessToken(BuildConfig.MAPBOX_ACCESS_TOKEN) + .mapIds("mapbox.mapbox-streets-v7") + .query(Point.fromLngLat(-122.42901, 37.806332)) + .radius(500) + .limit(2) + .geometry("point") + .dedupe(true) + .layers("poi_label,building") + .build(); + + tilequery.enqueueCall(new Callback() { + @Override + public void onResponse(Call call, Response response) { + System.out.println("Total results: " + response.body().features().size()); + for (Feature feature : response.body().features()) { + if (feature.properties().has("name")) { + System.out.println("" + feature.properties().get("name").getAsString()); + } else { + System.out.println("Unnamed feature."); + } + } + } + + @Override + public void onFailure(Call call, Throwable t) { + System.out.println("Request failed: " + t.getMessage()); + t.printStackTrace(); + } + }); + } +} diff --git a/services-tilequery/.gitignore b/services-tilequery/.gitignore new file mode 100644 index 000000000..796b96d1c --- /dev/null +++ b/services-tilequery/.gitignore @@ -0,0 +1 @@ +/build diff --git a/services-tilequery/build.gradle b/services-tilequery/build.gradle new file mode 100644 index 000000000..0c1ca203a --- /dev/null +++ b/services-tilequery/build.gradle @@ -0,0 +1,20 @@ +apply plugin: 'java-library' + +dependencies { + api project(":services-core") + api project(":services-geojson") + + // Annotations + compileOnly dependenciesList.supportAnnotation + + // AutoValue + compileOnly dependenciesList.autoValue + compileOnly dependenciesList.autoValueGson + + // Test Dependencies + testImplementation dependenciesList.okhttp3Mockwebserver + testImplementation project(path: ':services-core', configuration: 'testOutput') +} + +apply from: "${rootDir}/gradle/checkstyle.gradle" +apply from: "${rootDir}/gradle/jacoco.gradle" diff --git a/services-tilequery/src/main/java/com/mapbox/api/tilequery/MapboxTilequery.java b/services-tilequery/src/main/java/com/mapbox/api/tilequery/MapboxTilequery.java new file mode 100644 index 000000000..98d98d8a5 --- /dev/null +++ b/services-tilequery/src/main/java/com/mapbox/api/tilequery/MapboxTilequery.java @@ -0,0 +1,322 @@ +package com.mapbox.api.tilequery; + +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; + +import com.google.auto.value.AutoValue; +import com.google.gson.GsonBuilder; +import com.mapbox.core.MapboxService; +import com.mapbox.core.constants.Constants; +import com.mapbox.core.exceptions.ServicesException; +import com.mapbox.core.utils.MapboxUtils; +import com.mapbox.core.utils.TextUtils; +import com.mapbox.geojson.BoundingBox; +import com.mapbox.geojson.FeatureCollection; +import com.mapbox.geojson.Geometry; +import com.mapbox.geojson.Point; +import com.mapbox.geojson.gson.BoundingBoxDeserializer; +import com.mapbox.geojson.gson.GeoJsonAdapterFactory; +import com.mapbox.geojson.gson.GeometryDeserializer; +import com.mapbox.geojson.gson.PointDeserializer; + +import java.io.IOException; +import java.util.List; +import java.util.Locale; + +import retrofit2.Call; +import retrofit2.Callback; +import retrofit2.Response; + + +/** + * The Mapbox Tilequery API allows you to retrieve data about specific features from a + * vector tileset, based on a given latitude and longitude. + * + * @see Tilequery API + * documentation + * + * @since 3.5.0 + */ +@AutoValue +public abstract class MapboxTilequery extends MapboxService { + private Call> batchCall; + + protected MapboxTilequery() { + super(TilequeryService.class); + } + + @Override + protected GsonBuilder getGsonBuilder() { + return new GsonBuilder() + .registerTypeAdapterFactory(GeoJsonAdapterFactory.create()) + .registerTypeAdapter(Point.class, new PointDeserializer()) + .registerTypeAdapter(BoundingBox.class, new BoundingBoxDeserializer()) + .registerTypeAdapter(Geometry.class, new GeometryDeserializer()); + } + + @Override + protected Call initializeCall() { + return getService().getCall( + mapIds(), + query(), + accessToken(), + radius(), + limit(), + dedupe(), + geometry(), + layers()); + } + + private Call> getBatchCall() { + // No need to recreate it + if (batchCall != null) { + return batchCall; + } + + batchCall = getService().getBatchCall( + mapIds(), + query(), + accessToken(), + radius(), + limit(), + dedupe(), + geometry(), + layers()); + + return batchCall; + } + + /** + * Wrapper method for Retrofit's {@link Call#execute()} call returning a batch response + * specific to the Tilequery API. + * + * @return the Tilequery batch response once the call completes successfully + * @throws IOException Signals that an I/O exception of some sort has occurred. + * @since 3.5.0 + */ + public Response> executeBatchCall() throws IOException { + return getBatchCall().execute(); + } + + /** + * Wrapper method for Retrofit's {@link Call#enqueue(Callback)} call returning a batch response + * specific to the Tilequery batch API. Use this method to make a tilequery request on the Main + * Thread. + * + * @param callback a {@link Callback} which is used once the {@link FeatureCollection} is created. + * @since 3.5.0 + */ + public void enqueueBatchCall(Callback> callback) { + getBatchCall().enqueue(callback); + } + + /** + * Wrapper method for Retrofit's {@link Call#cancel()} call, important to manually cancel call if + * the user dismisses the calling activity or no longer needs the returned results. + * + * @since 3.5.0 + */ + public void cancelBatchCall() { + getBatchCall().cancel(); + } + + /** + * Wrapper method for Retrofit's {@link Call#clone()} call, useful for getting call information. + * + * @return cloned call + * @since 3.5.0 + */ + public Call> cloneBatchCall() { + return getBatchCall().clone(); + } + + /** + * Build a new {@link MapboxTilequery} object with the initial value set for + * {@link #baseUrl()}. + * + * @return a {@link MapboxTilequery.Builder} object for creating this object + * @since 3.5.0 + */ + public static Builder builder() { + return new AutoValue_MapboxTilequery.Builder() + .baseUrl(Constants.BASE_API_URL); + } + + @NonNull + @Override + protected abstract String baseUrl(); + + @NonNull + abstract String accessToken(); + + @NonNull + abstract String mapIds(); + + @NonNull + abstract String query(); + + @Nullable + abstract Integer radius(); + + @Nullable + abstract Integer limit(); + + @Nullable + abstract Boolean dedupe(); + + @Nullable + abstract @TilequeryCriteria.TilequeryGeometry String geometry(); + + @Nullable + abstract String layers(); + + /** + * This builder is used to create a new request to the Mapbox Tilequery API. At a bare minimum, + * your request must include an access token, a map ID, and a query of some kind. All other + * fields can be left alone in order to use the default behaviour of the API. + *

+ * Note to contributors: All optional booleans in this builder use the object {@code Boolean} + * rather than the primitive to allow for unset (null) values. + *

+ * + * @since 3.5.0 + */ + @AutoValue.Builder + public abstract static class Builder { + + /** + * 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 + * @since 3.5.0 + */ + public abstract Builder baseUrl(@NonNull String baseUrl); + + /** + * Required to call when this is being built. If no access token provided, + * {@link ServicesException} will be thrown. + * + * @param accessToken Mapbox access token + * @return this builder for chaining options together + * @since 3.5.0 + */ + public abstract Builder accessToken(@NonNull String accessToken); + + /** + * The ID of the map being queried. If you need to composite multiple layers, the Tilequery + * API endpoint can also support a comma-separated list of map IDs. + * + * @param mapIds Map ID(s) + * @return this builder for chaining options together + * @since 3.5.0 + */ + public abstract Builder mapIds(String mapIds); + + /** + * The longitude and latitude to be queried. + * + * @param point query point + * @return this builder for chaining options together + * @since 3.5.0 + */ + public Builder query(@NonNull Point point) { + query(String.format(Locale.US, "%s,%s", + TextUtils.formatCoordinate(point.longitude()), + TextUtils.formatCoordinate(point.latitude()))); + + String str = String.format(Locale.US, "%s,%s", + TextUtils.formatCoordinate(point.longitude()), + TextUtils.formatCoordinate(point.latitude())); + return this; + } + + /** + * The longitude and latitude to be queried. + * + * @param query query point + * @return this builder for chaining options together + * @since 3.5.0 + */ + public abstract Builder query(@NonNull String query); + + /** + * The approximate distance in meters to query for features. Defaults to 0. Has no upper + * bound. Required for queries against point and line data. Due to the nature of tile + * buffering, a query with a large radius made against equally large point or line data may + * not include all possible features in the results. Queries will use tiles from the + * maximum zoom of the tileset, and will only include the intersecting tile plus 8 + * surrounding tiles when searching for nearby features. + * + * @param radius distance in meters to query for features + * @return this builder for chaining options together + * @since 3.5.0 + */ + public abstract Builder radius(@Nullable Integer radius); + + /** + * The number of features between 1 - 50 to return. Defaults to 5. + * + * @param limit the number of features + * @return this builder for chaining options together + * @since 3.5.0 + */ + public abstract Builder limit(@Nullable Integer limit); + + /** + * Determines whether results will be deduplicated or not. Defaults to true. + * + * @param dedupe whether results will be deduplicated + * @return this builder for chaining options together + * @since 3.5.0 + */ + public abstract Builder dedupe(@Nullable Boolean dedupe); + + /** + * Queries for a specific geometry type. Options are polygon, linestring, or point. + * + * @param geometry polygon, linestring, or point + * @return this builder for chaining options together + * @since 3.5.0 + */ + public abstract Builder geometry( + @Nullable @TilequeryCriteria.TilequeryGeometry String geometry); + + /** + * A comma-separated list of layers to query, rather than querying all layers. If a + * specified layer does not exist, it is skipped. If no layers exist, returns an + * empty FeatureCollection. + * + * @param layers list of layers to query + * @return this builder for chaining options together + * @since 3.5.0 + */ + public abstract Builder layers(@Nullable String layers); + + /** + * + * @return this builder for chaining options together + * @since 3.5.0 + */ + abstract MapboxTilequery autoBuild(); + + /** + * Build a new {@link MapboxTilequery} object. + * + * @return this builder for chaining options together + * @since 3.5.0 + */ + public MapboxTilequery build() { + MapboxTilequery tilequery = autoBuild(); + + if (!MapboxUtils.isAccessTokenValid(tilequery.accessToken())) { + throw new ServicesException("Using Mapbox Services requires setting a valid access token."); + } + + if (tilequery.query().isEmpty()) { + throw new ServicesException("A query with latitude and longitude values is required."); + } + + return tilequery; + } + } +} diff --git a/services-tilequery/src/main/java/com/mapbox/api/tilequery/TilequeryCriteria.java b/services-tilequery/src/main/java/com/mapbox/api/tilequery/TilequeryCriteria.java new file mode 100644 index 000000000..f49a354b4 --- /dev/null +++ b/services-tilequery/src/main/java/com/mapbox/api/tilequery/TilequeryCriteria.java @@ -0,0 +1,50 @@ +package com.mapbox.api.tilequery; + +import android.support.annotation.StringDef; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Constants that should be used when using the Tilequery API. + * + * @since 3.5.0 + */ +public class TilequeryCriteria { + + /** + * Queries for a specific geometry type (polygon). + * + * @since 3.5.0 + */ + public static final String TILEQUERY_GEOMETRY_POLYGON = "polygon"; + + /** + * Queries for a specific geometry type (linestring). + * + * @since 3.5.0 + */ + public static final String TILEQUERY_GEOMETRY_LINESTRING = "linestring"; + + /** + * Queries for a specific geometry type (point). + * + * @since 3.5.0 + */ + public static final String TILEQUERY_GEOMETRY_POINT = "point"; + + /** + * Queries for a specific geometry type selector. + * + * @since 3.5.0 + */ + @Retention(RetentionPolicy.SOURCE) + @StringDef( { + TILEQUERY_GEOMETRY_POLYGON, + TILEQUERY_GEOMETRY_LINESTRING, + TILEQUERY_GEOMETRY_POINT + }) + public @interface TilequeryGeometry { + } + +} diff --git a/services-tilequery/src/main/java/com/mapbox/api/tilequery/TilequeryService.java b/services-tilequery/src/main/java/com/mapbox/api/tilequery/TilequeryService.java new file mode 100644 index 000000000..f510d7c2b --- /dev/null +++ b/services-tilequery/src/main/java/com/mapbox/api/tilequery/TilequeryService.java @@ -0,0 +1,68 @@ +package com.mapbox.api.tilequery; + +import com.mapbox.geojson.FeatureCollection; + +import java.util.List; + +import retrofit2.Call; +import retrofit2.http.GET; +import retrofit2.http.Path; +import retrofit2.http.Query; + +/** + * Interface that defines the tilequery service. + * + * @since 3.5.0 + */ +public interface TilequeryService { + + /** + * Constructs the HTTP request for the specified parameters. + * + * @param mapIds Map ID(s) + * @param query query point + * @param accessToken Mapbox access token + * @param radius distance in meters to query for features + * @param limit the number of features + * @param dedupe whether results will be deduplicated + * @param geometry polygon, linestring, or point + * @param layers list of layers to query + * @return A retrofit Call object + * @since 3.5.0 + */ + @GET("/v4/{mapIds}/tilequery/{query}.json") + Call getCall( + @Path("mapIds") String mapIds, + @Path("query") String query, + @Query("access_token") String accessToken, + @Query("radius") Integer radius, + @Query("limit") Integer limit, + @Query("dedupe") Boolean dedupe, + @Query("geometry") String geometry, + @Query("layers") String layers); + + /** + * Constructs the HTTP request for the specified parameters. + * + * @param mapIds Map ID(s) + * @param query query point + * @param accessToken Mapbox access token + * @param radius distance in meters to query for features + * @param limit the number of features + * @param dedupe whether results will be deduplicated + * @param geometry polygon, linestring, or point + * @param layers list of layers to query + * @return A retrofit Call object + * @since 3.5.0 + */ + @GET("/v4/{mapIds}/tilequery/{query}.json") + Call> getBatchCall( + @Path("mapIds") String mapIds, + @Path("query") String query, + @Query("access_token") String accessToken, + @Query("radius") Integer radius, + @Query("limit") Integer limit, + @Query("dedupe") Boolean dedupe, + @Query("geometry") String geometry, + @Query("layers") String layers); +} diff --git a/services-tilequery/src/main/java/com/mapbox/api/tilequery/models/package-info.java b/services-tilequery/src/main/java/com/mapbox/api/tilequery/models/package-info.java new file mode 100644 index 000000000..9e39ded2c --- /dev/null +++ b/services-tilequery/src/main/java/com/mapbox/api/tilequery/models/package-info.java @@ -0,0 +1,4 @@ +/** + * Contains the Mapbox Java Services model classes related to the Mapbox Tilequery API. + */ +package com.mapbox.api.tilequery.models; diff --git a/services-tilequery/src/main/java/com/mapbox/api/tilequery/package-info.java b/services-tilequery/src/main/java/com/mapbox/api/tilequery/package-info.java new file mode 100644 index 000000000..fdbf0ed99 --- /dev/null +++ b/services-tilequery/src/main/java/com/mapbox/api/tilequery/package-info.java @@ -0,0 +1,4 @@ +/** + * Contains the Mapbox Java Services classes related to the Mapbox Tilequery API. + */ +package com.mapbox.api.tilequery; diff --git a/services-tilequery/src/test/java/com/mapbox/api/tilequery/MapboxTilequeryTest.java b/services-tilequery/src/test/java/com/mapbox/api/tilequery/MapboxTilequeryTest.java new file mode 100644 index 000000000..5d7f14009 --- /dev/null +++ b/services-tilequery/src/test/java/com/mapbox/api/tilequery/MapboxTilequeryTest.java @@ -0,0 +1,218 @@ +package com.mapbox.api.tilequery; + +import com.mapbox.core.exceptions.ServicesException; +import com.mapbox.geojson.FeatureCollection; +import com.mapbox.geojson.Point; + +import org.hamcrest.core.StringStartsWith; +import org.junit.Test; + +import java.io.IOException; +import java.util.Map; + +import retrofit2.Response; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +public class MapboxTilequeryTest extends TilequeryTestUtils { + + /** + * Test the most basic request (default response format) + */ + @Test + public void sanity() throws ServicesException, IOException { + MapboxTilequery client = MapboxTilequery.builder() + .accessToken(ACCESS_TOKEN) + .query("-122.42901,37.80633") + .mapIds("mapbox.mapbox-streets-v7") + .baseUrl(mockUrl.toString()) + .build(); + Response response = client.executeCall(); + assertEquals(200, response.code()); + assertNotNull(response.body()); + } + + @Test + public void query_acceptsPointsCorrectly() throws Exception { + MapboxTilequery client = MapboxTilequery.builder() + .accessToken(ACCESS_TOKEN) + .query(Point.fromLngLat(-122.42901,37.80633)) + .mapIds("mapbox.mapbox-streets-v7") + .baseUrl(mockUrl.toString()) + .build(); + + String str = client.cloneCall().request().url().toString(); + + assertTrue(client.cloneCall().request().url().toString() + .contains("-122.42901,37.80633")); + } + + @Test + public void build_noAccessTokenExceptionThrown() throws Exception { + thrown.expect(IllegalStateException.class); + thrown.expectMessage("Missing required properties: accessToken"); + MapboxTilequery.builder() + .query("-122.42901,37.80633") + .mapIds("mapbox.mapbox-streets-v7") + .baseUrl(mockUrl.toString()) + .build(); + } + + @Test + public void build_invalidAccessTokenExceptionThrown() throws ServicesException { + thrown.expect(ServicesException.class); + thrown.expectMessage(StringStartsWith.startsWith("Using Mapbox Services requires setting a valid access token")); + MapboxTilequery.builder() + .accessToken("") + .query("-122.42901,37.80633") + .mapIds("mapbox.mapbox-streets-v7") + .baseUrl(mockUrl.toString()) + .build(); + } + + @Test + public void build_noQueryExceptionThrown() throws Exception { + thrown.expect(IllegalStateException.class); + thrown.expectMessage("Missing required properties: query"); + MapboxTilequery.builder() + .accessToken(ACCESS_TOKEN) + .mapIds("mapbox.mapbox-streets-v7") + .baseUrl(mockUrl.toString()) + .build(); + } + + @Test + public void build_invalidQueryExceptionThrown() throws ServicesException { + thrown.expect(ServicesException.class); + thrown.expectMessage(StringStartsWith.startsWith("A query with latitude and longitude values is required")); + MapboxTilequery.builder() + .accessToken(ACCESS_TOKEN) + .query("") + .mapIds("mapbox.mapbox-streets-v7") + .baseUrl(mockUrl.toString()) + .build(); + } + + @Test + public void build_noMapIdExceptionThrown() throws Exception { + thrown.expect(IllegalStateException.class); + thrown.expectMessage("Missing required properties: mapIds"); + MapboxTilequery.builder() + .accessToken(ACCESS_TOKEN) + .query("-122.42901,37.80633") + .baseUrl(mockUrl.toString()) + .build(); + } + + @Test + public void build_optionalParameters() throws Exception { + MapboxTilequery client = MapboxTilequery.builder() + .accessToken(ACCESS_TOKEN) + .query("-122.42901,37.80633") + .mapIds("mapbox.mapbox-streets-v7") + .baseUrl(mockUrl.toString()) + .build(); + + assertNull(client.limit()); + assertNull(client.radius()); + assertNull(client.dedupe()); + assertNull(client.geometry()); + assertNull(client.layers()); + + Response response = client.executeCall(); + assertEquals(200, response.code()); + assertNotNull(response.body()); + } + + @Test + public void build_limitGetsAddedToListCorrectly() throws Exception { + MapboxTilequery client = MapboxTilequery.builder() + .accessToken(ACCESS_TOKEN) + .query("-122.42901,37.80633") + .mapIds("mapbox.mapbox-streets-v7") + .baseUrl(mockUrl.toString()) + .limit(50) + .build(); + assertTrue(client.cloneCall().request().url().toString() + .contains("limit=50")); + } + + @Test + public void build_radiusGetsAddedToListCorrectly() throws Exception { + MapboxTilequery client = MapboxTilequery.builder() + .accessToken(ACCESS_TOKEN) + .query("-122.42901,37.80633") + .mapIds("mapbox.mapbox-streets-v7") + .baseUrl(mockUrl.toString()) + .radius(200) + .build(); + assertTrue(client.cloneCall().request().url().toString() + .contains("radius=200")); + } + + @Test + public void build_geometryGetsAddedToListCorrectly() throws Exception { + MapboxTilequery client = MapboxTilequery.builder() + .accessToken(ACCESS_TOKEN) + .query("-122.42901,37.80633") + .mapIds("mapbox.mapbox-streets-v7") + .baseUrl(mockUrl.toString()) + .geometry(TilequeryCriteria.TILEQUERY_GEOMETRY_LINESTRING) + .build(); + + assertTrue(client.cloneCall().request().url().toString() + .contains("geometry=linestring")); + } + + @Test + public void build_dedupeGetsAddedToListCorrectly() throws Exception { + MapboxTilequery client = MapboxTilequery.builder() + .accessToken(ACCESS_TOKEN) + .query("-122.42901,37.80633") + .mapIds("mapbox.mapbox-streets-v7") + .baseUrl(mockUrl.toString()) + .dedupe(true) + .build(); + + assertTrue(client.cloneCall().request().url().toString() + .contains("dedupe=true")); + } + + @Test + public void build_layersGetAddedToListCorrectly() throws Exception { + MapboxTilequery client = MapboxTilequery.builder() + .accessToken(ACCESS_TOKEN) + .query("-122.42901,37.80633") + .mapIds("mapbox.mapbox-streets-v7") + .baseUrl(mockUrl.toString()) + .layers("poi_label") + .build(); + assertTrue(client.cloneCall().request().url().toString() + .contains("layers=poi_label")); + } + + @Test + public void executeCall_optionalParamLimitHonored() throws Exception { + MapboxTilequery clientAppParams = MapboxTilequery.builder() + .accessToken(ACCESS_TOKEN) + .query("-122.42901,37.80633") + .mapIds("mapbox.mapbox-streets-v7") + .baseUrl(mockUrl.toString()) + .layers("poi_label") + .geometry("point") + .radius(500) + .limit(2) + .layers("poi_label") + .build(); + + Response response = clientAppParams.executeCall(); + assertEquals(200, response.code()); + assertNotNull(response.body()); + + FeatureCollection featureCollection = (FeatureCollection)response.body(); + assertEquals(2, featureCollection.features().size()); + } +} diff --git a/services-tilequery/src/test/java/com/mapbox/api/tilequery/TilequeryTestUtils.java b/services-tilequery/src/test/java/com/mapbox/api/tilequery/TilequeryTestUtils.java new file mode 100644 index 000000000..2e32f7806 --- /dev/null +++ b/services-tilequery/src/test/java/com/mapbox/api/tilequery/TilequeryTestUtils.java @@ -0,0 +1,57 @@ +package com.mapbox.api.tilequery; + +import com.mapbox.core.TestUtils; +import com.mapbox.core.exceptions.ServicesException; +import com.mapbox.geojson.Point; + +import okhttp3.HttpUrl; +import okhttp3.mockwebserver.Dispatcher; +import okhttp3.mockwebserver.MockResponse; +import okhttp3.mockwebserver.MockWebServer; +import okhttp3.mockwebserver.RecordedRequest; + +import org.hamcrest.core.StringStartsWith; +import org.hamcrest.junit.ExpectedException; +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; + +import java.io.IOException; + +public class TilequeryTestUtils extends TestUtils { + protected static final String TILEQUERY_VALID = "tilequery.json"; + protected static final String TILEQUERY_ALL_PARAM_VALID = "tilequery-all-params.json"; + + private MockWebServer server; + protected HttpUrl mockUrl; + + @Rule + public ExpectedException thrown = ExpectedException.none(); + + @Before + public void setUp() throws Exception { + server = new MockWebServer(); + server.setDispatcher(new Dispatcher() { + @Override + public MockResponse dispatch(RecordedRequest request) throws InterruptedException { + try { + String response = loadJsonFixture(TILEQUERY_VALID); + if (request.getPath().contains("limit")) { + response = loadJsonFixture(TILEQUERY_ALL_PARAM_VALID); + } + return new MockResponse().setBody(response); + } catch (IOException ioException) { + throw new RuntimeException(ioException); + } + } + }); + server.start(); + mockUrl = server.url(""); + } + + @After + public void tearDown() throws IOException { + server.shutdown(); + } +} diff --git a/services-tilequery/src/test/resources/tilequery-all-params.json b/services-tilequery/src/test/resources/tilequery-all-params.json new file mode 100644 index 000000000..3f550afab --- /dev/null +++ b/services-tilequery/src/test/resources/tilequery-all-params.json @@ -0,0 +1 @@ +{"type":"FeatureCollection","features":[{"type":"Feature","geometry":{"type":"Point","coordinates":[-122.42901,37.806332]},"properties":{"class":"wood","type":"wood","tilequery":{"distance":0,"geometry":"polygon","layer":"landuse"}}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-122.42901,37.806332]},"properties":{"class":"national_park","type":"national_park","tilequery":{"distance":0,"geometry":"polygon","layer":"landuse_overlay"}}}]} \ No newline at end of file diff --git a/services-tilequery/src/test/resources/tilequery.json b/services-tilequery/src/test/resources/tilequery.json new file mode 100644 index 000000000..3f550afab --- /dev/null +++ b/services-tilequery/src/test/resources/tilequery.json @@ -0,0 +1 @@ +{"type":"FeatureCollection","features":[{"type":"Feature","geometry":{"type":"Point","coordinates":[-122.42901,37.806332]},"properties":{"class":"wood","type":"wood","tilequery":{"distance":0,"geometry":"polygon","layer":"landuse"}}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-122.42901,37.806332]},"properties":{"class":"national_park","type":"national_park","tilequery":{"distance":0,"geometry":"polygon","layer":"landuse_overlay"}}}]} \ No newline at end of file diff --git a/services/build.gradle b/services/build.gradle index 0689dd263..396f3e449 100644 --- a/services/build.gradle +++ b/services/build.gradle @@ -1,22 +1,22 @@ apply plugin: 'java-library' sourceSets { - main.java.srcDirs = ['../services-directions/src/main/java', '../services-geocoding/src/main/java', - '../services-matching/src/main/java', '../services-matrix/src/main/java', - '../services-optimization/src/main/java', '../services-staticmap/src/main/java', - '../services-speech/src/main/java'] + main.java.srcDirs = ['../services-directions/src/main/java', '../services-geocoding/src/main/java', + '../services-matching/src/main/java', '../services-matrix/src/main/java', + '../services-optimization/src/main/java', '../services-staticmap/src/main/java', + '../services-speech/src/main/java', '../services-tilequery/src/main/java'] } dependencies { - api project(":services-core") - api project(":services-geojson") + api project(":services-core") + api project(":services-geojson") - // Annotations - compileOnly dependenciesList.supportAnnotation + // Annotations + compileOnly dependenciesList.supportAnnotation - // AutoValue - compileOnly dependenciesList.autoValue - compileOnly dependenciesList.autoValueGson + // AutoValue + compileOnly dependenciesList.autoValue + compileOnly dependenciesList.autoValueGson } apply from: "${rootDir}/gradle/mvn-push.gradle" diff --git a/settings.gradle b/settings.gradle index c89359ad9..0e293f3c2 100644 --- a/settings.gradle +++ b/settings.gradle @@ -11,5 +11,5 @@ include ':services-optimization' include ':services-matrix' include ':services-matching' include ':services-staticmap' +include ':services-tilequery' include 'samples' -