Skip to content

Commit f81564b

Browse files
authored
31 add attribution for data source in the global response object (#32)
* refactor: extract GeoJSON conversion logic to `GeometryHelper` and remove duplication - Centralized `convertJtsToGeoJson` and related methods into the new `GeometryHelper` utility class. - Replaced in-line GeoJSON conversion in services with calls to `GeometryHelper`. - Enhanced `GeocodingController` to include metadata and data source information in responses. - Removed unused exception handling for missing POIs. * feat: add admin password property check for AdminController
1 parent 60fb7a3 commit f81564b

6 files changed

Lines changed: 107 additions & 150 deletions

File tree

src/main/java/com/dedicatedcode/paikka/controller/AdminController.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import com.dedicatedcode.paikka.service.BuildingService;
2323
import org.slf4j.Logger;
2424
import org.slf4j.LoggerFactory;
25+
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
2526
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
2627
import org.springframework.http.HttpStatus;
2728
import org.springframework.http.ResponseEntity;
@@ -38,6 +39,7 @@
3839
@RestController
3940
@RequestMapping("/admin")
4041
@ConditionalOnProperty(name = "paikka.import-mode", havingValue = "false", matchIfMissing = true)
42+
@ConditionalOnExpression("!'${paikka.admin.password}'.trim().isEmpty()")
4143
public class AdminController {
4244

4345
private static final Logger logger = LoggerFactory.getLogger(AdminController.class);

src/main/java/com/dedicatedcode/paikka/controller/GeocodingController.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,10 @@ public ResponseEntity<Map<String, Object>> reverse(
105105
"lang", lang,
106106
"limit", effectiveLimit
107107
));
108-
108+
Map<String, Object> metadata = metadataService.getMetadata();
109+
response.put("metadata", metadata);
110+
response.put("dataSources", Map.of("OpenStreetMap", "https://openstreetmap.org/copyright"));
111+
109112
return ResponseEntity.ok()
110113
.header("X-Result-Count", String.valueOf(results.size()))
111114
.header("Cache-Control", "no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0") // No caching

src/main/java/com/dedicatedcode/paikka/service/BoundaryService.java

Lines changed: 1 addition & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ public GeoJsonGeometry getBoundaryGeometry(long osmId) {
147147
WKBReader wkbReader = new WKBReader();
148148
org.locationtech.jts.geom.Geometry jtsGeometry = wkbReader.read(geometryData);
149149

150-
return convertJtsToGeoJson(jtsGeometry);
150+
return GeometryHelper.convertJtsToGeoJson(jtsGeometry);
151151

152152
} catch (RocksDBException e) {
153153
logger.error("RocksDB error retrieving boundary for OSM ID {}: {}", osmId, e.getMessage());
@@ -175,68 +175,4 @@ private byte[] longToByteArray(long value) {
175175
(byte) value
176176
};
177177
}
178-
179-
/**
180-
* Convert JTS Geometry to GeoJSON format.
181-
* This is a simplified conversion that handles basic geometry types.
182-
*/
183-
private GeoJsonGeometry convertJtsToGeoJson(org.locationtech.jts.geom.Geometry geometry) {
184-
String geometryType = geometry.getGeometryType();
185-
Object coordinates = null;
186-
187-
switch (geometryType) {
188-
case "Point":
189-
coordinates = new double[]{geometry.getCoordinate().x, geometry.getCoordinate().y};
190-
break;
191-
case "Polygon":
192-
coordinates = extractPolygonCoordinates(geometry);
193-
break;
194-
case "MultiPolygon":
195-
coordinates = extractMultiPolygonCoordinates(geometry);
196-
break;
197-
default:
198-
logger.debug("Unsupported geometry type for GeoJSON conversion: {}", geometryType);
199-
return new GeoJsonGeometry("Unknown", null);
200-
}
201-
202-
return new GeoJsonGeometry(geometryType, coordinates);
203-
}
204-
205-
private Object extractPolygonCoordinates(org.locationtech.jts.geom.Geometry polygon) {
206-
try {
207-
// Get exterior ring coordinates
208-
org.locationtech.jts.geom.Coordinate[] coords = polygon.getCoordinates();
209-
double[][] ring = new double[coords.length][2];
210-
211-
for (int i = 0; i < coords.length; i++) {
212-
ring[i][0] = coords[i].x; // longitude
213-
ring[i][1] = coords[i].y; // latitude
214-
}
215-
216-
// GeoJSON polygon format: [[[x,y],[x,y],...]]
217-
return new double[][][]{ring};
218-
} catch (Exception e) {
219-
logger.warn("Failed to extract polygon coordinates: {}", e.getMessage());
220-
return null;
221-
}
222-
}
223-
224-
private Object extractMultiPolygonCoordinates(org.locationtech.jts.geom.Geometry multiPolygon) {
225-
try {
226-
List<double[][][]> polygons = new ArrayList<>();
227-
228-
for (int i = 0; i < multiPolygon.getNumGeometries(); i++) {
229-
org.locationtech.jts.geom.Geometry polygon = multiPolygon.getGeometryN(i);
230-
Object polyCoords = extractPolygonCoordinates(polygon);
231-
if (polyCoords instanceof double[][][]) {
232-
polygons.add((double[][][]) polyCoords);
233-
}
234-
}
235-
236-
return polygons.toArray(new double[0][][][]);
237-
} catch (Exception e) {
238-
logger.warn("Failed to extract multipolygon coordinates: {}", e.getMessage());
239-
return null;
240-
}
241-
}
242178
}
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
/*
2+
* This file is part of paikka.
3+
*
4+
* Paikka is free software: you can redistribute it and/or
5+
* modify it under the terms of the GNU Affero General Public License
6+
* as published by the Free Software Foundation, either version 3 or
7+
* any later version.
8+
*
9+
* Paikka is distributed in the hope that it will be useful,
10+
* but WITHOUT ANY WARRANTY; without even the implied
11+
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12+
* See the GNU Affero General Public License for more details.
13+
* You should have received a copy of the GNU Affero General Public License
14+
* along with Paikka. If not, see <https://www.gnu.org/licenses/>.
15+
*/
16+
17+
package com.dedicatedcode.paikka.service;
18+
19+
import com.dedicatedcode.paikka.dto.GeoJsonGeometry;
20+
import org.locationtech.jts.geom.Geometry;
21+
import org.slf4j.Logger;
22+
import org.slf4j.LoggerFactory;
23+
24+
import java.util.ArrayList;
25+
import java.util.List;
26+
27+
public class GeometryHelper {
28+
private static final Logger logger = LoggerFactory.getLogger(GeometryHelper.class);
29+
30+
public static GeoJsonGeometry convertJtsToGeoJson(Geometry geometry) {
31+
String geometryType = geometry.getGeometryType();
32+
Object coordinates = null;
33+
34+
switch (geometryType) {
35+
case "Point":
36+
coordinates = new double[]{geometry.getCoordinate().x, geometry.getCoordinate().y};
37+
break;
38+
case "Polygon":
39+
// For polygons, we need to extract the exterior ring coordinates
40+
// This is a simplified implementation
41+
coordinates = extractPolygonCoordinates(geometry);
42+
break;
43+
case "MultiPolygon":
44+
// For multipolygons, extract all polygon coordinates
45+
coordinates = extractMultiPolygonCoordinates(geometry);
46+
break;
47+
default:
48+
// For other geometry types, just indicate the type
49+
logger.debug("Unsupported geometry type for GeoJSON conversion: {}", geometryType);
50+
return new GeoJsonGeometry("Unknown", null);
51+
}
52+
53+
return new GeoJsonGeometry(geometryType, coordinates);
54+
}
55+
56+
57+
private static Object extractPolygonCoordinates(Geometry polygon) {
58+
try {
59+
// Get exterior ring coordinates
60+
org.locationtech.jts.geom.Coordinate[] coords = polygon.getCoordinates();
61+
double[][] ring = new double[coords.length][2];
62+
63+
for (int i = 0; i < coords.length; i++) {
64+
ring[i][0] = coords[i].x; // longitude
65+
ring[i][1] = coords[i].y; // latitude
66+
}
67+
68+
// GeoJSON polygon format: [[[x,y],[x,y],...]]
69+
return new double[][][]{ring};
70+
} catch (Exception e) {
71+
logger.warn("Failed to extract polygon coordinates: {}", e.getMessage());
72+
return null;
73+
}
74+
}
75+
76+
private static Object extractMultiPolygonCoordinates(Geometry multiPolygon) {
77+
try {
78+
List<double[][][]> polygons = new ArrayList<>();
79+
80+
for (int i = 0; i < multiPolygon.getNumGeometries(); i++) {
81+
Geometry polygon = multiPolygon.getGeometryN(i);
82+
Object polyCoords = extractPolygonCoordinates(polygon);
83+
if (polyCoords instanceof double[][][]) {
84+
polygons.add((double[][][]) polyCoords);
85+
}
86+
}
87+
88+
return polygons.toArray(new double[0][][][]);
89+
} catch (Exception e) {
90+
logger.warn("Failed to extract multipolygon coordinates: {}", e.getMessage());
91+
return null;
92+
}
93+
}
94+
}

src/main/java/com/dedicatedcode/paikka/service/ReverseGeocodingService.java

Lines changed: 4 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919
import com.dedicatedcode.paikka.config.PaikkaConfiguration;
2020
import com.dedicatedcode.paikka.dto.GeoJsonGeometry;
2121
import com.dedicatedcode.paikka.dto.POIResponse;
22-
import com.dedicatedcode.paikka.exception.POINotFoundException;
2322
import com.dedicatedcode.paikka.flatbuffers.*;
2423
import org.locationtech.jts.geom.Geometry;
2524
import org.locationtech.jts.io.WKBReader;
@@ -105,18 +104,7 @@ public synchronized void reloadDatabase() {
105104
logger.warn("POI shards database reload completed but database is not available");
106105
}
107106
}
108-
109-
/**
110-
* Find the nearest POI to the given coordinates.
111-
*/
112-
public POIResponse findNearestPOI(double lat, double lon, String lang) {
113-
List<POIResponse> results = findNearbyPOIs(lat, lon, lang, 1);
114-
if (results.isEmpty()) {
115-
throw new POINotFoundException(String.format("No POI found within search radius for coordinates lat=%.6f, lon=%.6f", lat, lon));
116-
}
117-
return results.getFirst();
118-
}
119-
107+
120108
/**
121109
* Find nearby POIs to the given coordinates.
122110
*
@@ -411,7 +399,7 @@ private POIResponse convertPOIToResponse(POIData poi, double queryLat, double qu
411399
Geometry geometry = wkbReader.read(poi.boundary());
412400

413401
// Create a simple GeoJSON representation
414-
GeoJsonGeometry geoJsonGeometry = convertJtsToGeoJson(geometry);
402+
GeoJsonGeometry geoJsonGeometry = GeometryHelper.convertJtsToGeoJson(geometry);
415403
response.setBoundary(geoJsonGeometry);
416404

417405
logger.debug("POI {} has boundary geometry converted to GeoJSON", poi.id());
@@ -444,7 +432,7 @@ private void enhanceWithBuildingInfo(POIResponse response, POIData poi) {
444432
try {
445433
WKBReader wkbReader = new WKBReader();
446434
Geometry geometry = wkbReader.read(buildingInfo.getBoundaryWkb());
447-
GeoJsonGeometry geoJsonGeometry = convertJtsToGeoJson(geometry);
435+
GeoJsonGeometry geoJsonGeometry = GeometryHelper.convertJtsToGeoJson(geometry);
448436
response.setBoundary(geoJsonGeometry);
449437
logger.debug("Enhanced POI {} with building boundary", poi.id());
450438
} catch (Exception e) {
@@ -467,75 +455,7 @@ private record HierarchyData(int level, String type, String name, String code, l
467455

468456
private record POIWithDistance(POIData poi, double distance) {
469457
}
470-
471-
/**
472-
* Convert JTS Geometry to GeoJSON format.
473-
* This is a simplified conversion that handles basic geometry types.
474-
*/
475-
private GeoJsonGeometry convertJtsToGeoJson(Geometry geometry) {
476-
String geometryType = geometry.getGeometryType();
477-
Object coordinates = null;
478-
479-
switch (geometryType) {
480-
case "Point":
481-
coordinates = new double[]{geometry.getCoordinate().x, geometry.getCoordinate().y};
482-
break;
483-
case "Polygon":
484-
// For polygons, we need to extract the exterior ring coordinates
485-
// This is a simplified implementation
486-
coordinates = extractPolygonCoordinates(geometry);
487-
break;
488-
case "MultiPolygon":
489-
// For multipolygons, extract all polygon coordinates
490-
coordinates = extractMultiPolygonCoordinates(geometry);
491-
break;
492-
default:
493-
// For other geometry types, just indicate the type
494-
logger.debug("Unsupported geometry type for GeoJSON conversion: {}", geometryType);
495-
return new GeoJsonGeometry("Unknown", null);
496-
}
497-
498-
return new GeoJsonGeometry(geometryType, coordinates);
499-
}
500-
501-
private Object extractPolygonCoordinates(Geometry polygon) {
502-
try {
503-
// Get exterior ring coordinates
504-
org.locationtech.jts.geom.Coordinate[] coords = polygon.getCoordinates();
505-
double[][] ring = new double[coords.length][2];
506-
507-
for (int i = 0; i < coords.length; i++) {
508-
ring[i][0] = coords[i].x; // longitude
509-
ring[i][1] = coords[i].y; // latitude
510-
}
511-
512-
// GeoJSON polygon format: [[[x,y],[x,y],...]]
513-
return new double[][][]{ring};
514-
} catch (Exception e) {
515-
logger.warn("Failed to extract polygon coordinates: {}", e.getMessage());
516-
return null;
517-
}
518-
}
519-
520-
private Object extractMultiPolygonCoordinates(Geometry multiPolygon) {
521-
try {
522-
List<double[][][]> polygons = new ArrayList<>();
523-
524-
for (int i = 0; i < multiPolygon.getNumGeometries(); i++) {
525-
Geometry polygon = multiPolygon.getGeometryN(i);
526-
Object polyCoords = extractPolygonCoordinates(polygon);
527-
if (polyCoords instanceof double[][][]) {
528-
polygons.add((double[][][]) polyCoords);
529-
}
530-
}
531-
532-
return polygons.toArray(new double[0][][][]);
533-
} catch (Exception e) {
534-
logger.warn("Failed to extract multipolygon coordinates: {}", e.getMessage());
535-
return null;
536-
}
537-
}
538-
458+
539459
/**
540460
* Build the geometry URL using the configured base URL and current data version.
541461
*/

src/main/resources/application.properties

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ paikka.data-dir=./data
1616
paikka.import.threads=16
1717
paikka.import.chunk-size=100000
1818

19+
paikka.admin.password=
20+
1921
paikka.query.max-results=500
2022
paikka.query.default-results=10
2123
paikka.query.base-url=http://localhost:8080

0 commit comments

Comments
 (0)