From a0a8697e51cec1b965fa2fb9bea3dec14672d525 Mon Sep 17 00:00:00 2001 From: Tom Clifford Date: Wed, 7 Feb 2024 17:35:02 -0800 Subject: [PATCH] feat! Added Address Descriptors to Geocoding response. Refactored Geocoding response to allow fields outside the GeocodingResult to be exposed through the client. --- README.md | 8 +- .../java/com/google/maps/GeocodingApi.java | 14 +- .../com/google/maps/GeocodingApiRequest.java | 15 +- .../google/maps/model/AddressDescriptor.java | 207 +++++ .../google/maps/model/GeocodingResponse.java | 47 ++ .../com/google/maps/model/LocalizedText.java | 38 + .../com/google/maps/GeoApiContextTest.java | 6 +- .../com/google/maps/GeocodingApiTest.java | 129 ++- .../google/maps/metrics/OpenCensusTest.java | 2 +- ...GeocodeWithAddressDescriptorsResponse.json | 742 ++++++++++++++++++ 10 files changed, 1172 insertions(+), 36 deletions(-) create mode 100644 src/main/java/com/google/maps/model/AddressDescriptor.java create mode 100644 src/main/java/com/google/maps/model/GeocodingResponse.java create mode 100644 src/main/java/com/google/maps/model/LocalizedText.java create mode 100644 src/test/resources/com/google/maps/ReverseGeocodeWithAddressDescriptorsResponse.json diff --git a/README.md b/README.md index e899acfdc..f146c6417 100644 --- a/README.md +++ b/README.md @@ -120,10 +120,10 @@ This example uses the [Geocoding API] with an API key: GeoApiContext context = new GeoApiContext.Builder() .apiKey("AIza...") .build(); -GeocodingResult[] results = GeocodingApi.geocode(context, +GeocodingResponse response = GeocodingApi.geocode(context, "1600 Amphitheatre Parkway Mountain View, CA 94043").await(); Gson gson = new GsonBuilder().setPrettyPrinting().create(); -System.out.println(gson.toJson(results[0].addressComponents)); +System.out.println(gson.toJson(response.results[0].addressComponents)); // Invoke .shutdown() after your application is done making requests context.shutdown(); @@ -205,9 +205,9 @@ try { req.awaitIgnoreError(); // No checked exception. // Async -req.setCallback(new PendingResult.Callback() { +req.setCallback(new PendingResult.Callback() { @Override - public void onResult(GeocodingResult[] result) { + public void onResult(GeocodingResponse result) { // Handle successful request. } diff --git a/src/main/java/com/google/maps/GeocodingApi.java b/src/main/java/com/google/maps/GeocodingApi.java index b760ad15c..39ed93c96 100644 --- a/src/main/java/com/google/maps/GeocodingApi.java +++ b/src/main/java/com/google/maps/GeocodingApi.java @@ -17,6 +17,8 @@ import com.google.maps.errors.ApiException; import com.google.maps.internal.ApiResponse; +import com.google.maps.model.AddressDescriptor; +import com.google.maps.model.GeocodingResponse; import com.google.maps.model.GeocodingResult; import com.google.maps.model.LatLng; @@ -68,10 +70,11 @@ public static GeocodingApiRequest reverseGeocode(GeoApiContext context, LatLng l return request; } - public static class Response implements ApiResponse { + public static class Response implements ApiResponse { public String status; public String errorMessage; - public GeocodingResult[] results; + public GeocodingResult results[]; + public AddressDescriptor addressDescriptor; @Override public boolean successful() { @@ -79,8 +82,11 @@ public boolean successful() { } @Override - public GeocodingResult[] getResult() { - return results; + public GeocodingResponse getResult() { + GeocodingResponse response = new GeocodingResponse(); + response.results = results; + response.addressDescriptor = addressDescriptor; + return response; } @Override diff --git a/src/main/java/com/google/maps/GeocodingApiRequest.java b/src/main/java/com/google/maps/GeocodingApiRequest.java index 4a4d251ef..3f83eb740 100644 --- a/src/main/java/com/google/maps/GeocodingApiRequest.java +++ b/src/main/java/com/google/maps/GeocodingApiRequest.java @@ -20,13 +20,13 @@ import com.google.maps.internal.ApiConfig; import com.google.maps.model.AddressType; import com.google.maps.model.ComponentFilter; -import com.google.maps.model.GeocodingResult; +import com.google.maps.model.GeocodingResponse; import com.google.maps.model.LatLng; import com.google.maps.model.LocationType; /** A request for the Geocoding API. */ public class GeocodingApiRequest - extends PendingResultBase { + extends PendingResultBase { private static final ApiConfig API_CONFIG = new ApiConfig("/maps/api/geocode/json"); @@ -151,4 +151,15 @@ public GeocodingApiRequest resultType(AddressType... resultTypes) { public GeocodingApiRequest locationType(LocationType... locationTypes) { return param("location_type", join('|', locationTypes)); } + + /** + * Determines whether the address descriptor is returned in the response. + * + * @param enableAddressDescriptor Flag to determine whether to return the address descriptor in + * the response. + * @return Returns this {@code GeocodingApiRequest} for call chaining. + */ + public GeocodingApiRequest enableAddressDescriptor(boolean enableAddressDescriptor) { + return param("enable_address_descriptor", String.valueOf(enableAddressDescriptor)); + } } diff --git a/src/main/java/com/google/maps/model/AddressDescriptor.java b/src/main/java/com/google/maps/model/AddressDescriptor.java new file mode 100644 index 000000000..4a13d7f64 --- /dev/null +++ b/src/main/java/com/google/maps/model/AddressDescriptor.java @@ -0,0 +1,207 @@ +/* + * Copyright 2024 Google Inc. All rights reserved. + * + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF + * ANY KIND, either express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +package com.google.maps.model; + +import java.io.Serializable; +import java.util.Arrays; + +/** + * Represents a descriptor of an address. + * + *

Please see Address + * Descriptors for more detail. + */ +public class AddressDescriptor implements Serializable { + + private static final long serialVersionUID = 1L; + + public static class Landmark implements Serializable { + + private static final long serialVersionUID = 1L; + + // The Place ID of the underlying establishment serving as the landmark. + // Can be used to resolve more information about the landmark through Place + // Details or Place Id Lookup. + public String placeId; + + // The best name for the landmark. + public LocalizedText displayName; + + // One or more values indicating the type of the returned result. Please see Types + // for more detail. + public String types[]; + + /** An enum representing the relationship in space between the landmark and the target. */ + public enum SpatialRelationship { + // This is the default relationship when nothing more specific below + // applies. + NEAR("NEAR"), + // The landmark has a spatial geometry and the target is within its + // bounds. + WITHIN("WITHIN"), + // The target is directly adjacent to the landmark or landmark's access + // point. + BESIDE("BESIDE"), + // The target is directly opposite the landmark on the other side of the + // road. + ACROSS_THE_ROAD("ACROSS_THE_ROAD"), + // On the same route as the landmark but not besides or across. + DOWN_THE_ROAD("DOWN_THE_ROAD"), + // Not on the same route as the landmark but a single 'turn' away. + AROUND_THE_CORNER("AROUND_THE_CORNER"), + // Close to the landmark's structure but further away from its access + // point. + BEHIND("BEHIND"); + + private final String spatialRelationship; + + SpatialRelationship(final String spatialRelationship) { + this.spatialRelationship = spatialRelationship; + } + + @Override + public String toString() { + return spatialRelationship; + } + + public String toCanonicalLiteral() { + return toString(); + } + } + + // Defines the spatial relationship between the target location and the + // landmark. + public SpatialRelationship spatialRelationship; + + // The straight line distance between the target location and one of the + // landmark's access points. + public float straightLineDistanceMeters; + + // The travel distance along the road network between the target + // location's closest point on a road, and the landmark's closest access + // point on a road. This can be unpopulated if the landmark is disconnected + // from the part of the road network the target is closest to OR if the + // target location was not actually considered to be on the road network. + public float travelDistanceMeters; + + @Override + public String toString() { + StringBuilder sb = new StringBuilder("[Landmark: "); + if (placeId != null) { + sb.append("placeId=").append(placeId); + } + if (displayName != null) { + sb.append(", displayName=").append(displayName); + } + if (types != null && types.length > 0) { + sb.append(", types=").append(Arrays.toString(types)); + } + if (spatialRelationship != null) { + sb.append(", spatialRelationship=").append(spatialRelationship); + } + sb.append(", straightLineDistanceMeters=").append(straightLineDistanceMeters); + sb.append(", travelDistanceMeters=").append(travelDistanceMeters); + sb.append("]"); + return sb.toString(); + } + } + + // A ranked list of nearby landmarks. The most useful (recognizable and + // nearby) landmarks are ranked first. + public Landmark landmarks[]; + + // Precise regions that are useful at describing a location. + public static class Area implements Serializable { + + private static final long serialVersionUID = 1L; + + // The Place ID of the underlying area feature. Can be used to + // resolve more information about the area through Place Details or + // Place Id Lookup. + public String placeId; + + // The best name for the area. + public LocalizedText displayName; + + /** An enum representing the relationship in space between the area and the target. */ + public enum Containment { + /** Indicates an unknown containment returned by the server. */ + CONTAINMENT_UNSPECIFIED("CONTAINMENT_UNSPECIFIED"), + + /** The target location is within the area region, close to the center. */ + WITHIN("WITHIN"), + + /** The target location is within the area region, close to the edge. */ + OUTSKIRTS("OUTSKIRTS"), + + /** The target location is outside the area region, but close by. */ + NEAR("NEAR"); + + private final String containment; + + Containment(final String containment) { + this.containment = containment; + } + + @Override + public String toString() { + return containment; + } + + public String toCanonicalLiteral() { + return toString(); + } + } + + // Defines the spatial relationship between the target location and the + // political region. + public Containment containment; + + @Override + public String toString() { + StringBuilder sb = new StringBuilder("[Area: "); + if (placeId != null) { + sb.append("placeId=").append(placeId); + } + if (displayName != null) { + sb.append(", displayName=").append(displayName); + } + if (containment != null) { + sb.append(", containment=").append(containment); + } + sb.append("]"); + return sb.toString(); + } + } + + // A ranked list of containing or adjacent areas. The most useful + // (recognizable and precise) areas are ranked first. + public Area areas[]; + + @Override + public String toString() { + StringBuilder sb = new StringBuilder("[AddressDescriptor: "); + if (areas != null && areas.length > 0) { + sb.append("areas=").append(Arrays.toString(areas)); + } + if (landmarks != null && landmarks.length > 0) { + sb.append(", landmarks=").append(Arrays.toString(landmarks)); + } + sb.append("]"); + return sb.toString(); + } +} diff --git a/src/main/java/com/google/maps/model/GeocodingResponse.java b/src/main/java/com/google/maps/model/GeocodingResponse.java new file mode 100644 index 000000000..ee31c3443 --- /dev/null +++ b/src/main/java/com/google/maps/model/GeocodingResponse.java @@ -0,0 +1,47 @@ +/* + * Copyright 2024 Google Inc. All rights reserved. + * + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF + * ANY KIND, either express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +package com.google.maps.model; + +import java.io.Serializable; + +/** + * The response from a Geocoding request. + * + *

Please see Geocoding + * Responses for more detail. + */ +public class GeocodingResponse implements Serializable { + + private static final long serialVersionUID = 1L; + + /** The list of Geocoding Results. */ + public GeocodingResult results[]; + + /** The Address Descriptor for the target. */ + public AddressDescriptor addressDescriptor; + + @Override + public String toString() { + StringBuilder sb = new StringBuilder("[GeocodingResponse: "); + sb.append(results.length).append(" results"); + if (addressDescriptor != null) { + sb.append(", addressDescriptor=").append(addressDescriptor); + } + sb.append("]"); + return sb.toString(); + } +} diff --git a/src/main/java/com/google/maps/model/LocalizedText.java b/src/main/java/com/google/maps/model/LocalizedText.java new file mode 100644 index 000000000..8f38bd55c --- /dev/null +++ b/src/main/java/com/google/maps/model/LocalizedText.java @@ -0,0 +1,38 @@ +/* + * Copyright 2024 Google Inc. All rights reserved. + * + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF + * ANY KIND, either express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +package com.google.maps.model; + +import java.io.Serializable; + +/** Localized variant of a text in a particular language. */ +public class LocalizedText implements Serializable { + + private static final long serialVersionUID = 1L; + + // Localized string in the language corresponding to language_code below. + public String text; + + // The text's BCP-47 language code, such as "en-US" or "sr-Latn". + // + // For more information, see + // http://www.unicode.org/reports/tr35/#Unicode_locale_identifier. + public String languageCode; + + @Override + public String toString() { + return String.format("(text=%d, languageCode=%d)", text, languageCode); + } +} diff --git a/src/test/java/com/google/maps/GeoApiContextTest.java b/src/test/java/com/google/maps/GeoApiContextTest.java index f6c431d58..576090140 100644 --- a/src/test/java/com/google/maps/GeoApiContextTest.java +++ b/src/test/java/com/google/maps/GeoApiContextTest.java @@ -193,7 +193,11 @@ public void testErrorResponseRetries() throws Exception { // Execute GeocodingResult[] result = - builder.build().get(new ApiConfig("/"), GeocodingApi.Response.class, "k", "v").await(); + builder + .build() + .get(new ApiConfig("/"), GeocodingApi.Response.class, "k", "v") + .await() + .results; assertEquals(1, result.length); assertEquals( "1600 Amphitheatre Parkway, Mountain View, CA 94043, USA", result[0].formattedAddress); diff --git a/src/test/java/com/google/maps/GeocodingApiTest.java b/src/test/java/com/google/maps/GeocodingApiTest.java index e99e2bab9..33d4162a4 100644 --- a/src/test/java/com/google/maps/GeocodingApiTest.java +++ b/src/test/java/com/google/maps/GeocodingApiTest.java @@ -25,8 +25,10 @@ import com.google.maps.internal.HttpHeaders; import com.google.maps.model.AddressComponentType; +import com.google.maps.model.AddressDescriptor; import com.google.maps.model.AddressType; import com.google.maps.model.ComponentFilter; +import com.google.maps.model.GeocodingResponse; import com.google.maps.model.GeocodingResult; import com.google.maps.model.LatLng; import com.google.maps.model.LocationType; @@ -48,6 +50,7 @@ public class GeocodingApiTest { private static String simpleReverseGeocodeResponse; private static String utfResultGeocodeResponse; private static String reverseGeocodeWithKitaWardResponse; + private static String reverseGeocodeWithAddressDescriptorsResponse; private static String geocodeLibraryType; public GeocodingApiTest() { @@ -57,13 +60,16 @@ public GeocodingApiTest() { simpleReverseGeocodeResponse = retrieveBody("SimpleReverseGeocodeResponse.json"); utfResultGeocodeResponse = retrieveBody("UtfResultGeocodeResponse.json"); reverseGeocodeWithKitaWardResponse = retrieveBody("ReverseGeocodeWithKitaWardResponse.json"); + reverseGeocodeWithAddressDescriptorsResponse = + retrieveBody("ReverseGeocodeWithAddressDescriptorsResponse.json"); geocodeLibraryType = retrieveBody("GeocodeLibraryType.json"); } @Test public void testGeocodeLibraryType() throws Exception { try (LocalTestServerContext sc = new LocalTestServerContext(geocodeLibraryType)) { - GeocodingResult[] results = GeocodingApi.newRequest(sc.context).address("80 FR").await(); + GeocodingResult[] results = + GeocodingApi.newRequest(sc.context).address("80 FR").await().results; assertEquals(1, results.length); assertEquals(3, results[0].types.length); @@ -77,7 +83,8 @@ public void testGeocodeLibraryType() throws Exception { @Test public void testSimpleGeocode() throws Exception { try (LocalTestServerContext sc = new LocalTestServerContext(simpleGeocodeResponse)) { - GeocodingResult[] results = GeocodingApi.newRequest(sc.context).address("Sydney").await(); + GeocodingResult[] results = + GeocodingApi.newRequest(sc.context).address("Sydney").await().results; checkSydneyResult(results); assertNotNull(Arrays.toString(results)); @@ -89,7 +96,8 @@ public void testSimpleGeocode() throws Exception { public void testPlaceGeocode() throws Exception { try (LocalTestServerContext sc = new LocalTestServerContext(placeGeocodeResponse)) { String placeID = "ChIJP3Sa8ziYEmsRUKgyFmh9AQM"; - GeocodingResult[] results = GeocodingApi.newRequest(sc.context).place(placeID).await(); + GeocodingResult[] results = + GeocodingApi.newRequest(sc.context).place(placeID).await().results; checkSydneyResult(results); sc.assertParamValue(placeID, "place_id"); @@ -121,7 +129,8 @@ private void testExperienceIdSample() { public void testNoExperienceId() throws Exception { try (LocalTestServerContext sc = new LocalTestServerContext(placeGeocodeResponse)) { String placeID = "ChIJP3Sa8ziYEmsRUKgyFmh9AQM"; - GeocodingResult[] results = GeocodingApi.newRequest(sc.context).place(placeID).await(); + GeocodingResult[] results = + GeocodingApi.newRequest(sc.context).place(placeID).await().results; final Headers headers = sc.headers(); final List experienceIds = headers.values(HttpHeaders.X_GOOG_MAPS_EXPERIENCE_ID); assertEquals(0, experienceIds.size()); @@ -148,7 +157,11 @@ public void testExperienceIds() throws Exception { String expId = "experienceId"; String expId2 = "experienceId2"; GeocodingResult[] results = - GeocodingApi.newRequest(sc.context).experienceIds(expId, expId2).place(placeID).await(); + GeocodingApi.newRequest(sc.context) + .experienceIds(expId, expId2) + .place(placeID) + .await() + .results; final Headers headers = sc.headers(); final List experienceIds = headers.values(HttpHeaders.X_GOOG_MAPS_EXPERIENCE_ID); assertEquals(1, experienceIds.size()); @@ -159,12 +172,12 @@ public void testExperienceIds() throws Exception { @Test public void testAsync() throws Exception { try (LocalTestServerContext sc = new LocalTestServerContext(simpleGeocodeResponse)) { - final List resps = new ArrayList<>(); + final List resps = new ArrayList<>(); - PendingResult.Callback callback = - new PendingResult.Callback() { + PendingResult.Callback callback = + new PendingResult.Callback() { @Override - public void onResult(GeocodingResult[] result) { + public void onResult(GeocodingResponse result) { resps.add(result); } @@ -179,7 +192,7 @@ public void onFailure(Throwable e) { assertFalse(resps.isEmpty()); assertNotNull(resps.get(0)); - checkSydneyResult(resps.get(0)); + checkSydneyResult(resps.get(0).results); sc.assertParamValue("Sydney", "address"); } @@ -198,7 +211,8 @@ private void checkSydneyResult(GeocodingResult[] results) { public void testReverseGeocode() throws Exception { try (LocalTestServerContext sc = new LocalTestServerContext(reverseGeocodeResponse)) { LatLng latlng = new LatLng(-33.8674869, 151.2069902); - GeocodingResult[] results = GeocodingApi.newRequest(sc.context).latlng(latlng).await(); + GeocodingResult[] results = + GeocodingApi.newRequest(sc.context).latlng(latlng).await().results; assertEquals(10, results.length); assertEquals("343 George St, Sydney NSW 2000, Australia", results[0].formattedAddress); @@ -300,7 +314,8 @@ public void testGeocodeTheGoogleplex() throws Exception { + " \"status\" : \"OK\"\n" + "}\n")) { String address = "1600 Amphitheatre Parkway, Mountain View, CA"; - GeocodingResult[] results = GeocodingApi.newRequest(sc.context).address(address).await(); + GeocodingResult[] results = + GeocodingApi.newRequest(sc.context).address(address).await().results; assertNotNull(results); assertNotNull(Arrays.toString(results)); @@ -389,7 +404,8 @@ public void testGeocodeWithBounds() throws Exception { GeocodingApi.newRequest(sc.context) .address("Winnetka") .bounds(new LatLng(34.172684, -118.604794), new LatLng(34.236144, -118.500938)) - .await(); + .await() + .results; assertNotNull(Arrays.toString(results)); @@ -471,7 +487,7 @@ public void testGeocodeWithRegionBiasing() throws Exception { + " \"status\" : \"OK\"\n" + "}\n")) { GeocodingResult[] results = - GeocodingApi.newRequest(sc.context).address("Toledo").region("es").await(); + GeocodingApi.newRequest(sc.context).address("Toledo").region("es").await().results; assertNotNull(Arrays.toString(results)); @@ -558,7 +574,8 @@ public void testGeocodeWithComponentFilter() throws Exception { GeocodingApi.newRequest(sc.context) .address("santa cruz") .components(ComponentFilter.country("ES")) - .await(); + .await() + .results; assertNotNull(Arrays.toString(results)); @@ -644,7 +661,8 @@ public void testGeocodeWithMultipleComponentFilters() throws Exception { GeocodingApi.newRequest(sc.context) .address("Torun") .components(administrativeArea("TX"), country("US")) - .await(); + .await() + .results; assertNotNull(Arrays.toString(results)); @@ -733,7 +751,8 @@ public void testGeocodeWithJustComponents() throws Exception { ComponentFilter.route("Annegatan"), ComponentFilter.administrativeArea("Helsinki"), ComponentFilter.country("Finland")) - .await(); + .await() + .results; assertNotNull(results); assertNotNull(Arrays.toString(results)); @@ -754,7 +773,8 @@ public void testGeocodeWithJustComponents() throws Exception { public void testSimpleReverseGeocode() throws Exception { try (LocalTestServerContext sc = new LocalTestServerContext(simpleReverseGeocodeResponse)) { LatLng latlng = new LatLng(40.714224, -73.961452); - GeocodingResult[] results = GeocodingApi.newRequest(sc.context).latlng(latlng).await(); + GeocodingResult[] results = + GeocodingApi.newRequest(sc.context).latlng(latlng).await().results; assertNotNull(results); assertNotNull(Arrays.toString(results)); @@ -854,7 +874,8 @@ public void testReverseGeocodeRestrictedByType() throws Exception { .latlng(latlng) .locationType(LocationType.ROOFTOP) .resultType(AddressType.STREET_ADDRESS) - .await(); + .await() + .results; assertNotNull(results); assertNotNull(Arrays.toString(results)); @@ -873,7 +894,8 @@ public void testReverseGeocodeRestrictedByType() throws Exception { public void testUtfResult() throws Exception { try (LocalTestServerContext sc = new LocalTestServerContext(utfResultGeocodeResponse)) { LatLng location = new LatLng(46.8023388, 1.6551867); - GeocodingResult[] results = GeocodingApi.newRequest(sc.context).latlng(location).await(); + GeocodingResult[] results = + GeocodingApi.newRequest(sc.context).latlng(location).await().results; assertEquals("1 Rue Fernand Raynaud, 36000 Châteauroux, France", results[0].formattedAddress); sc.assertParamValue(location.toUrlValue(), "latlng"); } @@ -975,7 +997,8 @@ public void testCustomParameterPassThrough() throws Exception { GeocodingApi.newRequest(sc.context) .address(address) .custom("new_forward_geocoder", "true") - .await(); + .await() + .results; assertNotNull(Arrays.toString(results)); assertEquals( @@ -993,7 +1016,8 @@ public void testReverseGeocodeWithKitaWard() throws Exception { try (LocalTestServerContext sc = new LocalTestServerContext(reverseGeocodeWithKitaWardResponse)) { LatLng location = new LatLng(35.03937, 135.729243); - GeocodingResult[] results = GeocodingApi.newRequest(sc.context).latlng(location).await(); + GeocodingResult[] results = + GeocodingApi.newRequest(sc.context).latlng(location).await().results; assertNotNull(results); assertNotNull(Arrays.toString(results)); @@ -1087,7 +1111,8 @@ public void testSupportedAddressTypesFood() throws Exception { + " \"status\" : \"OK\"\n" + "}\n")) { String address = "Noah's Marketplace, 21800 W Eleven Mile Rd"; - GeocodingResult[] results = GeocodingApi.newRequest(sc.context).address(address).await(); + GeocodingResult[] results = + GeocodingApi.newRequest(sc.context).address(address).await().results; assertNotNull(results); assertNotNull(Arrays.toString(results)); @@ -1172,7 +1197,8 @@ public void testSupportedAddressTypesSynagogue() throws Exception { + " \"status\" : \"OK\"\n" + "}\n")) { String address = "Ahavas Olam, 15620 W. Ten Mile Road"; - GeocodingResult[] results = GeocodingApi.newRequest(sc.context).address(address).await(); + GeocodingResult[] results = + GeocodingApi.newRequest(sc.context).address(address).await().results; assertNotNull(results); assertNotNull(Arrays.toString(results)); @@ -1184,4 +1210,59 @@ public void testSupportedAddressTypesSynagogue() throws Exception { sc.assertParamValue(address, "address"); } } + + /** Testing Address Descriptors in reverse geocode. */ + @Test + public void testReverseGeocodeWithAddressDescriptors() throws Exception { + try (LocalTestServerContext sc = + new LocalTestServerContext(reverseGeocodeWithAddressDescriptorsResponse)) { + LatLng location = new LatLng(35.03937, 135.729243); + GeocodingResponse response = + GeocodingApi.newRequest(sc.context) + .latlng(location) + .enableAddressDescriptor(true) + .await(); + GeocodingResult[] results = response.results; + + assertNotNull(results); + assertNotNull(Arrays.toString(results)); + assertEquals( + "Japan, 〒603-8361 Kyōto-fu, Kyōto-shi, Kita-ku, Kinkakujichō, 1 北山鹿苑寺金閣寺", + results[0].formattedAddress); + assertEquals("Kita Ward", results[3].addressComponents[0].shortName); + assertEquals("Kita Ward", results[3].addressComponents[0].longName); + assertEquals(AddressComponentType.LOCALITY, results[3].addressComponents[0].types[0]); + assertEquals(AddressComponentType.POLITICAL, results[3].addressComponents[0].types[1]); + assertEquals(AddressComponentType.WARD, results[3].addressComponents[0].types[2]); + + sc.assertParamValue(location.toUrlValue(), "latlng"); + + assertNotNull(response.addressDescriptor); + assertEquals("ChIJvUbrwCCoAWARX2QiHCsn5A4", response.addressDescriptor.landmarks[0].placeId); + assertEquals("Kinkaku-ji", response.addressDescriptor.landmarks[0].displayName.text); + assertEquals("en", response.addressDescriptor.landmarks[0].displayName.languageCode); + assertEquals( + 0.009104185, response.addressDescriptor.landmarks[0].straightLineDistanceMeters, 0.0001); + assertEquals(0, response.addressDescriptor.landmarks[0].travelDistanceMeters, 0.0001); + assertEquals("establishment", response.addressDescriptor.landmarks[0].types[0]); + assertEquals( + AddressDescriptor.Area.Containment.WITHIN, + response.addressDescriptor.areas[0].containment); + assertEquals("ChIJe9XMwiCoAWARVrQpOsYqdBE", response.addressDescriptor.areas[0].placeId); + assertEquals("Kinkakujicho", response.addressDescriptor.areas[0].displayName.text); + assertEquals("en", response.addressDescriptor.areas[0].displayName.languageCode); + assertEquals( + AddressDescriptor.Area.Containment.WITHIN, + response.addressDescriptor.areas[0].containment); + assertEquals( + AddressDescriptor.Area.Containment.OUTSKIRTS, + response.addressDescriptor.areas[1].containment); + assertEquals( + AddressDescriptor.Landmark.SpatialRelationship.NEAR, + response.addressDescriptor.landmarks[0].spatialRelationship); + assertEquals( + AddressDescriptor.Landmark.SpatialRelationship.WITHIN, + response.addressDescriptor.landmarks[1].spatialRelationship); + } + } } diff --git a/src/test/java/com/google/maps/metrics/OpenCensusTest.java b/src/test/java/com/google/maps/metrics/OpenCensusTest.java index 1efc90926..d22b0a04f 100644 --- a/src/test/java/com/google/maps/metrics/OpenCensusTest.java +++ b/src/test/java/com/google/maps/metrics/OpenCensusTest.java @@ -88,7 +88,7 @@ public void testSuccess() throws Exception { server.enqueue(mockResponse(200, "OK", 300)); // succeed GeocodingResult[] result = - context.get(new ApiConfig("/path"), GeocodingApi.Response.class, "k", "v").await(); + context.get(new ApiConfig("/path"), GeocodingApi.Response.class, "k", "v").await().results; assertEquals(1, result.length); List tags = diff --git a/src/test/resources/com/google/maps/ReverseGeocodeWithAddressDescriptorsResponse.json b/src/test/resources/com/google/maps/ReverseGeocodeWithAddressDescriptorsResponse.json new file mode 100644 index 000000000..137b3c9ca --- /dev/null +++ b/src/test/resources/com/google/maps/ReverseGeocodeWithAddressDescriptorsResponse.json @@ -0,0 +1,742 @@ +{ + "results": [ + { + "address_components": [ + { + "long_name": "北山鹿苑寺金閣寺", + "short_name": "北山鹿苑寺金閣寺", + "types": [ + "premise" + ] + }, + { + "long_name": "1", + "short_name": "1", + "types": [ + "political", + "sublocality", + "sublocality_level_4" + ] + }, + { + "long_name": "Kinkakujichō", + "short_name": "Kinkakujichō", + "types": [ + "political", + "sublocality", + "sublocality_level_1" + ] + }, + { + "long_name": "Kita-ku", + "short_name": "Kita-ku", + "types": [ + "locality", + "political", + "ward" + ] + }, + { + "long_name": "Kyōto-shi", + "short_name": "Kyōto-shi", + "types": [ + "locality", + "political" + ] + }, + { + "long_name": "Kyōto-fu", + "short_name": "Kyōto-fu", + "types": [ + "administrative_area_level_1", + "political" + ] + }, + { + "long_name": "Japan", + "short_name": "JP", + "types": [ + "country", + "political" + ] + }, + { + "long_name": "603-8361", + "short_name": "603-8361", + "types": [ + "postal_code" + ] + } + ], + "formatted_address": "Japan, 〒603-8361 Kyōto-fu, Kyōto-shi, Kita-ku, Kinkakujichō, 1 北山鹿苑寺金閣寺", + "geometry": { + "bounds": { + "northeast": { + "lat": 35.0396014, + "lng": 135.7295118 + }, + "southwest": { + "lat": 35.0391291, + "lng": 135.7289492 + } + }, + "location": { + "lat": 35.0393986, + "lng": 135.7293744 + }, + "location_type": "ROOFTOP", + "viewport": { + "northeast": { + "lat": 35.0407142302915, + "lng": 135.7305794802915 + }, + "southwest": { + "lat": 35.0380162697085, + "lng": 135.7278815197085 + } + } + }, + "place_id": "ChIJLxi4xCCoAWAR0nKK_sUaOtM", + "types": [ + "premise" + ] + }, + { + "address_components": [ + { + "long_name": "1", + "short_name": "1", + "types": [ + "political", + "sublocality", + "sublocality_level_4" + ] + }, + { + "long_name": "Kinkakujichō", + "short_name": "Kinkakujichō", + "types": [ + "political", + "sublocality", + "sublocality_level_1" + ] + }, + { + "long_name": "Kita-ku", + "short_name": "Kita-ku", + "types": [ + "locality", + "political", + "ward" + ] + }, + { + "long_name": "Kyōto-shi", + "short_name": "Kyōto-shi", + "types": [ + "locality", + "political" + ] + }, + { + "long_name": "Kyōto-fu", + "short_name": "Kyōto-fu", + "types": [ + "administrative_area_level_1", + "political" + ] + }, + { + "long_name": "Japan", + "short_name": "JP", + "types": [ + "country", + "political" + ] + }, + { + "long_name": "603-8361", + "short_name": "603-8361", + "types": [ + "postal_code" + ] + } + ], + "formatted_address": "Japan, 〒603-8361 Kyōto-fu, Kyōto-shi, Kita-ku, Kinkakujichō, 1", + "geometry": { + "location": { + "lat": 35.0393553, + "lng": 135.7293265 + }, + "location_type": "GEOMETRIC_CENTER", + "viewport": { + "northeast": { + "lat": 35.04070428029149, + "lng": 135.7306754802915 + }, + "southwest": { + "lat": 35.0380063197085, + "lng": 135.7279775197085 + } + } + }, + "place_id": "ChIJnT1kwyCoAWAR-d2HQrYxlTs", + "types": [ + "political", + "sublocality", + "sublocality_level_4" + ] + }, + { + "address_components": [ + { + "long_name": "Kinkakujicho", + "short_name": "Kinkakujicho", + "types": [ + "political", + "sublocality", + "sublocality_level_1" + ] + }, + { + "long_name": "Kita Ward", + "short_name": "Kita Ward", + "types": [ + "locality", + "political", + "ward" + ] + }, + { + "long_name": "Kyoto", + "short_name": "Kyoto", + "types": [ + "locality", + "political" + ] + }, + { + "long_name": "Kyoto Prefecture", + "short_name": "Kyoto Prefecture", + "types": [ + "administrative_area_level_1", + "political" + ] + }, + { + "long_name": "Japan", + "short_name": "JP", + "types": [ + "country", + "political" + ] + }, + { + "long_name": "603-8361", + "short_name": "603-8361", + "types": [ + "postal_code" + ] + } + ], + "formatted_address": "Kinkakujicho, Kita Ward, Kyoto, Kyoto Prefecture 603-8361, Japan", + "geometry": { + "bounds": { + "northeast": { + "lat": 35.0409162, + "lng": 135.7318809 + }, + "southwest": { + "lat": 35.0381293, + "lng": 135.7271086 + } + }, + "location": { + "lat": 35.0393553, + "lng": 135.7293265 + }, + "location_type": "APPROXIMATE", + "viewport": { + "northeast": { + "lat": 35.0409162, + "lng": 135.7318809 + }, + "southwest": { + "lat": 35.0381293, + "lng": 135.7271086 + } + } + }, + "place_id": "ChIJe9XMwiCoAWARVrQpOsYqdBE", + "types": [ + "political", + "sublocality", + "sublocality_level_1" + ] + }, + { + "address_components": [ + { + "long_name": "Kita Ward", + "short_name": "Kita Ward", + "types": [ + "locality", + "political", + "ward" + ] + }, + { + "long_name": "Kyoto", + "short_name": "Kyoto", + "types": [ + "locality", + "political" + ] + }, + { + "long_name": "Kyoto Prefecture", + "short_name": "Kyoto Prefecture", + "types": [ + "administrative_area_level_1", + "political" + ] + }, + { + "long_name": "Japan", + "short_name": "JP", + "types": [ + "country", + "political" + ] + } + ], + "formatted_address": "Kita Ward, Kyoto, Kyoto Prefecture, Japan", + "geometry": { + "bounds": { + "northeast": { + "lat": 35.1714945, + "lng": 135.7728535 + }, + "southwest": { + "lat": 35.0222614, + "lng": 135.6471605 + } + }, + "location": { + "lat": 35.041053, + "lng": 135.7539826 + }, + "location_type": "APPROXIMATE", + "viewport": { + "northeast": { + "lat": 35.1714945, + "lng": 135.7728535 + }, + "southwest": { + "lat": 35.0222614, + "lng": 135.6471605 + } + } + }, + "place_id": "ChIJHSR_jiupAWARcQjngz-_Cxk", + "types": [ + "locality", + "political", + "ward" + ] + }, + { + "address_components": [ + { + "long_name": "Kyoto", + "short_name": "Kyoto", + "types": [ + "locality", + "political" + ] + }, + { + "long_name": "Kyoto Prefecture", + "short_name": "Kyoto Prefecture", + "types": [ + "administrative_area_level_1", + "political" + ] + }, + { + "long_name": "Japan", + "short_name": "JP", + "types": [ + "country", + "political" + ] + } + ], + "formatted_address": "Kyoto, Kyoto Prefecture, Japan", + "geometry": { + "bounds": { + "northeast": { + "lat": 35.32119230000001, + "lng": 135.878779 + }, + "southwest": { + "lat": 34.8748598, + "lng": 135.5589845 + } + }, + "location": { + "lat": 35.0116363, + "lng": 135.7680294 + }, + "location_type": "APPROXIMATE", + "viewport": { + "northeast": { + "lat": 35.0542, + "lng": 135.8236 + }, + "southwest": { + "lat": 34.958, + "lng": 135.6983 + } + } + }, + "place_id": "ChIJ8cM8zdaoAWARPR27azYdlsA", + "types": [ + "locality", + "political" + ] + }, + { + "address_components": [ + { + "long_name": "603-8361", + "short_name": "603-8361", + "types": [ + "postal_code" + ] + }, + { + "long_name": "Kinkakujicho", + "short_name": "Kinkakujicho", + "types": [ + "political", + "sublocality", + "sublocality_level_1" + ] + }, + { + "long_name": "Kita Ward", + "short_name": "Kita Ward", + "types": [ + "locality", + "political", + "ward" + ] + }, + { + "long_name": "Kyoto", + "short_name": "Kyoto", + "types": [ + "locality", + "political" + ] + }, + { + "long_name": "Kyoto Prefecture", + "short_name": "Kyoto Prefecture", + "types": [ + "administrative_area_level_1", + "political" + ] + }, + { + "long_name": "Japan", + "short_name": "JP", + "types": [ + "country", + "political" + ] + } + ], + "formatted_address": "603-8361, Japan", + "geometry": { + "bounds": { + "northeast": { + "lat": 35.0409162, + "lng": 135.7318809 + }, + "southwest": { + "lat": 35.0381293, + "lng": 135.7271086 + } + }, + "location": { + "lat": 35.0392985, + "lng": 135.7290044 + }, + "location_type": "APPROXIMATE", + "viewport": { + "northeast": { + "lat": 35.0409162, + "lng": 135.7318809 + }, + "southwest": { + "lat": 35.0381293, + "lng": 135.7271086 + } + } + }, + "place_id": "ChIJnT1kwyCoAWARkK61Za4dRY4", + "types": [ + "postal_code" + ] + }, + { + "address_components": [ + { + "long_name": "Osaka Metropolitan Area", + "short_name": "Osaka Metropolitan Area", + "types": [ + "political" + ] + }, + { + "long_name": "Japan", + "short_name": "JP", + "types": [ + "country", + "political" + ] + } + ], + "formatted_address": "Japan, Osaka Metropolitan Area", + "geometry": { + "bounds": { + "northeast": { + "lat": 35.1393087, + "lng": 136.0102211 + }, + "southwest": { + "lat": 34.3113767, + "lng": 134.4371108 + } + }, + "location": { + "lat": 34.7307812, + "lng": 135.5251982 + }, + "location_type": "APPROXIMATE", + "viewport": { + "northeast": { + "lat": 35.1393087, + "lng": 136.0102211 + }, + "southwest": { + "lat": 34.3113767, + "lng": 134.4371108 + } + } + }, + "place_id": "ChIJN7QiqzCwAGAR3arYsOjiWEY", + "types": [ + "political" + ] + }, + { + "address_components": [ + { + "long_name": "Kyoto Prefecture", + "short_name": "Kyoto Prefecture", + "types": [ + "administrative_area_level_1", + "establishment", + "point_of_interest", + "political" + ] + }, + { + "long_name": "Japan", + "short_name": "JP", + "types": [ + "country", + "political" + ] + } + ], + "formatted_address": "Kyoto Prefecture, Japan", + "geometry": { + "bounds": { + "northeast": { + "lat": 35.7793193, + "lng": 136.0540829 + }, + "southwest": { + "lat": 34.7059884, + "lng": 134.8536955 + } + }, + "location": { + "lat": 35.0212466, + "lng": 135.7555968 + }, + "location_type": "APPROXIMATE", + "viewport": { + "northeast": { + "lat": 35.7793193, + "lng": 136.0540829 + }, + "southwest": { + "lat": 34.7059885, + "lng": 134.8536957 + } + } + }, + "place_id": "ChIJYRsf-SB0_18ROJWxOMJ7Clk", + "types": [ + "administrative_area_level_1", + "establishment", + "point_of_interest", + "political" + ] + }, + { + "address_components": [ + { + "long_name": "Japan", + "short_name": "JP", + "types": [ + "country", + "political" + ] + } + ], + "formatted_address": "Japan", + "geometry": { + "bounds": { + "northeast": { + "lat": 45.6412626, + "lng": 154.0031455 + }, + "southwest": { + "lat": 20.3585295, + "lng": 122.8554688 + } + }, + "location": { + "lat": 36.204824, + "lng": 138.252924 + }, + "location_type": "APPROXIMATE", + "viewport": { + "northeast": { + "lat": 45.52177229999999, + "lng": 145.8162778 + }, + "southwest": { + "lat": 24.0459244, + "lng": 122.9338302 + } + } + }, + "place_id": "ChIJLxl_1w9OZzQRRFJmfNR1QvU", + "types": [ + "country", + "political" + ] + } + ], + "address_descriptor": { + "landmarks": [ + { + "place_id": "ChIJvUbrwCCoAWARX2QiHCsn5A4", + "display_name": { + "text": "Kinkaku-ji", + "language_code": "en" + }, + "types": [ + "establishment", + "place_of_worship", + "point_of_interest", + "tourist_attraction" + ], + "spatial_relationship": "NEAR", + "straight_line_distance_meters": 0.009104185 + }, + { + "place_id": "ChIJf2s61SCoAWARVtK8cnSu6zw", + "display_name": { + "text": "Shariden Kinkaku", + "language_code": "en" + }, + "types": [ + "establishment", + "place_of_worship", + "point_of_interest", + "tourist_attraction" + ], + "spatial_relationship": "WITHIN", + "straight_line_distance_meters": 73.58092 + }, + { + "place_id": "ChIJXZeF2jipAWARNbF8pJDRjFc", + "display_name": { + "text": "Kyōko-chi Pond", + "language_code": "en" + }, + "types": [ + "establishment", + "park", + "point_of_interest" + ], + "spatial_relationship": "BEHIND", + "straight_line_distance_meters": 57.99922 + }, + { + "place_id": "ChIJj69vLCapAWAR0FBBPEfPeAQ", + "display_name": { + "text": "鹿苑寺(金閣寺)", + "language_code": "ja" + }, + "types": [ + "establishment", + "place_of_worship", + "point_of_interest" + ], + "spatial_relationship": "WITHIN", + "straight_line_distance_meters": 32.30453 + }, + { + "place_id": "ChIJ482HblCpAWARoLBXDZpv7aI", + "display_name": { + "text": "Kinkaku-ji Fence", + "language_code": "en" + }, + "types": [ + "establishment", + "point_of_interest" + ], + "spatial_relationship": "WITHIN", + "straight_line_distance_meters": 99.38629 + } + ], + "areas": [ + { + "place_id": "ChIJe9XMwiCoAWARVrQpOsYqdBE", + "display_name": { + "text": "Kinkakujicho", + "language_code": "en" + }, + "containment" : "WITHIN" + }, + { + "place_id": "ChIJk-6T5COoAWARa-KMWGWzrwQ", + "display_name": { + "text": "Kinkaku-ji", + "language_code": "en" + }, + "containment" : "OUTSKIRTS" + } + ] + }, + "status": "OK" + } \ No newline at end of file