Skip to content

Commit 21376b3

Browse files
authored
158 support geocoding not working (#160)
1 parent 9c85c84 commit 21376b3

2 files changed

Lines changed: 359 additions & 6 deletions

File tree

src/main/java/com/dedicatedcode/reitti/service/geocoding/DefaultGeocodeServiceManager.java

Lines changed: 57 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -126,24 +126,75 @@ private Optional<GeocodeResult> extractGeoCodeResult(String response) throws Jso
126126

127127
//try to find elements from address;
128128
JsonNode address = properties.path("address");
129-
if (address.isMissingNode()) {
129+
JsonNode geocoding = properties.path("geocoding");
130+
if (geocoding.isObject()) {
131+
label = geocoding.path("name").asText();
132+
if (label.isBlank()) {
133+
label = geocoding.path("label").asText();
134+
}
135+
street = geocoding.path("street").asText();
136+
if (street.isBlank()) {
137+
street = geocoding.path("road").asText();
138+
}
139+
if (geocoding.has("housenumber")) {
140+
street = street + " " + geocoding.path("housenumber").asText();
141+
}
142+
city = geocoding.path("city").asText();
143+
district = geocoding.path("city_district").asText();
144+
if (district.isBlank()) {
145+
district = geocoding.path("district").asText();
146+
}
147+
if (district.isBlank()) {
148+
district = geocoding.path("locality").asText();
149+
}
150+
} else if (address.isMissingNode()) {
151+
//try to find it directly under the root node
130152
label = properties.path("formatted").asText("");
131153
street = properties.path("street").asText("");
132154
city = properties.path("city").asText("");
133-
district = properties.path("district").asText("");
155+
district = properties.path("city_district").asText("");
134156
} else {
157+
//there is an address, find it there
135158
label = properties.path("name").asText("");
136159
street = address.path("road").asText("");
137160
city = address.path("city").asText("");
138161
district = address.path("city_district").asText("");
139162
}
140163

141-
if (label.isEmpty() && !street.isEmpty()) {
142-
label = street;
164+
Optional<GeocodeResult> result = createGeoCodeResult(label, street, city, district);
165+
if (result.isPresent()) {
166+
return result;
167+
}
168+
}
169+
170+
if (root.has("name") && root.has("address")) {
171+
String label = root.get("name").asText();
172+
String street = root.path("address").path("street").asText();
173+
if (street.isBlank()) {
174+
street = root.path("address").path("road").asText();
143175
}
144-
if (StringUtils.hasText(label)) {
145-
return Optional.of(new GeocodeResult(label, street, "", city, "", district));
176+
if (root.path("address").path("house_number").isTextual()) {
177+
street = street + " " + root.path("address").path("house_number").asText();
146178
}
179+
String city = root.path("address").path("city").asText();
180+
String district = root.path("address").path("district").asText();
181+
if (district.isBlank()) {
182+
district = root.path("address").path("neighbourhood").asText();
183+
}
184+
Optional<GeocodeResult> result = createGeoCodeResult(label, street, city, district);
185+
if (result.isPresent()) {
186+
return result;
187+
}
188+
}
189+
return Optional.empty();
190+
}
191+
192+
private static Optional<GeocodeResult> createGeoCodeResult(String label, String street, String city, String district) {
193+
if (label.isEmpty() && !street.isEmpty()) {
194+
label = street;
195+
}
196+
if (StringUtils.hasText(label)) {
197+
return Optional.of(new GeocodeResult(label, street, "", city, "", district));
147198
}
148199
return Optional.empty();
149200
}
Lines changed: 302 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,302 @@
1+
package com.dedicatedcode.reitti.service.geocoding;
2+
3+
import com.dedicatedcode.reitti.model.RemoteGeocodeService;
4+
import com.dedicatedcode.reitti.repository.GeocodeServiceJdbcService;
5+
import com.fasterxml.jackson.databind.ObjectMapper;
6+
import org.junit.jupiter.api.BeforeEach;
7+
import org.junit.jupiter.api.Test;
8+
import org.junit.jupiter.api.extension.ExtendWith;
9+
import org.mockito.Mock;
10+
import org.mockito.junit.jupiter.MockitoExtension;
11+
import org.springframework.web.client.RestTemplate;
12+
13+
import java.util.Collections;
14+
import java.util.List;
15+
import java.util.Optional;
16+
17+
import static org.assertj.core.api.Assertions.assertThat;
18+
import static org.mockito.ArgumentMatchers.*;
19+
import static org.mockito.Mockito.verify;
20+
import static org.mockito.Mockito.when;
21+
22+
@ExtendWith(MockitoExtension.class)
23+
class DefaultGeocodeServiceManagerTest {
24+
25+
@Mock
26+
private GeocodeServiceJdbcService geocodeServiceJdbcService;
27+
28+
@Mock
29+
private RestTemplate restTemplate;
30+
31+
@Mock
32+
private GeocodeService fixedGeocodeService;
33+
34+
private DefaultGeocodeServiceManager geocodeServiceManager;
35+
private ObjectMapper objectMapper;
36+
37+
@BeforeEach
38+
void setUp() {
39+
objectMapper = new ObjectMapper();
40+
geocodeServiceManager = new DefaultGeocodeServiceManager(
41+
geocodeServiceJdbcService,
42+
Collections.emptyList(),
43+
restTemplate,
44+
objectMapper,
45+
3
46+
);
47+
}
48+
49+
@Test
50+
void shouldReturnEmptyWhenNoServicesAvailable() {
51+
// Given
52+
when(geocodeServiceJdbcService.findByEnabledTrueOrderByLastUsedAsc())
53+
.thenReturn(Collections.emptyList());
54+
55+
// When
56+
Optional<GeocodeResult> result = geocodeServiceManager.reverseGeocode(53.863149, 10.700927);
57+
58+
// Then
59+
assertThat(result).isEmpty();
60+
}
61+
62+
@Test
63+
void shouldReturnGeocodeResultFromRemoteService() {
64+
// Given
65+
double latitude = 53.863149;
66+
double longitude = 10.700927;
67+
68+
RemoteGeocodeService service = new RemoteGeocodeService(
69+
1L, "Test Service", "http://test.com?lat={lat}&lng={lng}",
70+
true, 0, null, null, 1L
71+
);
72+
73+
when(geocodeServiceJdbcService.findByEnabledTrueOrderByLastUsedAsc())
74+
.thenReturn(List.of(service));
75+
76+
String mockResponse = """
77+
{
78+
"features": [
79+
{
80+
"properties": {
81+
"name": "Test Location",
82+
"address": {
83+
"road": "Test Street",
84+
"city": "Test City",
85+
"city_district": "Test District"
86+
}
87+
}
88+
}
89+
]
90+
}
91+
""";
92+
93+
when(restTemplate.getForObject(anyString(), eq(String.class)))
94+
.thenReturn(mockResponse);
95+
96+
// When
97+
Optional<GeocodeResult> result = geocodeServiceManager.reverseGeocode(latitude, longitude);
98+
99+
// Then
100+
assertThat(result).isPresent();
101+
GeocodeResult geocodeResult = result.get();
102+
assertThat(geocodeResult.label()).isEqualTo("Test Location");
103+
assertThat(geocodeResult.street()).isEqualTo("Test Street");
104+
assertThat(geocodeResult.city()).isEqualTo("Test City");
105+
assertThat(geocodeResult.district()).isEqualTo("Test District");
106+
107+
verify(geocodeServiceJdbcService).save(any(RemoteGeocodeService.class));
108+
}
109+
110+
@Test
111+
void shouldReturnCorrectGeoCodeResultFromRemoteService() {
112+
// Given
113+
double latitude = 53.863149;
114+
double longitude = 10.700927;
115+
116+
RemoteGeocodeService service = new RemoteGeocodeService(
117+
1L, "Test Service", "http://test.com?lat={lat}&lng={lng}",
118+
true, 0, null, null, 1L
119+
);
120+
121+
when(geocodeServiceJdbcService.findByEnabledTrueOrderByLastUsedAsc())
122+
.thenReturn(List.of(service));
123+
124+
String mockResponse = """
125+
{"place_id":309281591,"licence":"Data © OpenStreetMap contributors, ODbL 1.0. http://osm.org/copyright","osm_type":"way","osm_id":555816145,"lat":"38.9763500","lon":"-94.5953511","class":"amenity","type":"fast_food","place_rank":30,"importance":7.305638208586279e-05,"addresstype":"amenity","name":"McDonald's","display_name":"McDonald's, 8326, Wornall Road, Waldo, Kansas City, Jackson County, Missouri, 64114, United States","address":{"amenity":"McDonald's","house_number":"8326","road":"Wornall Road","neighbourhood":"Waldo","city":"Kansas City","county":"Jackson County","state":"Missouri","ISO3166-2-lvl4":"US-MO","postcode":"64114","country":"United States","country_code":"us"},"boundingbox":["38.9762717","38.9764468","-94.5955208","-94.5951802"]}
126+
""";
127+
128+
when(restTemplate.getForObject(anyString(), eq(String.class)))
129+
.thenReturn(mockResponse);
130+
131+
// When
132+
Optional<GeocodeResult> result = geocodeServiceManager.reverseGeocode(latitude, longitude);
133+
134+
// Then
135+
assertThat(result).isPresent();
136+
GeocodeResult geocodeResult = result.get();
137+
assertThat(geocodeResult.label()).isEqualTo("McDonald's");
138+
assertThat(geocodeResult.street()).isEqualTo("Wornall Road 8326");
139+
assertThat(geocodeResult.city()).isEqualTo("Kansas City");
140+
assertThat(geocodeResult.district()).isEqualTo("Waldo");
141+
142+
verify(geocodeServiceJdbcService).save(any(RemoteGeocodeService.class));
143+
}
144+
145+
@Test
146+
void shouldReturnCorrectGeoCodeResultFromGeoCodeJsonRemoteService() {
147+
// Given
148+
double latitude = 53.863149;
149+
double longitude = 10.700927;
150+
151+
RemoteGeocodeService service = new RemoteGeocodeService(
152+
1L, "Test Service", "http://test.com?lat={lat}&lng={lng}",
153+
true, 0, null, null, 1L
154+
);
155+
156+
when(geocodeServiceJdbcService.findByEnabledTrueOrderByLastUsedAsc())
157+
.thenReturn(List.of(service));
158+
159+
String mockResponse = """
160+
{
161+
"type": "FeatureCollection",
162+
"geocoding": {
163+
"version": "0.1.0",
164+
"attribution": "Data © OpenStreetMap contributors, ODbL 1.0. http://osm.org/copyright",
165+
"licence": "ODbL",
166+
"query": ""
167+
},
168+
"features": [
169+
{
170+
"type": "Feature",
171+
"properties": {
172+
"geocoding": {
173+
"place_id": 309600843,
174+
"osm_type": "way",
175+
"osm_id": 555816145,
176+
"osm_key": "amenity",
177+
"osm_value": "fast_food",
178+
"type": "house",
179+
"accuracy": 0,
180+
"label": "McDonald's, 8326, Wornall Road, Waldo, Kansas City, Jackson County, Missouri, 64114, United States",
181+
"name": "McDonald's",
182+
"housenumber": "8326",
183+
"postcode": "64114",
184+
"street": "Wornall Road",
185+
"locality": "Waldo",
186+
"city": "Kansas City",
187+
"county": "Jackson County",
188+
"state": "Missouri",
189+
"country": "United States",
190+
"country_code": "us",
191+
"admin": {
192+
"level8": "Kansas City",
193+
"level6": "Jackson County",
194+
"level4": "Missouri"
195+
}
196+
}
197+
},
198+
"geometry": {
199+
"type": "Point",
200+
"coordinates": [
201+
-94.59535111452982,
202+
38.97635
203+
]
204+
}
205+
}
206+
]
207+
}
208+
""";
209+
210+
when(restTemplate.getForObject(anyString(), eq(String.class)))
211+
.thenReturn(mockResponse);
212+
213+
// When
214+
Optional<GeocodeResult> result = geocodeServiceManager.reverseGeocode(latitude, longitude);
215+
216+
// Then
217+
assertThat(result).isPresent();
218+
GeocodeResult geocodeResult = result.get();
219+
assertThat(geocodeResult.label()).isEqualTo("McDonald's");
220+
assertThat(geocodeResult.street()).isEqualTo("Wornall Road 8326");
221+
assertThat(geocodeResult.city()).isEqualTo("Kansas City");
222+
assertThat(geocodeResult.district()).isEqualTo("Waldo");
223+
224+
verify(geocodeServiceJdbcService).save(any(RemoteGeocodeService.class));
225+
}
226+
227+
@Test
228+
void shouldUseFixedGeocodeServiceWhenAvailable() {
229+
// Given
230+
double latitude = 53.863149;
231+
double longitude = 10.700927;
232+
233+
DefaultGeocodeServiceManager managerWithFixedService = new DefaultGeocodeServiceManager(
234+
geocodeServiceJdbcService,
235+
List.of(fixedGeocodeService),
236+
restTemplate,
237+
objectMapper,
238+
3
239+
);
240+
241+
when(fixedGeocodeService.getName()).thenReturn("Photon Service");
242+
when(fixedGeocodeService.getUrlTemplate()).thenReturn("http://photon.test?lat={lat}&lng={lng}");
243+
244+
String photonResponse = """
245+
{
246+
"features": [
247+
{
248+
"properties": {
249+
"name": "Photon Location",
250+
"street": "Photon Street",
251+
"city": "Photon City",
252+
"district": "Photon District",
253+
"housenumber": "123",
254+
"postcode": "12345"
255+
}
256+
}
257+
]
258+
}
259+
""";
260+
261+
when(restTemplate.getForObject(anyString(), eq(String.class)))
262+
.thenReturn(photonResponse);
263+
264+
// When
265+
Optional<GeocodeResult> result = managerWithFixedService.reverseGeocode(latitude, longitude);
266+
267+
// Then
268+
assertThat(result).isPresent();
269+
GeocodeResult geocodeResult = result.get();
270+
assertThat(geocodeResult.label()).isEqualTo("Photon Location");
271+
assertThat(geocodeResult.street()).isEqualTo("Photon Street");
272+
assertThat(geocodeResult.city()).isEqualTo("Photon City");
273+
assertThat(geocodeResult.district()).isEqualTo("Photon District");
274+
assertThat(geocodeResult.houseNumber()).isEqualTo("123");
275+
assertThat(geocodeResult.postcode()).isEqualTo("12345");
276+
}
277+
278+
@Test
279+
void shouldHandleServiceErrorAndRecordIt() {
280+
// Given
281+
double latitude = 53.863149;
282+
double longitude = 10.700927;
283+
284+
RemoteGeocodeService service = new RemoteGeocodeService(
285+
1L, "Failing Service", "http://fail.com?lat={lat}&lng={lng}",
286+
true, 0, null, null, 1L
287+
);
288+
289+
when(geocodeServiceJdbcService.findByEnabledTrueOrderByLastUsedAsc())
290+
.thenReturn(List.of(service));
291+
292+
when(restTemplate.getForObject(anyString(), eq(String.class)))
293+
.thenThrow(new RuntimeException("Service unavailable"));
294+
295+
// When
296+
Optional<GeocodeResult> result = geocodeServiceManager.reverseGeocode(latitude, longitude);
297+
298+
// Then
299+
assertThat(result).isEmpty();
300+
verify(geocodeServiceJdbcService).save(any(RemoteGeocodeService.class));
301+
}
302+
}

0 commit comments

Comments
 (0)