3333
3434@ Service
3535public class GeocodingClient {
36- public static final String GEOCODING_VERSION = "street-address-v1 " ;
36+ public static final String GEOCODING_VERSION = "street-address-v2 " ;
3737
3838 private static final int DEFAULT_OWNER_CALLS_PER_HOUR = 1_000 ;
3939 private static final int DEFAULT_GLOBAL_CALLS_PER_HOUR = 2_000 ;
@@ -107,11 +107,11 @@ public List<PlaceCandidate> batchGeocode(String owner, List<PlaceCandidate> inpu
107107 uniqueQueries .putIfAbsent (candidate .text (), candidate );
108108 }
109109
110- reserve (owner , uniqueQueries .size ());
110+ reserve (owner , uniqueQueries .size (), true );
111111 try {
112112 Map <String , PlaceCandidate > resultsByQuery = new HashMap <>();
113113 for (Map .Entry <String , PlaceCandidate > query : uniqueQueries .entrySet ()) {
114- resultsByQuery .put (query .getKey (), geocodeOne (query .getValue ()));
114+ resultsByQuery .put (query .getKey (), geocodeOne (owner , query .getValue ()));
115115 }
116116 return inputs .stream ().map (candidate -> {
117117 PlaceCandidate result = resultsByQuery .get (candidate .text ());
@@ -122,7 +122,7 @@ public List<PlaceCandidate> batchGeocode(String owner, List<PlaceCandidate> inpu
122122 }
123123 }
124124
125- private void reserve (String owner , int calls ) {
125+ private void reserve (String owner , int calls , boolean newBatch ) {
126126 long now = System .nanoTime ();
127127 synchronized (budgetLock ) {
128128 globalUsage .prune (now );
@@ -135,14 +135,16 @@ private void reserve(String owner, int calls) {
135135 int activeForOwner = activeOwnerBatches .getOrDefault (owner , 0 );
136136 if (calls > ownerCallsPerHour || calls > globalCallsPerHour
137137 || account .calls + calls > ownerCallsPerHour || globalUsage .calls + calls > globalCallsPerHour
138- || activeForOwner >= ownerConcurrentBatches || activeGlobalBatches >= globalConcurrentBatches ) {
138+ || ( newBatch && ( activeForOwner >= ownerConcurrentBatches || activeGlobalBatches >= globalConcurrentBatches )) ) {
139139 throw new ResponseStatusException (HttpStatus .TOO_MANY_REQUESTS ,
140140 "Geocoding rate or concurrency limit exceeded; retry later" );
141141 }
142142 account .reserve (now , calls );
143143 globalUsage .reserve (now , calls );
144- activeOwnerBatches .put (owner , activeForOwner + 1 );
145- activeGlobalBatches ++;
144+ if (newBatch ) {
145+ activeOwnerBatches .put (owner , activeForOwner + 1 );
146+ activeGlobalBatches ++;
147+ }
146148 }
147149 }
148150
@@ -182,8 +184,16 @@ private HttpResponse<String> sendWithGlobalPacing(HttpRequest request) {
182184 }
183185 }
184186
185- private PlaceCandidate geocodeOne (PlaceCandidate candidate ) {
186- URI uri = URI .create (geocodeUrl + "?address=" + encode (withoutSecondaryUnit (candidate .text ()))
187+ private PlaceCandidate geocodeOne (String owner , PlaceCandidate candidate ) {
188+ PlaceCandidate resolved = lookup (candidate , candidate .text ());
189+ String primaryAddress = withoutSecondaryUnit (candidate .text ());
190+ if (resolved .pid () != null || primaryAddress .equals (candidate .text ())) return resolved ;
191+ reserve (owner , 1 , false );
192+ return lookup (candidate , primaryAddress );
193+ }
194+
195+ private PlaceCandidate lookup (PlaceCandidate candidate , String address ) {
196+ URI uri = URI .create (geocodeUrl + "?address=" + encode (address )
187197 + "®ion=" + encode (regionBias ) + "&key=" + encode (apiKey ));
188198 HttpRequest request = HttpRequest .newBuilder (uri ).timeout (Duration .ofSeconds (15 )).GET ().build ();
189199 HttpResponse <String > response = sendWithGlobalPacing (request );
@@ -196,7 +206,7 @@ private PlaceCandidate geocodeOne(PlaceCandidate candidate) {
196206 }
197207 if (body == null ) throw new IllegalStateException ("Geocoding returned an empty response" );
198208 String status = body .path ("status" ).asString ();
199- if ("OK" .equals (status ) && body .path ("results" ).isArray () && ! body . path ( "results" ). isEmpty () ) {
209+ if ("OK" .equals (status ) && body .path ("results" ).isArray ()) {
200210 String requestedHouseNumber = houseNumber (candidate .text ());
201211 for (JsonNode match : body .path ("results" )) {
202212 boolean addressType = hasType (match , "street_address" ) || hasType (match , "premise" )
@@ -228,7 +238,9 @@ private PlaceCandidate geocodeOne(PlaceCandidate candidate) {
228238 }
229239
230240 private static String withoutSecondaryUnit (String address ) {
231- return SECONDARY_UNIT .matcher (address ).replaceFirst ("" )
241+ Matcher unit = SECONDARY_UNIT .matcher (address );
242+ if (!unit .find ()) return address ;
243+ return unit .replaceFirst ("" )
232244 .replaceAll ("\\ s+" , " " )
233245 .replaceAll ("\\ s*,\\ s*" , ", " )
234246 .replaceAll ("(?:,\\ s*){2,}" , ", " )
0 commit comments