From 9ec20d4612eef18660ed2cb29b962f23c15c355a Mon Sep 17 00:00:00 2001 From: Your Name <you@example.com> Date: Tue, 18 Jun 2024 17:24:10 +0200 Subject: [PATCH 01/18] feat: initial support for geometry column --- app/es_embedded/es/mappings.json | 3 +++ .../elasticsearch/PhotonDocConverter.java | 17 +++++++++++++++++ .../photon/opensearch/PhotonDocSerializer.java | 11 +++++++---- buildSrc/shared.gradle | 1 + src/main/java/de/komoot/photon/PhotonDoc.java | 16 +++++++++++++++- .../photon/nominatim/NominatimConnector.java | 3 ++- .../photon/searcher/GeocodeJsonFormatter.java | 8 +++++--- .../de/komoot/photon/searcher/PhotonResult.java | 1 + 8 files changed, 51 insertions(+), 9 deletions(-) diff --git a/app/es_embedded/es/mappings.json b/app/es_embedded/es/mappings.json index 7085a49c1..39c4f7563 100644 --- a/app/es_embedded/es/mappings.json +++ b/app/es_embedded/es/mappings.json @@ -49,6 +49,9 @@ "coordinate": { "type": "geo_point" }, + "geometry": { + "type": "geo_shape" + }, "country": { "properties": { "default": { diff --git a/app/es_embedded/src/main/java/de/komoot/photon/elasticsearch/PhotonDocConverter.java b/app/es_embedded/src/main/java/de/komoot/photon/elasticsearch/PhotonDocConverter.java index cc2688bd5..00d37a6e1 100644 --- a/app/es_embedded/src/main/java/de/komoot/photon/elasticsearch/PhotonDocConverter.java +++ b/app/es_embedded/src/main/java/de/komoot/photon/elasticsearch/PhotonDocConverter.java @@ -3,11 +3,17 @@ import de.komoot.photon.Constants; import de.komoot.photon.PhotonDoc; import de.komoot.photon.nominatim.model.AddressType; + +import org.elasticsearch.common.xcontent.NamedXContentRegistry; import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentFactory; +import org.elasticsearch.common.xcontent.XContentParser; +import org.elasticsearch.common.xcontent.json.JsonXContent; import org.locationtech.jts.geom.Envelope; +import org.locationtech.jts.io.geojson.GeoJsonWriter; import java.io.IOException; +import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.Map; @@ -38,6 +44,17 @@ public static XContentBuilder convert(PhotonDoc doc, String[] languages, String[ .endObject(); } + if (doc.getGeometry() != null) { + GeoJsonWriter g = new GeoJsonWriter(); + + XContentParser parser = JsonXContent + .jsonXContent + .createParser(NamedXContentRegistry.EMPTY, g.write(doc.getGeometry())); + + builder.field("geometry"); + builder.copyCurrentStructure(parser); + } + if (doc.getHouseNumber() != null) { builder.field("housenumber", doc.getHouseNumber()); } diff --git a/app/opensearch/src/main/java/de/komoot/photon/opensearch/PhotonDocSerializer.java b/app/opensearch/src/main/java/de/komoot/photon/opensearch/PhotonDocSerializer.java index 2ed731620..b0bd31f85 100644 --- a/app/opensearch/src/main/java/de/komoot/photon/opensearch/PhotonDocSerializer.java +++ b/app/opensearch/src/main/java/de/komoot/photon/opensearch/PhotonDocSerializer.java @@ -7,12 +7,10 @@ import de.komoot.photon.PhotonDoc; import de.komoot.photon.Utils; import org.locationtech.jts.geom.Envelope; +import org.locationtech.jts.io.geojson.GeoJsonWriter; import java.io.IOException; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; +import java.util.*; public class PhotonDocSerializer extends StdSerializer<PhotonDoc> { final private String[] languages; @@ -48,6 +46,11 @@ public void serialize(PhotonDoc value, JsonGenerator gen, SerializerProvider pro gen.writeEndObject(); } + if (value.getGeometry() != null) { + GeoJsonWriter g = new GeoJsonWriter(); + gen.writeObjectField("geometry", g.write(value.getGeometry())); + } + if (value.getHouseNumber() != null) { gen.writeStringField("housenumber", value.getHouseNumber()); } diff --git a/buildSrc/shared.gradle b/buildSrc/shared.gradle index f0b4dd6cf..8c98eb9d1 100644 --- a/buildSrc/shared.gradle +++ b/buildSrc/shared.gradle @@ -52,6 +52,7 @@ dependencies { exclude(module: 'commons-logging') } implementation 'org.locationtech.jts:jts-core:1.19.0' + implementation 'org.locationtech.jts.io:jts-io-common:1.19.0' implementation 'com.sparkjava:spark-core:2.9.4' implementation 'net.postgis:postgis-jdbc:2023.1.0' implementation 'org.json:json:20240303' diff --git a/src/main/java/de/komoot/photon/PhotonDoc.java b/src/main/java/de/komoot/photon/PhotonDoc.java index 719c42617..a0c7fbf08 100644 --- a/src/main/java/de/komoot/photon/PhotonDoc.java +++ b/src/main/java/de/komoot/photon/PhotonDoc.java @@ -1,9 +1,10 @@ -package de.komoot.photon; + package de.komoot.photon; import org.locationtech.jts.geom.Envelope; import org.locationtech.jts.geom.Geometry; import org.locationtech.jts.geom.Point; import de.komoot.photon.nominatim.model.AddressType; +import org.locationtech.jts.geom.Polygon; import org.slf4j.Logger; import java.util.*; @@ -34,6 +35,7 @@ public class PhotonDoc { private Set<Map<String, String>> context = new HashSet<>(); private String houseNumber = null; private Point centroid = null; + private Geometry geometry = null; public PhotonDoc(long placeId, String osmType, long osmId, String tagKey, String tagValue) { this.placeId = placeId; @@ -62,6 +64,7 @@ public PhotonDoc(PhotonDoc other) { this.rankAddress = other.rankAddress; this.addressParts = other.addressParts; this.context = other.context; + this.geometry = other.geometry; } public PhotonDoc names(Map<String, String> names) { @@ -86,6 +89,14 @@ public PhotonDoc centroid(Geometry centroid) { return this; } + public PhotonDoc geometry(Geometry polygon) { + // if (polygon.getGeometryType().equals("Polygon")) { + this.geometry = (Geometry) polygon; + // } + return this; + + } + public PhotonDoc countryCode(String countryCode) { if (countryCode != null) { this.countryCode = countryCode.toUpperCase(); @@ -312,4 +323,7 @@ public String getHouseNumber() { public Point getCentroid() { return this.centroid; } + public Geometry getGeometry() { + return this.geometry; + } } diff --git a/src/main/java/de/komoot/photon/nominatim/NominatimConnector.java b/src/main/java/de/komoot/photon/nominatim/NominatimConnector.java index eebe9ab37..5243687ca 100644 --- a/src/main/java/de/komoot/photon/nominatim/NominatimConnector.java +++ b/src/main/java/de/komoot/photon/nominatim/NominatimConnector.java @@ -24,7 +24,7 @@ public class NominatimConnector { private static final Logger LOGGER = org.slf4j.LoggerFactory.getLogger(NominatimConnector.class); - private static final String SELECT_COLS_PLACEX = "SELECT place_id, osm_type, osm_id, class, type, name, postcode, address, extratags, ST_Envelope(geometry) AS bbox, parent_place_id, linked_place_id, rank_address, rank_search, importance, country_code, centroid"; + private static final String SELECT_COLS_PLACEX = "SELECT place_id, osm_type, osm_id, class, type, name, postcode, address, extratags, ST_Envelope(geometry) AS bbox, parent_place_id, linked_place_id, rank_address, rank_search, importance, country_code, centroid, geometry"; private static final String SELECT_COLS_ADDRESS = "SELECT p.name, p.class, p.type, p.rank_address"; private final DBDataAdapter dbutils; @@ -58,6 +58,7 @@ public NominatimResult mapRow(ResultSet rs, int rowNum) throws SQLException { .parentPlaceId(rs.getLong("parent_place_id")) .countryCode(rs.getString("country_code")) .centroid(dbutils.extractGeometry(rs, "centroid")) + .geometry(dbutils.extractGeometry(rs, "geometry")) .linkedPlaceId(rs.getLong("linked_place_id")) .rankAddress(rs.getInt("rank_address")) .postcode(rs.getString("postcode")); diff --git a/src/main/java/de/komoot/photon/searcher/GeocodeJsonFormatter.java b/src/main/java/de/komoot/photon/searcher/GeocodeJsonFormatter.java index 8557c3d10..dc2d20bb1 100644 --- a/src/main/java/de/komoot/photon/searcher/GeocodeJsonFormatter.java +++ b/src/main/java/de/komoot/photon/searcher/GeocodeJsonFormatter.java @@ -31,9 +31,11 @@ public String convert(List<PhotonResult> results, String debugInfo) { features.put(new JSONObject() .put("type", "Feature") .put("properties", getResultProperties(result)) - .put("geometry", new JSONObject() - .put("type", "Point") - .put("coordinates", coordinates))); + .put("geometry", result.get("geometry"))); + + // .put("geometry", new JSONObject() + // .put("type", "Point") + // .put("coordinates", coordinates))); } final JSONObject out = new JSONObject(); diff --git a/src/main/java/de/komoot/photon/searcher/PhotonResult.java b/src/main/java/de/komoot/photon/searcher/PhotonResult.java index 946a9c0ba..f019b59bb 100644 --- a/src/main/java/de/komoot/photon/searcher/PhotonResult.java +++ b/src/main/java/de/komoot/photon/searcher/PhotonResult.java @@ -22,6 +22,7 @@ public interface PhotonResult { Map<String, String> getMap(String key); double[] getCoordinates(); + double[] getExtent(); double getScore(); From 5a7fb1a7eb170b70d5f3900273c8e745fd1f414f Mon Sep 17 00:00:00 2001 From: Your Name <you@example.com> Date: Tue, 9 Jul 2024 15:56:17 +0200 Subject: [PATCH 02/18] feat: add unit test --- src/main/java/de/komoot/photon/PhotonDoc.java | 5 +- .../searcher/GeocodeJsonFormatterTest.java | 48 ++++++++++++++++--- 2 files changed, 42 insertions(+), 11 deletions(-) diff --git a/src/main/java/de/komoot/photon/PhotonDoc.java b/src/main/java/de/komoot/photon/PhotonDoc.java index a0c7fbf08..17afeeda5 100644 --- a/src/main/java/de/komoot/photon/PhotonDoc.java +++ b/src/main/java/de/komoot/photon/PhotonDoc.java @@ -90,11 +90,8 @@ public PhotonDoc centroid(Geometry centroid) { } public PhotonDoc geometry(Geometry polygon) { - // if (polygon.getGeometryType().equals("Polygon")) { - this.geometry = (Geometry) polygon; - // } + this.geometry = (Geometry) polygon; return this; - } public PhotonDoc countryCode(String countryCode) { diff --git a/src/test/java/de/komoot/photon/searcher/GeocodeJsonFormatterTest.java b/src/test/java/de/komoot/photon/searcher/GeocodeJsonFormatterTest.java index eaa8e6333..2ea134a0f 100644 --- a/src/test/java/de/komoot/photon/searcher/GeocodeJsonFormatterTest.java +++ b/src/test/java/de/komoot/photon/searcher/GeocodeJsonFormatterTest.java @@ -15,11 +15,16 @@ class GeocodeJsonFormatterTest { @Test void testConvertToGeojson() { GeocodeJsonFormatter formatter = new GeocodeJsonFormatter(false, "en"); - List<PhotonResult> allResults = new ArrayList<>(); - allResults.add(createDummyResult("99999", "Park Foo", "leisure", "park")); - allResults.add(createDummyResult("88888", "Bar Park", "leisure", "park")); + List<PhotonResult> allPointResults = new ArrayList<>(); + allPointResults.add(createDummyPointResult("99999", "Park Foo", "leisure", "park")); + allPointResults.add(createDummyPointResult("88888", "Bar Park", "leisure", "park")); - String geojsonString = formatter.convert(allResults, null); + List<PhotonResult> allPolygonResults = new ArrayList<>(); + allPolygonResults.add(createDummyPolygonResult("99999", "Park Foo", "leisure", "park")); + allPolygonResults.add(createDummyPolygonResult("88888", "Bar Park", "leisure", "park")); + + // Test Points + String geojsonString = formatter.convert(allPointResults, null); JSONObject jsonObj = new JSONObject(geojsonString); assertEquals("FeatureCollection", jsonObj.getString("type")); JSONArray features = jsonObj.getJSONArray("features"); @@ -31,15 +36,44 @@ void testConvertToGeojson() { assertEquals("leisure", feature.getJSONObject("properties").getString(Constants.OSM_KEY)); assertEquals("park", feature.getJSONObject("properties").getString(Constants.OSM_VALUE)); } + + // Test Polygon + geojsonString = formatter.convert(allPolygonResults, null); + jsonObj = new JSONObject(geojsonString); + assertEquals("FeatureCollection", jsonObj.getString("type")); + features = jsonObj.getJSONArray("features"); + assertEquals(2, features.length()); + for (int i = 0; i < features.length(); i++) { + JSONObject feature = features.getJSONObject(i); + assertEquals("Feature", feature.getString("type")); + assertEquals("Polygon", feature.getJSONObject("geometry").getString("type")); + assertEquals("leisure", feature.getJSONObject("properties").getString(Constants.OSM_KEY)); + assertEquals("park", feature.getJSONObject("properties").getString(Constants.OSM_VALUE)); + } } - private PhotonResult createDummyResult(String postCode, String name, String osmKey, - String osmValue) { + private PhotonResult createDummyPointResult(String postCode, String name, String osmKey, + String osmValue) { + return new MockPhotonResult() + .put(Constants.POSTCODE, postCode) + .putLocalized(Constants.NAME, "en", name) + .put(Constants.OSM_KEY, osmKey) + .put(Constants.OSM_VALUE, osmValue) + .put("geometry", new JSONObject() + .put("type", "Point") + .put("coordinates", new double[]{42, 21})); + } + + private PhotonResult createDummyPolygonResult(String postCode, String name, String osmKey, + String osmValue) { return new MockPhotonResult() .put(Constants.POSTCODE, postCode) .putLocalized(Constants.NAME, "en", name) .put(Constants.OSM_KEY, osmKey) - .put(Constants.OSM_VALUE, osmValue); + .put(Constants.OSM_VALUE, osmValue) + .put("geometry", new JSONObject() + .put("type", "Polygon") + .put("coordinates", new double[][]{{100.0, 0.0}, {101.0, 0.0}, {101.0, 1.0}, {100.0, 1.0}, {100.0, 0.0}})); } } From 31e2111ac5a24258c310eb197003318db7e21c2f Mon Sep 17 00:00:00 2001 From: Your Name <you@example.com> Date: Thu, 11 Jul 2024 08:46:23 +0200 Subject: [PATCH 03/18] feat: add geometry column as an commanline argument --- src/main/java/de/komoot/photon/App.java | 12 +++++----- .../de/komoot/photon/CommandLineArgs.java | 7 ++++++ .../photon/ReverseSearchRequestHandler.java | 8 ++++--- .../photon/nominatim/NominatimConnector.java | 14 +++++++---- .../photon/nominatim/NominatimUpdater.java | 8 +++---- .../photon/query/ReverseRequestFactory.java | 2 +- .../photon/searcher/GeocodeJsonFormatter.java | 23 ++++++++++++------- .../nominatim/NominatimUpdaterDBTest.java | 2 +- .../query/ReverseRequestFactoryTest.java | 20 ++++++++-------- 9 files changed, 59 insertions(+), 37 deletions(-) diff --git a/src/main/java/de/komoot/photon/App.java b/src/main/java/de/komoot/photon/App.java index af7815be7..5325e61b3 100644 --- a/src/main/java/de/komoot/photon/App.java +++ b/src/main/java/de/komoot/photon/App.java @@ -101,7 +101,7 @@ private static void startJsonDump(CommandLineArgs args) { try { final String filename = args.getJsonDump(); final JsonDumper jsonDumper = new JsonDumper(filename, args.getLanguages(), args.getExtraTags()); - NominatimConnector nominatimConnector = new NominatimConnector(args.getHost(), args.getPort(), args.getDatabase(), args.getUser(), args.getPassword()); + NominatimConnector nominatimConnector = new NominatimConnector(args.getHost(), args.getPort(), args.getDatabase(), args.getUser(), args.getPassword(), args.isUseGeometryColumn()); nominatimConnector.setImporter(jsonDumper); nominatimConnector.readEntireDatabase(args.getCountryCodes()); LOGGER.info("Json dump was created: {}", filename); @@ -116,7 +116,7 @@ private static void startJsonDump(CommandLineArgs args) { */ private static void startNominatimImport(CommandLineArgs args, Server esServer) { DatabaseProperties dbProperties; - NominatimConnector nominatimConnector = new NominatimConnector(args.getHost(), args.getPort(), args.getDatabase(), args.getUser(), args.getPassword()); + NominatimConnector nominatimConnector = new NominatimConnector(args.getHost(), args.getPort(), args.getDatabase(), args.getUser(), args.getPassword(), args.isUseGeometryColumn()); Date importDate = nominatimConnector.getLastImportDate(); try { dbProperties = esServer.recreateIndex(args.getLanguages(), importDate); // clear out previous data @@ -132,7 +132,7 @@ private static void startNominatimImport(CommandLineArgs args, Server esServer) } private static void startNominatimUpdateInit(CommandLineArgs args) { - NominatimUpdater nominatimUpdater = new NominatimUpdater(args.getHost(), args.getPort(), args.getDatabase(), args.getUser(), args.getPassword()); + NominatimUpdater nominatimUpdater = new NominatimUpdater(args.getHost(), args.getPort(), args.getDatabase(), args.getUser(), args.getPassword(), args.isUseGeometryColumn()); nominatimUpdater.initUpdates(args.getNominatimUpdateInit()); } @@ -159,7 +159,7 @@ private static NominatimUpdater setupNominatimUpdater(CommandLineArgs args, Serv DatabaseProperties dbProperties = new DatabaseProperties(); server.loadFromDatabase(dbProperties); - NominatimUpdater nominatimUpdater = new NominatimUpdater(args.getHost(), args.getPort(), args.getDatabase(), args.getUser(), args.getPassword()); + NominatimUpdater nominatimUpdater = new NominatimUpdater(args.getHost(), args.getPort(), args.getDatabase(), args.getUser(), args.getPassword(), args.isUseGeometryColumn()); nominatimUpdater.setUpdater(server.createUpdater(dbProperties.getLanguages(), args.getExtraTags())); return nominatimUpdater; } @@ -195,9 +195,9 @@ private static void startApi(CommandLineArgs args, Server server) throws IOExcep ReverseHandler reverseHandler = server.createReverseHandler(args.getQueryTimeout()); get("reverse", new ReverseSearchRequestHandler("reverse", reverseHandler, dbProperties.getLanguages(), - args.getDefaultLanguage(), args.getMaxReverseResults())); + args.getDefaultLanguage(), args.getMaxReverseResults(), args.isUseGeometryColumn())); get("reverse/", new ReverseSearchRequestHandler("reverse/", reverseHandler, dbProperties.getLanguages(), - args.getDefaultLanguage(), args.getMaxReverseResults())); + args.getDefaultLanguage(), args.getMaxReverseResults(), args.isUseGeometryColumn())); get("status", new StatusRequestHandler("status", server)); get("status/", new StatusRequestHandler("status/", server)); diff --git a/src/main/java/de/komoot/photon/CommandLineArgs.java b/src/main/java/de/komoot/photon/CommandLineArgs.java index 3a05d46a1..24e84f48f 100644 --- a/src/main/java/de/komoot/photon/CommandLineArgs.java +++ b/src/main/java/de/komoot/photon/CommandLineArgs.java @@ -88,6 +88,9 @@ public class CommandLineArgs { @Parameter(names = "-max-reverse-results", description = "The maximum possible 'limit' parameter for reverse geocoding searches") private int maxReverseResults = 50; + @Parameter(names = "-use-geometry-column", description = "Use the 'geometry' column from Nominatim instead of the 'centroid' column on import and serving (i.e. get Polygons for cities). WARNING: This will increase the Elasticsearch Index size! (~575GB for Planet)") + private boolean useGeometryColumn = false; + public String[] getLanguages(boolean useDefaultIfEmpty) { if (useDefaultIfEmpty && languages.length == 0) { return new String[]{"en", "de", "fr", "it"}; @@ -199,5 +202,9 @@ public int getMaxReverseResults() { public int getMaxResults() { return maxResults; } + + public boolean isUseGeometryColumn() { + return useGeometryColumn; + } } diff --git a/src/main/java/de/komoot/photon/ReverseSearchRequestHandler.java b/src/main/java/de/komoot/photon/ReverseSearchRequestHandler.java index e34bb61fe..066e31b9d 100644 --- a/src/main/java/de/komoot/photon/ReverseSearchRequestHandler.java +++ b/src/main/java/de/komoot/photon/ReverseSearchRequestHandler.java @@ -22,11 +22,13 @@ public class ReverseSearchRequestHandler extends RouteImpl { private final ReverseRequestFactory reverseRequestFactory; private final ReverseHandler requestHandler; + private final boolean useGeometryColumn; - ReverseSearchRequestHandler(String path, ReverseHandler dbHandler, String[] languages, String defaultLanguage, int maxResults) { + ReverseSearchRequestHandler(String path, ReverseHandler dbHandler, String[] languages, String defaultLanguage, int maxResults, boolean useGeometryColumn) { super(path); + this.useGeometryColumn = useGeometryColumn; List<String> supportedLanguages = Arrays.asList(languages); - this.reverseRequestFactory = new ReverseRequestFactory(supportedLanguages, defaultLanguage, maxResults); + this.reverseRequestFactory = new ReverseRequestFactory(supportedLanguages, defaultLanguage, maxResults, useGeometryColumn); this.requestHandler = dbHandler; } @@ -53,6 +55,6 @@ public String handle(Request request, Response response) { debugInfo = requestHandler.dumpQuery(photonRequest); } - return new GeocodeJsonFormatter(false, photonRequest.getLanguage()).convert(results, debugInfo); + return new GeocodeJsonFormatter(false, photonRequest.getLanguage(), useGeometryColumn).convert(results, debugInfo); } } diff --git a/src/main/java/de/komoot/photon/nominatim/NominatimConnector.java b/src/main/java/de/komoot/photon/nominatim/NominatimConnector.java index 5243687ca..acfa9de32 100644 --- a/src/main/java/de/komoot/photon/nominatim/NominatimConnector.java +++ b/src/main/java/de/komoot/photon/nominatim/NominatimConnector.java @@ -40,6 +40,7 @@ public class NominatimConnector { private final String selectOsmlineSql; private Importer importer; + private final boolean useGeometryColumn; /** * Maps a placex row in nominatim to a photon doc. @@ -58,11 +59,14 @@ public NominatimResult mapRow(ResultSet rs, int rowNum) throws SQLException { .parentPlaceId(rs.getLong("parent_place_id")) .countryCode(rs.getString("country_code")) .centroid(dbutils.extractGeometry(rs, "centroid")) - .geometry(dbutils.extractGeometry(rs, "geometry")) .linkedPlaceId(rs.getLong("linked_place_id")) .rankAddress(rs.getInt("rank_address")) .postcode(rs.getString("postcode")); + if (useGeometryColumn) { + doc.geometry(dbutils.extractGeometry(rs, "geometry")); + } + double importance = rs.getDouble("importance"); doc.importance(rs.wasNull() ? (0.75 - rs.getInt("rank_search") / 40d) : importance); @@ -88,11 +92,11 @@ public NominatimResult mapRow(ResultSet rs, int rowNum) throws SQLException { * @param username db username * @param password db username's password */ - public NominatimConnector(String host, int port, String database, String username, String password) { - this(host, port, database, username, password, new PostgisDataAdapter()); + public NominatimConnector(String host, int port, String database, String username, String password, boolean useGeometryColumn) { + this(host, port, database, username, password, new PostgisDataAdapter(), useGeometryColumn); } - public NominatimConnector(String host, int port, String database, String username, String password, DBDataAdapter dataAdapter) { + public NominatimConnector(String host, int port, String database, String username, String password, DBDataAdapter dataAdapter, boolean useGeometryColumn) { BasicDataSource dataSource = buildDataSource(host, port, database, username, password, false); template = new JdbcTemplate(dataSource); @@ -100,6 +104,8 @@ public NominatimConnector(String host, int port, String database, String usernam dbutils = dataAdapter; + this.useGeometryColumn = useGeometryColumn; + // Setup handling of interpolation table. There are two different formats depending on the Nominatim version. if (dbutils.hasColumn(template, "location_property_osmline", "step")) { // new-style interpolations diff --git a/src/main/java/de/komoot/photon/nominatim/NominatimUpdater.java b/src/main/java/de/komoot/photon/nominatim/NominatimUpdater.java index 3e97ab0ea..417ab6d2b 100644 --- a/src/main/java/de/komoot/photon/nominatim/NominatimUpdater.java +++ b/src/main/java/de/komoot/photon/nominatim/NominatimUpdater.java @@ -199,14 +199,14 @@ private List<UpdateRow> getPlaces(String table) { * @param username Nominatim database username * @param password Nominatim database password */ - public NominatimUpdater(String host, int port, String database, String username, String password, DBDataAdapter dataAdapter) { + public NominatimUpdater(String host, int port, String database, String username, String password, DBDataAdapter dataAdapter, boolean useGeometryColumn) { BasicDataSource dataSource = NominatimConnector.buildDataSource(host, port, database, username, password, true); - exporter = new NominatimConnector(host, port, database, username, password, dataAdapter); + exporter = new NominatimConnector(host, port, database, username, password, dataAdapter, useGeometryColumn); template = new JdbcTemplate(dataSource); } - public NominatimUpdater(String host, int port, String database, String username, String password) { - this(host, port, database, username, password, new PostgisDataAdapter()); + public NominatimUpdater(String host, int port, String database, String username, String password, boolean useGeometryColumn) { + this(host, port, database, username, password, new PostgisDataAdapter(), useGeometryColumn); } } diff --git a/src/main/java/de/komoot/photon/query/ReverseRequestFactory.java b/src/main/java/de/komoot/photon/query/ReverseRequestFactory.java index e539fe127..fe65b162d 100644 --- a/src/main/java/de/komoot/photon/query/ReverseRequestFactory.java +++ b/src/main/java/de/komoot/photon/query/ReverseRequestFactory.java @@ -23,7 +23,7 @@ public class ReverseRequestFactory { private final LayerParamValidator layerParamValidator; private final int maxResults; - public ReverseRequestFactory(List<String> supportedLanguages, String defaultLanguage, int maxResults) { + public ReverseRequestFactory(List<String> supportedLanguages, String defaultLanguage, int maxResults, boolean useGeometryColumn) { this.languageResolver = new RequestLanguageResolver(supportedLanguages, defaultLanguage); this.layerParamValidator = new LayerParamValidator(); this.maxResults = maxResults; diff --git a/src/main/java/de/komoot/photon/searcher/GeocodeJsonFormatter.java b/src/main/java/de/komoot/photon/searcher/GeocodeJsonFormatter.java index dc2d20bb1..1cc8ee722 100644 --- a/src/main/java/de/komoot/photon/searcher/GeocodeJsonFormatter.java +++ b/src/main/java/de/komoot/photon/searcher/GeocodeJsonFormatter.java @@ -15,10 +15,12 @@ public class GeocodeJsonFormatter implements ResultFormatter { private final boolean addDebugInfo; private final String language; + private final boolean useGeometryColumn; - public GeocodeJsonFormatter(boolean addDebugInfo, String language) { + public GeocodeJsonFormatter(boolean addDebugInfo, String language, boolean useGeometryColumn) { this.addDebugInfo = addDebugInfo; this.language = language; + this.useGeometryColumn = useGeometryColumn; } @Override @@ -26,16 +28,21 @@ public String convert(List<PhotonResult> results, String debugInfo) { final JSONArray features = new JSONArray(results.size()); for (PhotonResult result : results) { - final double[] coordinates = result.getCoordinates(); - - features.put(new JSONObject() + if (useGeometryColumn) { + features.put(new JSONObject() .put("type", "Feature") .put("properties", getResultProperties(result)) .put("geometry", result.get("geometry"))); - - // .put("geometry", new JSONObject() - // .put("type", "Point") - // .put("coordinates", coordinates))); + } else { + final double[] coordinates = result.getCoordinates(); + + features.put(new JSONObject() + .put("type", "Feature") + .put("properties", getResultProperties(result)) + .put("geometry", new JSONObject() + .put("type", "Point") + .put("coordinates", coordinates))); + } } final JSONObject out = new JSONObject(); diff --git a/src/test/java/de/komoot/photon/nominatim/NominatimUpdaterDBTest.java b/src/test/java/de/komoot/photon/nominatim/NominatimUpdaterDBTest.java index 23d55f014..1d5bdee3c 100644 --- a/src/test/java/de/komoot/photon/nominatim/NominatimUpdaterDBTest.java +++ b/src/test/java/de/komoot/photon/nominatim/NominatimUpdaterDBTest.java @@ -26,7 +26,7 @@ void setup() { .build(); - connector = new NominatimUpdater(null, 0, null, null, null, new H2DataAdapter()); + connector = new NominatimUpdater(null, 0, null, null, null, new H2DataAdapter(), false); updater = new CollectingUpdater(); connector.setUpdater(updater); diff --git a/src/test/java/de/komoot/photon/query/ReverseRequestFactoryTest.java b/src/test/java/de/komoot/photon/query/ReverseRequestFactoryTest.java index 1e302e0f6..5683836e8 100644 --- a/src/test/java/de/komoot/photon/query/ReverseRequestFactoryTest.java +++ b/src/test/java/de/komoot/photon/query/ReverseRequestFactoryTest.java @@ -56,7 +56,7 @@ private void requestWithLayers(Request mockRequest, String... layers) { @Test void testWithLocation() throws Exception { Request mockRequest = createRequestWithLongitudeLatitude(-87d, 41d); - ReverseRequestFactory reverseRequestFactory = new ReverseRequestFactory(Collections.singletonList("en"), "en", 10); + ReverseRequestFactory reverseRequestFactory = new ReverseRequestFactory(Collections.singletonList("en"), "en", 10, false); reverseRequest = reverseRequestFactory.create(mockRequest); assertEquals(-87, reverseRequest.getLocation().getX(), 0); assertEquals(41, reverseRequest.getLocation().getY(), 0); @@ -66,7 +66,7 @@ void testWithLocation() throws Exception { private void assertBadRequest(Request mockRequest, String expectedMessage) { try { - ReverseRequestFactory reverseRequestFactory = new ReverseRequestFactory(Collections.singletonList("en"), "en", 10); + ReverseRequestFactory reverseRequestFactory = new ReverseRequestFactory(Collections.singletonList("en"), "en", 10, false); reverseRequest = reverseRequestFactory.create(mockRequest); fail(); } catch (BadRequestException e) { @@ -155,7 +155,7 @@ void testWithBadRadius() throws Exception { void testHighRadius() throws Exception { Request mockRequest = createRequestWithLongitudeLatitude(-87d, 41d); Mockito.when(mockRequest.queryParams("radius")).thenReturn("5.1"); - ReverseRequestFactory reverseRequestFactory = new ReverseRequestFactory(Collections.singletonList("en"), "en", 10); + ReverseRequestFactory reverseRequestFactory = new ReverseRequestFactory(Collections.singletonList("en"), "en", 10, false); reverseRequest = reverseRequestFactory.create(mockRequest); assertEquals(5.1d, reverseRequest.getRadius(), 0); Mockito.verify(mockRequest, Mockito.times(1)).queryParams("radius"); @@ -180,7 +180,7 @@ void testWithBadLimit() throws Exception { void testHighLimit() throws Exception { Request mockRequest = createRequestWithLongitudeLatitude(-87d, 41d); Mockito.when(mockRequest.queryParams("limit")).thenReturn("51"); - ReverseRequestFactory reverseRequestFactory = new ReverseRequestFactory(Collections.singletonList("en"), "en", 50); + ReverseRequestFactory reverseRequestFactory = new ReverseRequestFactory(Collections.singletonList("en"), "en", 50, false); reverseRequest = reverseRequestFactory.create(mockRequest); assertEquals(50, reverseRequest.getLimit()); Mockito.verify(mockRequest, Mockito.times(1)).queryParams("limit"); @@ -189,7 +189,7 @@ void testHighLimit() throws Exception { @Test void testDistanceSortDefault() throws Exception { Request mockRequest = createRequestWithLongitudeLatitude(-87d, 41d); - ReverseRequestFactory reverseRequestFactory = new ReverseRequestFactory(Collections.singletonList("en"), "en", 10); + ReverseRequestFactory reverseRequestFactory = new ReverseRequestFactory(Collections.singletonList("en"), "en", 10, false); reverseRequest = reverseRequestFactory.create(mockRequest); Mockito.verify(mockRequest, Mockito.times(1)).queryParamOrDefault("distance_sort", "true"); assertEquals(true, reverseRequest.getLocationDistanceSort()); @@ -199,7 +199,7 @@ void testDistanceSortDefault() throws Exception { void testWithLayersFilters() throws Exception { Request mockRequest = createRequestWithLongitudeLatitude(-87d, 41d); requestWithLayers(mockRequest, "city", "locality"); - ReverseRequestFactory reverseRequestFactory = new ReverseRequestFactory(Collections.singletonList("en"), "en", 10); + ReverseRequestFactory reverseRequestFactory = new ReverseRequestFactory(Collections.singletonList("en"), "en", 10, false); reverseRequest = reverseRequestFactory.create(mockRequest); assertEquals(new HashSet<>(Arrays.asList("city", "locality")), reverseRequest.getLayerFilters()); } @@ -208,7 +208,7 @@ void testWithLayersFilters() throws Exception { void testWithDuplicatedLayerFilters() throws Exception { Request mockRequest = createRequestWithLongitudeLatitude(-87d, 41d); requestWithLayers(mockRequest, "city", "locality", "city"); - ReverseRequestFactory reverseRequestFactory = new ReverseRequestFactory(Collections.singletonList("en"), "en", 10); + ReverseRequestFactory reverseRequestFactory = new ReverseRequestFactory(Collections.singletonList("en"), "en", 10, false); reverseRequest = reverseRequestFactory.create(mockRequest); assertEquals(new HashSet<>(Arrays.asList("city", "locality")), reverseRequest.getLayerFilters()); } @@ -225,7 +225,7 @@ void testWithBadLayerFilters() { void testTagFilters() throws Exception { Request mockRequest = createRequestWithLongitudeLatitude(-87d, 41d); requestWithOsmFilters(mockRequest, "foo", ":!bar"); - ReverseRequestFactory reverseRequestFactory = new ReverseRequestFactory(Collections.singletonList("en"), "en", 10); + ReverseRequestFactory reverseRequestFactory = new ReverseRequestFactory(Collections.singletonList("en"), "en", 10, false); reverseRequest = reverseRequestFactory.create(mockRequest); List<TagFilter> result = reverseRequest.getOsmTagFilters(); @@ -242,7 +242,7 @@ void testTagFilters() throws Exception { void testBadTagFilters() { Request mockRequest = createRequestWithLongitudeLatitude(-87d, 41d); requestWithOsmFilters(mockRequest, "good", "bad:bad:bad"); - ReverseRequestFactory reverseRequestFactory = new ReverseRequestFactory(Collections.singletonList("en"), "en", 10); + ReverseRequestFactory reverseRequestFactory = new ReverseRequestFactory(Collections.singletonList("en"), "en", 10, false); assertThrows(BadRequestException.class, () -> reverseRequestFactory.create(mockRequest)); } @@ -252,7 +252,7 @@ void testWithDebug() throws Exception { Request mockRequest = createRequestWithLongitudeLatitude(-87d, 41d); Mockito.when(mockRequest.queryParams("debug")).thenReturn("1"); - ReverseRequestFactory reverseRequestFactory = new ReverseRequestFactory(Collections.singletonList("en"), "en", 10); + ReverseRequestFactory reverseRequestFactory = new ReverseRequestFactory(Collections.singletonList("en"), "en", 10, false); reverseRequest = reverseRequestFactory.create(mockRequest); assertTrue(reverseRequest.getDebug()); From 031d96263e59f0b59172d3c5c0b95c568d2822c7 Mon Sep 17 00:00:00 2001 From: Your Name <you@example.com> Date: Thu, 11 Jul 2024 08:54:47 +0200 Subject: [PATCH 04/18] bugfix: undo intellij magic --- .../de/komoot/photon/opensearch/PhotonDocSerializer.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/opensearch/src/main/java/de/komoot/photon/opensearch/PhotonDocSerializer.java b/app/opensearch/src/main/java/de/komoot/photon/opensearch/PhotonDocSerializer.java index b0bd31f85..936a6e9a5 100644 --- a/app/opensearch/src/main/java/de/komoot/photon/opensearch/PhotonDocSerializer.java +++ b/app/opensearch/src/main/java/de/komoot/photon/opensearch/PhotonDocSerializer.java @@ -10,7 +10,10 @@ import org.locationtech.jts.io.geojson.GeoJsonWriter; import java.io.IOException; -import java.util.*; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; public class PhotonDocSerializer extends StdSerializer<PhotonDoc> { final private String[] languages; From 152c2d1fb30f8288243e6df6ca649e4b00b2a6d4 Mon Sep 17 00:00:00 2001 From: Your Name <you@example.com> Date: Thu, 11 Jul 2024 09:08:47 +0200 Subject: [PATCH 05/18] bugfix: minor build bugs --- src/main/java/de/komoot/photon/App.java | 8 +++---- .../komoot/photon/SearchRequestHandler.java | 6 +++-- .../StructuredSearchRequestHandler.java | 6 +++-- .../nominatim/NominatimConnectorDBTest.java | 2 +- .../searcher/GeocodeJsonFormatterTest.java | 23 +++++++++++-------- 5 files changed, 27 insertions(+), 18 deletions(-) diff --git a/src/main/java/de/komoot/photon/App.java b/src/main/java/de/komoot/photon/App.java index 2792a6d21..640893738 100644 --- a/src/main/java/de/komoot/photon/App.java +++ b/src/main/java/de/komoot/photon/App.java @@ -191,13 +191,13 @@ private static void startApi(CommandLineArgs args, Server server) throws IOExcep String[] langs = dbProperties.getLanguages(); SearchHandler searchHandler = server.createSearchHandler(langs, args.getQueryTimeout()); - get("api", new SearchRequestHandler("api", searchHandler, langs, args.getDefaultLanguage(), args.getMaxResults())); - get("api/", new SearchRequestHandler("api/", searchHandler, langs, args.getDefaultLanguage(), args.getMaxResults())); + get("api", new SearchRequestHandler("api", searchHandler, langs, args.getDefaultLanguage(), args.getMaxResults(), args.isUseGeometryColumn())); + get("api/", new SearchRequestHandler("api/", searchHandler, langs, args.getDefaultLanguage(), args.getMaxResults(), args.isUseGeometryColumn())); if (dbProperties.getSupportStructuredQueries()) { StructuredSearchHandler structured = server.createStructuredSearchHandler(langs, args.getQueryTimeout()); - get("structured", new StructuredSearchRequestHandler("structured", structured, langs, args.getDefaultLanguage(), args.getMaxResults())); - get("structured/", new StructuredSearchRequestHandler("structured/", structured, langs, args.getDefaultLanguage(), args.getMaxResults())); + get("structured", new StructuredSearchRequestHandler("structured", structured, langs, args.getDefaultLanguage(), args.getMaxResults(), args.isUseGeometryColumn())); + get("structured/", new StructuredSearchRequestHandler("structured/", structured, langs, args.getDefaultLanguage(), args.getMaxResults(), args.isUseGeometryColumn())); } ReverseHandler reverseHandler = server.createReverseHandler(args.getQueryTimeout()); diff --git a/src/main/java/de/komoot/photon/SearchRequestHandler.java b/src/main/java/de/komoot/photon/SearchRequestHandler.java index 5f11578dd..6391a0978 100644 --- a/src/main/java/de/komoot/photon/SearchRequestHandler.java +++ b/src/main/java/de/komoot/photon/SearchRequestHandler.java @@ -21,10 +21,12 @@ public class SearchRequestHandler extends RouteImpl { private final PhotonRequestFactory photonRequestFactory; private final SearchHandler requestHandler; private final int maxResults; + private final boolean useGeometryColumn; - SearchRequestHandler(String path, SearchHandler dbHandler, String[] languages, String defaultLanguage, int maxResults) { + SearchRequestHandler(String path, SearchHandler dbHandler, String[] languages, String defaultLanguage, int maxResults, boolean useGeometryColumn) { super(path); List<String> supportedLanguages = Arrays.asList(languages); + this.useGeometryColumn = useGeometryColumn; this.photonRequestFactory = new PhotonRequestFactory(supportedLanguages, defaultLanguage, maxResults); this.requestHandler = dbHandler; this.maxResults = maxResults; @@ -56,6 +58,6 @@ public String handle(Request request, Response response) { debugInfo = requestHandler.dumpQuery(photonRequest); } - return new GeocodeJsonFormatter(photonRequest.getDebug(), photonRequest.getLanguage()).convert(results, debugInfo); + return new GeocodeJsonFormatter(photonRequest.getDebug(), photonRequest.getLanguage(), useGeometryColumn).convert(results, debugInfo); } } \ No newline at end of file diff --git a/src/main/java/de/komoot/photon/StructuredSearchRequestHandler.java b/src/main/java/de/komoot/photon/StructuredSearchRequestHandler.java index 805c25560..cef36329d 100644 --- a/src/main/java/de/komoot/photon/StructuredSearchRequestHandler.java +++ b/src/main/java/de/komoot/photon/StructuredSearchRequestHandler.java @@ -17,10 +17,12 @@ public class StructuredSearchRequestHandler extends RouteImpl { private final PhotonRequestFactory photonRequestFactory; private final StructuredSearchHandler requestHandler; + private final boolean useGeometryColumn; - StructuredSearchRequestHandler(String path, StructuredSearchHandler dbHandler, String[] languages, String defaultLanguage, int maxResults) { + StructuredSearchRequestHandler(String path, StructuredSearchHandler dbHandler, String[] languages, String defaultLanguage, int maxResults, boolean useGeometryColumn) { super(path); List<String> supportedLanguages = Arrays.asList(languages); + this.useGeometryColumn = useGeometryColumn; this.photonRequestFactory = new PhotonRequestFactory(supportedLanguages, defaultLanguage, maxResults); this.requestHandler = dbHandler; } @@ -51,6 +53,6 @@ public String handle(Request request, Response response) { debugInfo = requestHandler.dumpQuery(photonRequest); } */ - return new GeocodeJsonFormatter(photonRequest.getDebug(), photonRequest.getLanguage()).convert(results, debugInfo); + return new GeocodeJsonFormatter(photonRequest.getDebug(), photonRequest.getLanguage(), useGeometryColumn).convert(results, debugInfo); } } \ No newline at end of file diff --git a/src/test/java/de/komoot/photon/nominatim/NominatimConnectorDBTest.java b/src/test/java/de/komoot/photon/nominatim/NominatimConnectorDBTest.java index 37c793931..5c75580f9 100644 --- a/src/test/java/de/komoot/photon/nominatim/NominatimConnectorDBTest.java +++ b/src/test/java/de/komoot/photon/nominatim/NominatimConnectorDBTest.java @@ -35,7 +35,7 @@ void setup() { .build(); - connector = new NominatimConnector(null, 0, null, null, null, new H2DataAdapter()); + connector = new NominatimConnector(null, 0, null, null, null, new H2DataAdapter(), false); importer = new CollectingImporter(); connector.setImporter(importer); diff --git a/src/test/java/de/komoot/photon/searcher/GeocodeJsonFormatterTest.java b/src/test/java/de/komoot/photon/searcher/GeocodeJsonFormatterTest.java index 2ea134a0f..c311c15e8 100644 --- a/src/test/java/de/komoot/photon/searcher/GeocodeJsonFormatterTest.java +++ b/src/test/java/de/komoot/photon/searcher/GeocodeJsonFormatterTest.java @@ -13,16 +13,12 @@ class GeocodeJsonFormatterTest { @Test - void testConvertToGeojson() { - GeocodeJsonFormatter formatter = new GeocodeJsonFormatter(false, "en"); + void testConvertPointToGeojson() { + GeocodeJsonFormatter formatter = new GeocodeJsonFormatter(false, "en", false); List<PhotonResult> allPointResults = new ArrayList<>(); allPointResults.add(createDummyPointResult("99999", "Park Foo", "leisure", "park")); allPointResults.add(createDummyPointResult("88888", "Bar Park", "leisure", "park")); - List<PhotonResult> allPolygonResults = new ArrayList<>(); - allPolygonResults.add(createDummyPolygonResult("99999", "Park Foo", "leisure", "park")); - allPolygonResults.add(createDummyPolygonResult("88888", "Bar Park", "leisure", "park")); - // Test Points String geojsonString = formatter.convert(allPointResults, null); JSONObject jsonObj = new JSONObject(geojsonString); @@ -36,12 +32,21 @@ void testConvertToGeojson() { assertEquals("leisure", feature.getJSONObject("properties").getString(Constants.OSM_KEY)); assertEquals("park", feature.getJSONObject("properties").getString(Constants.OSM_VALUE)); } + } + + @Test + void testConvertPolygonToGeojson() { + GeocodeJsonFormatter formatter = new GeocodeJsonFormatter(false, "en", true); + + List<PhotonResult> allPolygonResults = new ArrayList<>(); + allPolygonResults.add(createDummyPolygonResult("99999", "Park Foo", "leisure", "park")); + allPolygonResults.add(createDummyPolygonResult("88888", "Bar Park", "leisure", "park")); // Test Polygon - geojsonString = formatter.convert(allPolygonResults, null); - jsonObj = new JSONObject(geojsonString); + String geojsonString = formatter.convert(allPolygonResults, null); + JSONObject jsonObj = new JSONObject(geojsonString); assertEquals("FeatureCollection", jsonObj.getString("type")); - features = jsonObj.getJSONArray("features"); + JSONArray features = jsonObj.getJSONArray("features"); assertEquals(2, features.length()); for (int i = 0; i < features.length(); i++) { JSONObject feature = features.getJSONObject(i); From 5d1b854b58667b84fc2c22088a84b6c9459187a6 Mon Sep 17 00:00:00 2001 From: Your Name <you@example.com> Date: Thu, 11 Jul 2024 10:16:56 +0200 Subject: [PATCH 06/18] feat: add polygon as an api request instead of a default --- src/main/java/de/komoot/photon/App.java | 12 +++++------ .../de/komoot/photon/CommandLineArgs.java | 2 +- .../photon/ReverseSearchRequestHandler.java | 8 +++----- .../komoot/photon/SearchRequestHandler.java | 6 ++---- .../StructuredSearchRequestHandler.java | 6 ++---- .../photon/query/PhotonRequestBase.java | 7 +++++++ .../photon/query/PhotonRequestFactory.java | 5 ++++- .../komoot/photon/query/ReverseRequest.java | 8 +++++++- .../photon/query/ReverseRequestFactory.java | 6 +++--- .../photon/searcher/GeocodeJsonFormatter.java | 2 +- .../query/QueryReverseFilterLayerTest.java | 2 +- .../query/QueryReverseFilterTagValueTest.java | 2 +- .../komoot/photon/query/QueryReverseTest.java | 2 +- .../query/ReverseRequestFactoryTest.java | 20 +++++++++---------- 14 files changed, 49 insertions(+), 39 deletions(-) diff --git a/src/main/java/de/komoot/photon/App.java b/src/main/java/de/komoot/photon/App.java index 640893738..a823110bb 100644 --- a/src/main/java/de/komoot/photon/App.java +++ b/src/main/java/de/komoot/photon/App.java @@ -191,20 +191,20 @@ private static void startApi(CommandLineArgs args, Server server) throws IOExcep String[] langs = dbProperties.getLanguages(); SearchHandler searchHandler = server.createSearchHandler(langs, args.getQueryTimeout()); - get("api", new SearchRequestHandler("api", searchHandler, langs, args.getDefaultLanguage(), args.getMaxResults(), args.isUseGeometryColumn())); - get("api/", new SearchRequestHandler("api/", searchHandler, langs, args.getDefaultLanguage(), args.getMaxResults(), args.isUseGeometryColumn())); + get("api", new SearchRequestHandler("api", searchHandler, langs, args.getDefaultLanguage(), args.getMaxResults())); + get("api/", new SearchRequestHandler("api/", searchHandler, langs, args.getDefaultLanguage(), args.getMaxResults())); if (dbProperties.getSupportStructuredQueries()) { StructuredSearchHandler structured = server.createStructuredSearchHandler(langs, args.getQueryTimeout()); - get("structured", new StructuredSearchRequestHandler("structured", structured, langs, args.getDefaultLanguage(), args.getMaxResults(), args.isUseGeometryColumn())); - get("structured/", new StructuredSearchRequestHandler("structured/", structured, langs, args.getDefaultLanguage(), args.getMaxResults(), args.isUseGeometryColumn())); + get("structured", new StructuredSearchRequestHandler("structured", structured, langs, args.getDefaultLanguage(), args.getMaxResults())); + get("structured/", new StructuredSearchRequestHandler("structured/", structured, langs, args.getDefaultLanguage(), args.getMaxResults())); } ReverseHandler reverseHandler = server.createReverseHandler(args.getQueryTimeout()); get("reverse", new ReverseSearchRequestHandler("reverse", reverseHandler, dbProperties.getLanguages(), - args.getDefaultLanguage(), args.getMaxReverseResults(), args.isUseGeometryColumn())); + args.getDefaultLanguage(), args.getMaxReverseResults())); get("reverse/", new ReverseSearchRequestHandler("reverse/", reverseHandler, dbProperties.getLanguages(), - args.getDefaultLanguage(), args.getMaxReverseResults(), args.isUseGeometryColumn())); + args.getDefaultLanguage(), args.getMaxReverseResults())); get("status", new StatusRequestHandler("status", server)); get("status/", new StatusRequestHandler("status/", server)); diff --git a/src/main/java/de/komoot/photon/CommandLineArgs.java b/src/main/java/de/komoot/photon/CommandLineArgs.java index 5d8520d8b..b4bb93324 100644 --- a/src/main/java/de/komoot/photon/CommandLineArgs.java +++ b/src/main/java/de/komoot/photon/CommandLineArgs.java @@ -91,7 +91,7 @@ public class CommandLineArgs { @Parameter(names = "-max-reverse-results", description = "The maximum possible 'limit' parameter for reverse geocoding searches") private int maxReverseResults = 50; - @Parameter(names = "-use-geometry-column", description = "Use the 'geometry' column from Nominatim instead of the 'centroid' column on import and serving (i.e. get Polygons for cities). WARNING: This will increase the Elasticsearch Index size! (~575GB for Planet)") + @Parameter(names = "-use-geometry-column", description = "[import-only] Add the 'geometry' column from Nominatim on import (i.e. add Polygons for cities). WARNING: This will increase the Elasticsearch Index size! (~575GB for Planet)") private boolean useGeometryColumn = false; public String[] getLanguages(boolean useDefaultIfEmpty) { diff --git a/src/main/java/de/komoot/photon/ReverseSearchRequestHandler.java b/src/main/java/de/komoot/photon/ReverseSearchRequestHandler.java index 066e31b9d..3416bd25b 100644 --- a/src/main/java/de/komoot/photon/ReverseSearchRequestHandler.java +++ b/src/main/java/de/komoot/photon/ReverseSearchRequestHandler.java @@ -22,13 +22,11 @@ public class ReverseSearchRequestHandler extends RouteImpl { private final ReverseRequestFactory reverseRequestFactory; private final ReverseHandler requestHandler; - private final boolean useGeometryColumn; - ReverseSearchRequestHandler(String path, ReverseHandler dbHandler, String[] languages, String defaultLanguage, int maxResults, boolean useGeometryColumn) { + ReverseSearchRequestHandler(String path, ReverseHandler dbHandler, String[] languages, String defaultLanguage, int maxResults) { super(path); - this.useGeometryColumn = useGeometryColumn; List<String> supportedLanguages = Arrays.asList(languages); - this.reverseRequestFactory = new ReverseRequestFactory(supportedLanguages, defaultLanguage, maxResults, useGeometryColumn); + this.reverseRequestFactory = new ReverseRequestFactory(supportedLanguages, defaultLanguage, maxResults); this.requestHandler = dbHandler; } @@ -55,6 +53,6 @@ public String handle(Request request, Response response) { debugInfo = requestHandler.dumpQuery(photonRequest); } - return new GeocodeJsonFormatter(false, photonRequest.getLanguage(), useGeometryColumn).convert(results, debugInfo); + return new GeocodeJsonFormatter(false, photonRequest.getLanguage(), photonRequest.getPolygon()).convert(results, debugInfo); } } diff --git a/src/main/java/de/komoot/photon/SearchRequestHandler.java b/src/main/java/de/komoot/photon/SearchRequestHandler.java index 6391a0978..909029245 100644 --- a/src/main/java/de/komoot/photon/SearchRequestHandler.java +++ b/src/main/java/de/komoot/photon/SearchRequestHandler.java @@ -21,12 +21,10 @@ public class SearchRequestHandler extends RouteImpl { private final PhotonRequestFactory photonRequestFactory; private final SearchHandler requestHandler; private final int maxResults; - private final boolean useGeometryColumn; - SearchRequestHandler(String path, SearchHandler dbHandler, String[] languages, String defaultLanguage, int maxResults, boolean useGeometryColumn) { + SearchRequestHandler(String path, SearchHandler dbHandler, String[] languages, String defaultLanguage, int maxResults) { super(path); List<String> supportedLanguages = Arrays.asList(languages); - this.useGeometryColumn = useGeometryColumn; this.photonRequestFactory = new PhotonRequestFactory(supportedLanguages, defaultLanguage, maxResults); this.requestHandler = dbHandler; this.maxResults = maxResults; @@ -58,6 +56,6 @@ public String handle(Request request, Response response) { debugInfo = requestHandler.dumpQuery(photonRequest); } - return new GeocodeJsonFormatter(photonRequest.getDebug(), photonRequest.getLanguage(), useGeometryColumn).convert(results, debugInfo); + return new GeocodeJsonFormatter(photonRequest.getDebug(), photonRequest.getLanguage(), photonRequest.getPolygon()).convert(results, debugInfo); } } \ No newline at end of file diff --git a/src/main/java/de/komoot/photon/StructuredSearchRequestHandler.java b/src/main/java/de/komoot/photon/StructuredSearchRequestHandler.java index cef36329d..e8aeafd36 100644 --- a/src/main/java/de/komoot/photon/StructuredSearchRequestHandler.java +++ b/src/main/java/de/komoot/photon/StructuredSearchRequestHandler.java @@ -17,12 +17,10 @@ public class StructuredSearchRequestHandler extends RouteImpl { private final PhotonRequestFactory photonRequestFactory; private final StructuredSearchHandler requestHandler; - private final boolean useGeometryColumn; - StructuredSearchRequestHandler(String path, StructuredSearchHandler dbHandler, String[] languages, String defaultLanguage, int maxResults, boolean useGeometryColumn) { + StructuredSearchRequestHandler(String path, StructuredSearchHandler dbHandler, String[] languages, String defaultLanguage, int maxResults) { super(path); List<String> supportedLanguages = Arrays.asList(languages); - this.useGeometryColumn = useGeometryColumn; this.photonRequestFactory = new PhotonRequestFactory(supportedLanguages, defaultLanguage, maxResults); this.requestHandler = dbHandler; } @@ -53,6 +51,6 @@ public String handle(Request request, Response response) { debugInfo = requestHandler.dumpQuery(photonRequest); } */ - return new GeocodeJsonFormatter(photonRequest.getDebug(), photonRequest.getLanguage(), useGeometryColumn).convert(results, debugInfo); + return new GeocodeJsonFormatter(photonRequest.getDebug(), photonRequest.getLanguage(), photonRequest.getPolygon()).convert(results, debugInfo); } } \ No newline at end of file diff --git a/src/main/java/de/komoot/photon/query/PhotonRequestBase.java b/src/main/java/de/komoot/photon/query/PhotonRequestBase.java index 720686e49..5f5f9c99a 100644 --- a/src/main/java/de/komoot/photon/query/PhotonRequestBase.java +++ b/src/main/java/de/komoot/photon/query/PhotonRequestBase.java @@ -18,6 +18,7 @@ public class PhotonRequestBase private int zoom = 14; private Envelope bbox = null; private boolean debug = false; + private boolean polygon = false; private final List<TagFilter> osmTagFilters = new ArrayList<>(1); private Set<String> layerFilters = new HashSet<>(1); @@ -53,6 +54,8 @@ public String getLanguage() { public boolean getDebug() { return debug; } + public boolean getPolygon() { return polygon; } + public List<TagFilter> getOsmTagFilters() { return osmTagFilters; } @@ -100,4 +103,8 @@ void setBbox(Envelope bbox) { void enableDebug() { this.debug = true; } + + public void setPolygon(boolean polygon) { + this.polygon = polygon; + } } diff --git a/src/main/java/de/komoot/photon/query/PhotonRequestFactory.java b/src/main/java/de/komoot/photon/query/PhotonRequestFactory.java index 081244d94..5fb5886d8 100644 --- a/src/main/java/de/komoot/photon/query/PhotonRequestFactory.java +++ b/src/main/java/de/komoot/photon/query/PhotonRequestFactory.java @@ -19,7 +19,7 @@ public class PhotonRequestFactory { private final int maxResults; private static final HashSet<String> REQUEST_QUERY_PARAMS = new HashSet<>(Arrays.asList("lang", "q", "lon", "lat", - "limit", "osm_tag", "location_bias_scale", "bbox", "debug", "zoom", "layer")); + "limit", "osm_tag", "location_bias_scale", "bbox", "debug", "zoom", "layer", "polygon")); private static final HashSet<String> STRUCTURED_ADDRESS_FIELDS = new HashSet<>(Arrays.asList("countrycode", "state", "county", "city", "postcode", "district", "housenumber", "street")); @@ -59,6 +59,7 @@ public StructuredPhotonRequest createStructured(Request webRequest) throws BadRe result.setStreet(webRequest.queryParams("street")); result.setHouseNumber(webRequest.queryParams("housenumber")); + addCommonParameters(webRequest, result); return result; @@ -110,6 +111,8 @@ private void addCommonParameters(Request webRequest, PhotonRequestBase request) if (layerFiltersQueryMap.hasValue()) { request.setLayerFilter(layerParamValidator.validate(layerFiltersQueryMap.values())); } + + request.setPolygon(Boolean.parseBoolean(webRequest.queryParams("polygon"))); } private Integer parseInt(Request webRequest, String param) throws BadRequestException { diff --git a/src/main/java/de/komoot/photon/query/ReverseRequest.java b/src/main/java/de/komoot/photon/query/ReverseRequest.java index cb8c78cde..5fabf0cb0 100644 --- a/src/main/java/de/komoot/photon/query/ReverseRequest.java +++ b/src/main/java/de/komoot/photon/query/ReverseRequest.java @@ -20,9 +20,10 @@ public class ReverseRequest implements Serializable { private final Set<String> layerFilters; private final List<TagFilter> osmTagFilters = new ArrayList<>(1); private final boolean debug; + private final boolean polygon; public ReverseRequest(Point location, String language, double radius, String queryStringFilter, int limit, - boolean locationDistanceSort, Set<String> layerFilter, boolean debug) { + boolean locationDistanceSort, Set<String> layerFilter, boolean debug, boolean polygon) { this.location = location; this.language = language; this.radius = radius; @@ -31,6 +32,7 @@ public ReverseRequest(Point location, String language, double radius, String que this.locationDistanceSort = locationDistanceSort; this.layerFilters = layerFilter; this.debug = debug; + this.polygon = polygon; } public Point getLocation() { @@ -69,6 +71,10 @@ public boolean getDebug() { return debug; } + public boolean getPolygon() { + return polygon; + } + ReverseRequest addOsmTagFilter(TagFilter filter) { osmTagFilters.add(filter); return this; diff --git a/src/main/java/de/komoot/photon/query/ReverseRequestFactory.java b/src/main/java/de/komoot/photon/query/ReverseRequestFactory.java index fe65b162d..d3ae421dc 100644 --- a/src/main/java/de/komoot/photon/query/ReverseRequestFactory.java +++ b/src/main/java/de/komoot/photon/query/ReverseRequestFactory.java @@ -16,14 +16,14 @@ public class ReverseRequestFactory { private static final HashSet<String> REQUEST_QUERY_PARAMS = new HashSet<>(Arrays.asList("lang", "lon", "lat", "radius", - "query_string_filter", "distance_sort", "limit", "layer", "osm_tag", "debug")); + "query_string_filter", "distance_sort", "limit", "layer", "osm_tag", "debug", "polygon")); private static final LocationParamConverter mandatoryLocationParamConverter = new LocationParamConverter(true); private final RequestLanguageResolver languageResolver; private final LayerParamValidator layerParamValidator; private final int maxResults; - public ReverseRequestFactory(List<String> supportedLanguages, String defaultLanguage, int maxResults, boolean useGeometryColumn) { + public ReverseRequestFactory(List<String> supportedLanguages, String defaultLanguage, int maxResults) { this.languageResolver = new RequestLanguageResolver(supportedLanguages, defaultLanguage); this.layerParamValidator = new LayerParamValidator(); this.maxResults = maxResults; @@ -86,7 +86,7 @@ public ReverseRequest create(Request webRequest) throws BadRequestException { } String queryStringFilter = webRequest.queryParams("query_string_filter"); - ReverseRequest request = new ReverseRequest(location, language, radius, queryStringFilter, limit, locationDistanceSort, layerFilter, enableDebug); + ReverseRequest request = new ReverseRequest(location, language, radius, queryStringFilter, limit, locationDistanceSort, layerFilter, enableDebug, Boolean.parseBoolean(webRequest.queryParams("polygon"))); QueryParamsMap tagFiltersQueryMap = webRequest.queryMap("osm_tag"); if (tagFiltersQueryMap.hasValue()) { diff --git a/src/main/java/de/komoot/photon/searcher/GeocodeJsonFormatter.java b/src/main/java/de/komoot/photon/searcher/GeocodeJsonFormatter.java index 1cc8ee722..677775e04 100644 --- a/src/main/java/de/komoot/photon/searcher/GeocodeJsonFormatter.java +++ b/src/main/java/de/komoot/photon/searcher/GeocodeJsonFormatter.java @@ -28,7 +28,7 @@ public String convert(List<PhotonResult> results, String debugInfo) { final JSONArray features = new JSONArray(results.size()); for (PhotonResult result : results) { - if (useGeometryColumn) { + if (useGeometryColumn && result.get("geometry") != null) { features.put(new JSONObject() .put("type", "Feature") .put("properties", getResultProperties(result)) diff --git a/src/test/java/de/komoot/photon/query/QueryReverseFilterLayerTest.java b/src/test/java/de/komoot/photon/query/QueryReverseFilterLayerTest.java index 5efac0bbc..c649fddf4 100644 --- a/src/test/java/de/komoot/photon/query/QueryReverseFilterLayerTest.java +++ b/src/test/java/de/komoot/photon/query/QueryReverseFilterLayerTest.java @@ -52,7 +52,7 @@ private List<PhotonResult> reverse(String... layers) { Point pt = FACTORY.createPoint(new Coordinate(10, 10)); Set<String> layerSet = Arrays.stream(layers).collect(Collectors.toSet()); - ReverseRequest request = new ReverseRequest(pt, "en", 1.0, "", 10, true, layerSet, false); + ReverseRequest request = new ReverseRequest(pt, "en", 1.0, "", 10, true, layerSet, false, false); return getServer().createReverseHandler(1).reverse(request); } diff --git a/src/test/java/de/komoot/photon/query/QueryReverseFilterTagValueTest.java b/src/test/java/de/komoot/photon/query/QueryReverseFilterTagValueTest.java index 4f4a46fe0..6fb8b5274 100644 --- a/src/test/java/de/komoot/photon/query/QueryReverseFilterTagValueTest.java +++ b/src/test/java/de/komoot/photon/query/QueryReverseFilterTagValueTest.java @@ -68,7 +68,7 @@ public void tearDown() throws IOException { private List<PhotonResult> reverseWithTags(String[] params) { Point pt = FACTORY.createPoint(new Coordinate(13.38886, 52.51704)); - ReverseRequest request = new ReverseRequest(pt, "en", 1.0, "", 50, true, new HashSet<>(), false); + ReverseRequest request = new ReverseRequest(pt, "en", 1.0, "", 50, true, new HashSet<>(), false, false); for (String param : params) { request.addOsmTagFilter(TagFilter.buildOsmTagFilter(param)); } diff --git a/src/test/java/de/komoot/photon/query/QueryReverseTest.java b/src/test/java/de/komoot/photon/query/QueryReverseTest.java index 7d96ff781..0210bdae9 100644 --- a/src/test/java/de/komoot/photon/query/QueryReverseTest.java +++ b/src/test/java/de/komoot/photon/query/QueryReverseTest.java @@ -48,7 +48,7 @@ private List<PhotonResult> reverse(double lon, double lat, double radius, int li Point pt = FACTORY.createPoint(new Coordinate(lon, lat)); return getServer().createReverseHandler(1).reverse( - new ReverseRequest(pt, "en", radius, "", limit, true, new HashSet<>(), false) + new ReverseRequest(pt, "en", radius, "", limit, true, new HashSet<>(), false, false) ); } diff --git a/src/test/java/de/komoot/photon/query/ReverseRequestFactoryTest.java b/src/test/java/de/komoot/photon/query/ReverseRequestFactoryTest.java index 5683836e8..1e302e0f6 100644 --- a/src/test/java/de/komoot/photon/query/ReverseRequestFactoryTest.java +++ b/src/test/java/de/komoot/photon/query/ReverseRequestFactoryTest.java @@ -56,7 +56,7 @@ private void requestWithLayers(Request mockRequest, String... layers) { @Test void testWithLocation() throws Exception { Request mockRequest = createRequestWithLongitudeLatitude(-87d, 41d); - ReverseRequestFactory reverseRequestFactory = new ReverseRequestFactory(Collections.singletonList("en"), "en", 10, false); + ReverseRequestFactory reverseRequestFactory = new ReverseRequestFactory(Collections.singletonList("en"), "en", 10); reverseRequest = reverseRequestFactory.create(mockRequest); assertEquals(-87, reverseRequest.getLocation().getX(), 0); assertEquals(41, reverseRequest.getLocation().getY(), 0); @@ -66,7 +66,7 @@ void testWithLocation() throws Exception { private void assertBadRequest(Request mockRequest, String expectedMessage) { try { - ReverseRequestFactory reverseRequestFactory = new ReverseRequestFactory(Collections.singletonList("en"), "en", 10, false); + ReverseRequestFactory reverseRequestFactory = new ReverseRequestFactory(Collections.singletonList("en"), "en", 10); reverseRequest = reverseRequestFactory.create(mockRequest); fail(); } catch (BadRequestException e) { @@ -155,7 +155,7 @@ void testWithBadRadius() throws Exception { void testHighRadius() throws Exception { Request mockRequest = createRequestWithLongitudeLatitude(-87d, 41d); Mockito.when(mockRequest.queryParams("radius")).thenReturn("5.1"); - ReverseRequestFactory reverseRequestFactory = new ReverseRequestFactory(Collections.singletonList("en"), "en", 10, false); + ReverseRequestFactory reverseRequestFactory = new ReverseRequestFactory(Collections.singletonList("en"), "en", 10); reverseRequest = reverseRequestFactory.create(mockRequest); assertEquals(5.1d, reverseRequest.getRadius(), 0); Mockito.verify(mockRequest, Mockito.times(1)).queryParams("radius"); @@ -180,7 +180,7 @@ void testWithBadLimit() throws Exception { void testHighLimit() throws Exception { Request mockRequest = createRequestWithLongitudeLatitude(-87d, 41d); Mockito.when(mockRequest.queryParams("limit")).thenReturn("51"); - ReverseRequestFactory reverseRequestFactory = new ReverseRequestFactory(Collections.singletonList("en"), "en", 50, false); + ReverseRequestFactory reverseRequestFactory = new ReverseRequestFactory(Collections.singletonList("en"), "en", 50); reverseRequest = reverseRequestFactory.create(mockRequest); assertEquals(50, reverseRequest.getLimit()); Mockito.verify(mockRequest, Mockito.times(1)).queryParams("limit"); @@ -189,7 +189,7 @@ void testHighLimit() throws Exception { @Test void testDistanceSortDefault() throws Exception { Request mockRequest = createRequestWithLongitudeLatitude(-87d, 41d); - ReverseRequestFactory reverseRequestFactory = new ReverseRequestFactory(Collections.singletonList("en"), "en", 10, false); + ReverseRequestFactory reverseRequestFactory = new ReverseRequestFactory(Collections.singletonList("en"), "en", 10); reverseRequest = reverseRequestFactory.create(mockRequest); Mockito.verify(mockRequest, Mockito.times(1)).queryParamOrDefault("distance_sort", "true"); assertEquals(true, reverseRequest.getLocationDistanceSort()); @@ -199,7 +199,7 @@ void testDistanceSortDefault() throws Exception { void testWithLayersFilters() throws Exception { Request mockRequest = createRequestWithLongitudeLatitude(-87d, 41d); requestWithLayers(mockRequest, "city", "locality"); - ReverseRequestFactory reverseRequestFactory = new ReverseRequestFactory(Collections.singletonList("en"), "en", 10, false); + ReverseRequestFactory reverseRequestFactory = new ReverseRequestFactory(Collections.singletonList("en"), "en", 10); reverseRequest = reverseRequestFactory.create(mockRequest); assertEquals(new HashSet<>(Arrays.asList("city", "locality")), reverseRequest.getLayerFilters()); } @@ -208,7 +208,7 @@ void testWithLayersFilters() throws Exception { void testWithDuplicatedLayerFilters() throws Exception { Request mockRequest = createRequestWithLongitudeLatitude(-87d, 41d); requestWithLayers(mockRequest, "city", "locality", "city"); - ReverseRequestFactory reverseRequestFactory = new ReverseRequestFactory(Collections.singletonList("en"), "en", 10, false); + ReverseRequestFactory reverseRequestFactory = new ReverseRequestFactory(Collections.singletonList("en"), "en", 10); reverseRequest = reverseRequestFactory.create(mockRequest); assertEquals(new HashSet<>(Arrays.asList("city", "locality")), reverseRequest.getLayerFilters()); } @@ -225,7 +225,7 @@ void testWithBadLayerFilters() { void testTagFilters() throws Exception { Request mockRequest = createRequestWithLongitudeLatitude(-87d, 41d); requestWithOsmFilters(mockRequest, "foo", ":!bar"); - ReverseRequestFactory reverseRequestFactory = new ReverseRequestFactory(Collections.singletonList("en"), "en", 10, false); + ReverseRequestFactory reverseRequestFactory = new ReverseRequestFactory(Collections.singletonList("en"), "en", 10); reverseRequest = reverseRequestFactory.create(mockRequest); List<TagFilter> result = reverseRequest.getOsmTagFilters(); @@ -242,7 +242,7 @@ void testTagFilters() throws Exception { void testBadTagFilters() { Request mockRequest = createRequestWithLongitudeLatitude(-87d, 41d); requestWithOsmFilters(mockRequest, "good", "bad:bad:bad"); - ReverseRequestFactory reverseRequestFactory = new ReverseRequestFactory(Collections.singletonList("en"), "en", 10, false); + ReverseRequestFactory reverseRequestFactory = new ReverseRequestFactory(Collections.singletonList("en"), "en", 10); assertThrows(BadRequestException.class, () -> reverseRequestFactory.create(mockRequest)); } @@ -252,7 +252,7 @@ void testWithDebug() throws Exception { Request mockRequest = createRequestWithLongitudeLatitude(-87d, 41d); Mockito.when(mockRequest.queryParams("debug")).thenReturn("1"); - ReverseRequestFactory reverseRequestFactory = new ReverseRequestFactory(Collections.singletonList("en"), "en", 10, false); + ReverseRequestFactory reverseRequestFactory = new ReverseRequestFactory(Collections.singletonList("en"), "en", 10); reverseRequest = reverseRequestFactory.create(mockRequest); assertTrue(reverseRequest.getDebug()); From d544bb380ddb037cdb694f5d1ac38543f3d01ee1 Mon Sep 17 00:00:00 2001 From: Your Name <you@example.com> Date: Thu, 5 Sep 2024 20:52:00 +0200 Subject: [PATCH 07/18] feat: add unit tests and process remarks of @lonvia --- .../main/java/de/komoot/photon/Server.java | 12 +++- .../java/de/komoot/photon/ESBaseTester.java | 16 +++-- .../elasticsearch/ElasticResultTest.java | 2 +- .../main/java/de/komoot/photon/Server.java | 6 +- .../photon/opensearch/DBPropertyEntry.java | 2 + .../photon/opensearch/IndexMapping.java | 1 + .../java/de/komoot/photon/ESBaseTester.java | 14 +++-- buildSrc/shared.gradle | 2 + src/main/java/de/komoot/photon/App.java | 22 ++++--- .../de/komoot/photon/CommandLineArgs.java | 4 +- .../de/komoot/photon/DatabaseProperties.java | 21 +++++++ src/main/java/de/komoot/photon/PhotonDoc.java | 9 +++ .../komoot/photon/SearchRequestHandler.java | 17 +++++- .../StructuredSearchRequestHandler.java | 2 +- .../photon/query/PhotonRequestBase.java | 17 ++++-- .../photon/query/PhotonRequestFactory.java | 5 +- .../de/komoot/photon/ApiIntegrationTest.java | 23 +++++++- .../komoot/photon/api/ApiLanguagesTest.java | 2 +- .../nominatim/NominatimConnectorDBTest.java | 16 ++++- .../nominatim/testdb/PlacexTestRow.java | 2 + .../query/PhotonRequestFactoryTest.java | 8 +++ .../photon/query/QueryByLanguageTest.java | 2 +- .../photon/query/QueryFilterLayerTest.java | 2 +- .../photon/query/QueryFilterTagValueTest.java | 2 +- .../komoot/photon/query/QueryPolygonTest.java | 58 +++++++++++++++++++ .../query/QueryReverseFilterLayerTest.java | 2 +- .../query/QueryReverseFilterTagValueTest.java | 5 +- .../komoot/photon/query/QueryReverseTest.java | 5 +- 28 files changed, 234 insertions(+), 45 deletions(-) create mode 100644 src/test/java/de/komoot/photon/query/QueryPolygonTest.java diff --git a/app/es_embedded/src/main/java/de/komoot/photon/Server.java b/app/es_embedded/src/main/java/de/komoot/photon/Server.java index c0e5c3359..bd983b3fc 100644 --- a/app/es_embedded/src/main/java/de/komoot/photon/Server.java +++ b/app/es_embedded/src/main/java/de/komoot/photon/Server.java @@ -48,6 +48,7 @@ public class Server { private static final String FIELD_VERSION = "database_version"; private static final String FIELD_LANGUAGES = "indexed_languages"; private static final String FIELD_IMPORT_DATE = "import_date"; + private static final String FIELD_SUPPORT_POLYGONS = "support_polygons"; private Node esNode; @@ -169,7 +170,7 @@ private void setupDirectories(URL directoryName) throws IOException, URISyntaxEx } - public DatabaseProperties recreateIndex(String[] languages, Date importDate, boolean supportStructuredQueries) throws IOException { + public DatabaseProperties recreateIndex(String[] languages, Date importDate, boolean supportStructuredQueries, boolean supportPolygons) throws IOException { deleteIndex(); loadIndexSettings().createIndex(esClient, PhotonIndex.NAME); @@ -178,7 +179,10 @@ public DatabaseProperties recreateIndex(String[] languages, Date importDate, boo DatabaseProperties dbProperties = new DatabaseProperties() .setLanguages(languages) - .setImportDate(importDate); + .setImportDate(importDate) + .setSupportPolygons(supportPolygons); + + LOGGER.info(dbProperties.toString()); saveToDatabase(dbProperties); return dbProperties; @@ -234,6 +238,7 @@ public void saveToDatabase(DatabaseProperties dbProperties) throws IOException .field(FIELD_VERSION, DatabaseProperties.DATABASE_VERSION) .field(FIELD_LANGUAGES, String.join(",", dbProperties.getLanguages())) .field(FIELD_IMPORT_DATE, dbProperties.getImportDate() instanceof Date ? dbProperties.getImportDate().toInstant() : null) + .field(FIELD_SUPPORT_POLYGONS, Boolean.toString(dbProperties.getSupportPolygons())) .endObject().endObject(); esClient.prepareIndex(PhotonIndex.NAME, PhotonIndex.TYPE). @@ -274,6 +279,9 @@ public void loadFromDatabase(DatabaseProperties dbProperties) { String importDateString = properties.get(FIELD_IMPORT_DATE); dbProperties.setImportDate(importDateString == null ? null : Date.from(Instant.parse(importDateString))); + + String supportPolygons = properties.get(FIELD_SUPPORT_POLYGONS); + dbProperties.setSupportPolygons(supportPolygons == null ? null : Boolean.parseBoolean(supportPolygons)); } public Importer createImporter(String[] languages, String[] extraTags) { diff --git a/app/es_embedded/src/test/java/de/komoot/photon/ESBaseTester.java b/app/es_embedded/src/test/java/de/komoot/photon/ESBaseTester.java index 5b2e5008c..676bd949b 100644 --- a/app/es_embedded/src/test/java/de/komoot/photon/ESBaseTester.java +++ b/app/es_embedded/src/test/java/de/komoot/photon/ESBaseTester.java @@ -7,6 +7,8 @@ import de.komoot.photon.searcher.PhotonResult; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.io.TempDir; +import org.locationtech.jts.io.ParseException; +import org.locationtech.jts.io.WKTReader; import java.io.IOException; import java.nio.file.Path; @@ -25,9 +27,9 @@ public class ESBaseTester { private ElasticTestServer server; - protected PhotonDoc createDoc(double lon, double lat, int id, int osmId, String key, String value) { + protected PhotonDoc createDoc(double lon, double lat, int id, int osmId, String key, String value) throws ParseException { Point location = FACTORY.createPoint(new Coordinate(lon, lat)); - return new PhotonDoc(id, "W", osmId, key, value).names(Collections.singletonMap("name", "berlin")).centroid(location); + return new PhotonDoc(id, "W", osmId, key, value).names(Collections.singletonMap("name", "berlin")).centroid(location).geometry(new WKTReader().read("POLYGON ((6.4440619 52.1969454, 6.4441094 52.1969158, 6.4441408 52.1969347, 6.4441138 52.1969516, 6.4440933 52.1969643, 6.4440619 52.1969454))")); } protected PhotonResult getById(int id) { @@ -45,17 +47,21 @@ public void tearDown() throws IOException { } public void setUpES() throws IOException { - setUpES(dataDirectory, "en"); + setUpES(dataDirectory, false,"en"); + } + + public void setUpESWithPolygons() throws IOException { + setUpES(dataDirectory, true,"en"); } /** * Setup the ES server * * @throws IOException */ - public void setUpES(Path test_directory, String... languages) throws IOException { + public void setUpES(Path test_directory, boolean supportPolygons, String... languages) throws IOException { server = new ElasticTestServer(test_directory.toString()); server.start(TEST_CLUSTER_NAME, new String[]{}); - server.recreateIndex(languages, new Date(), false); + server.recreateIndex(languages, new Date(), false, supportPolygons); refresh(); } diff --git a/app/es_embedded/src/test/java/de/komoot/photon/elasticsearch/ElasticResultTest.java b/app/es_embedded/src/test/java/de/komoot/photon/elasticsearch/ElasticResultTest.java index db51d985e..58e5ce73d 100644 --- a/app/es_embedded/src/test/java/de/komoot/photon/elasticsearch/ElasticResultTest.java +++ b/app/es_embedded/src/test/java/de/komoot/photon/elasticsearch/ElasticResultTest.java @@ -44,7 +44,7 @@ protected PhotonDoc createDoc(double lon, double lat, int id, int osmId, String @BeforeAll void setUp() throws Exception { - setUpES(instanceTestDirectory, "en", "de", "fr", "it"); + setUpES(instanceTestDirectory, false, "en", "de", "fr", "it"); Importer instance = getServer().createImporter(new String[]{"en", "de", "fr", "it"}, new String[]{"population", "capital"}); diff --git a/app/opensearch/src/main/java/de/komoot/photon/Server.java b/app/opensearch/src/main/java/de/komoot/photon/Server.java index ab7456269..3ecf08ab5 100644 --- a/app/opensearch/src/main/java/de/komoot/photon/Server.java +++ b/app/opensearch/src/main/java/de/komoot/photon/Server.java @@ -98,7 +98,7 @@ public void shutdown() { } } - public DatabaseProperties recreateIndex(String[] languages, Date importDate, boolean supportStructuredQueries) throws IOException { + public DatabaseProperties recreateIndex(String[] languages, Date importDate, boolean supportStructuredQueries, boolean supportPolygons) throws IOException { // delete any existing data if (client.indices().exists(e -> e.index(PhotonIndex.NAME)).value()) { client.indices().delete(d -> d.index(PhotonIndex.NAME)); @@ -111,7 +111,8 @@ public DatabaseProperties recreateIndex(String[] languages, Date importDate, boo var dbProperties = new DatabaseProperties() .setLanguages(languages) .setSupportStructuredQueries(supportStructuredQueries) - .setImportDate(importDate); + .setImportDate(importDate) + .setSupportPolygons(supportPolygons); saveToDatabase(dbProperties); return dbProperties; @@ -157,6 +158,7 @@ public void loadFromDatabase(DatabaseProperties dbProperties) throws IOException dbProperties.setLanguages(dbEntry.source().languages); dbProperties.setImportDate(dbEntry.source().importDate); dbProperties.setSupportStructuredQueries(dbEntry.source().supportStructuredQueries); + dbProperties.setSupportPolygons(dbEntry.source().supportPolygons); } public Importer createImporter(String[] languages, String[] extraTags) { diff --git a/app/opensearch/src/main/java/de/komoot/photon/opensearch/DBPropertyEntry.java b/app/opensearch/src/main/java/de/komoot/photon/opensearch/DBPropertyEntry.java index fa96a1d3e..27ed1050d 100644 --- a/app/opensearch/src/main/java/de/komoot/photon/opensearch/DBPropertyEntry.java +++ b/app/opensearch/src/main/java/de/komoot/photon/opensearch/DBPropertyEntry.java @@ -9,6 +9,7 @@ public class DBPropertyEntry { public Date importDate; public String[] languages; public boolean supportStructuredQueries; + public boolean supportPolygons; public DBPropertyEntry() {} @@ -17,5 +18,6 @@ public DBPropertyEntry(DatabaseProperties props) { importDate = props.getImportDate(); languages = props.getLanguages(); supportStructuredQueries = props.getSupportStructuredQueries(); + supportPolygons = props.getSupportPolygons(); } } diff --git a/app/opensearch/src/main/java/de/komoot/photon/opensearch/IndexMapping.java b/app/opensearch/src/main/java/de/komoot/photon/opensearch/IndexMapping.java index 2c81d76a7..fd8e2920c 100644 --- a/app/opensearch/src/main/java/de/komoot/photon/opensearch/IndexMapping.java +++ b/app/opensearch/src/main/java/de/komoot/photon/opensearch/IndexMapping.java @@ -79,6 +79,7 @@ private void setupBaseMappings() { } mappings.properties("coordinate", b -> b.geoPoint(p -> p)); + mappings.properties("geometry", b -> b.geoShape(p -> p)); mappings.properties("countrycode", b -> b.keyword(p -> p.index(true))); mappings.properties("importance", b -> b.float_(p -> p.index(false))); diff --git a/app/opensearch/src/test/java/de/komoot/photon/ESBaseTester.java b/app/opensearch/src/test/java/de/komoot/photon/ESBaseTester.java index e8d9eec9f..9a7241f9b 100644 --- a/app/opensearch/src/test/java/de/komoot/photon/ESBaseTester.java +++ b/app/opensearch/src/test/java/de/komoot/photon/ESBaseTester.java @@ -1,6 +1,8 @@ package de.komoot.photon; +import de.komoot.photon.opensearch.Importer; import de.komoot.photon.opensearch.OpenSearchTestServer; +import de.komoot.photon.opensearch.Updater; import de.komoot.photon.searcher.PhotonResult; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.io.TempDir; @@ -49,28 +51,28 @@ public void setUpES() throws IOException { public void setUpES(Path test_directory, String... languages) throws IOException { server = new OpenSearchTestServer(test_directory.toString()); server.startTestServer(TEST_CLUSTER_NAME); - server.recreateIndex(languages, new Date(), true); + server.recreateIndex(languages, new Date(), true, true); server.refreshIndexes(); } protected Importer makeImporter() { - return server.createImporter(new String[]{"en"}, new String[]{}); + return (Importer) server.createImporter(new String[]{"en"}, new String[]{}); } protected Importer makeImporterWithExtra(String... extraTags) { - return server.createImporter(new String[]{"en"}, extraTags); + return (Importer) server.createImporter(new String[]{"en"}, extraTags); } protected Importer makeImporterWithLanguages(String... languages) { - return server.createImporter(languages, new String[]{}); + return (Importer) server.createImporter(languages, new String[]{}); } protected Updater makeUpdater() { - return server.createUpdater(new String[]{"en"}, new String[]{}); + return (Updater) server.createUpdater(new String[]{"en"}, new String[]{}); } protected Updater makeUpdaterWithExtra(String... extraTags) { - return server.createUpdater(new String[]{"en"}, extraTags); + return (Updater) server.createUpdater(new String[]{"en"}, extraTags); } protected Server getServer() { diff --git a/buildSrc/shared.gradle b/buildSrc/shared.gradle index 8c98eb9d1..ff77bf08b 100644 --- a/buildSrc/shared.gradle +++ b/buildSrc/shared.gradle @@ -13,6 +13,8 @@ application { java { sourceCompatibility = JavaVersion.VERSION_11 targetCompatibility = JavaVersion.VERSION_11 + compileJava.options.encoding = "UTF-8" + compileTestJava.options.encoding = "UTF-8" } repositories { diff --git a/src/main/java/de/komoot/photon/App.java b/src/main/java/de/komoot/photon/App.java index a823110bb..00aea759c 100644 --- a/src/main/java/de/komoot/photon/App.java +++ b/src/main/java/de/komoot/photon/App.java @@ -102,7 +102,7 @@ private static void startJsonDump(CommandLineArgs args) { try { final String filename = args.getJsonDump(); final JsonDumper jsonDumper = new JsonDumper(filename, args.getLanguages(), args.getExtraTags()); - NominatimConnector nominatimConnector = new NominatimConnector(args.getHost(), args.getPort(), args.getDatabase(), args.getUser(), args.getPassword(), args.isUseGeometryColumn()); + NominatimConnector nominatimConnector = new NominatimConnector(args.getHost(), args.getPort(), args.getDatabase(), args.getUser(), args.getPassword(), args.getUseGeometryColumn()); nominatimConnector.setImporter(jsonDumper); nominatimConnector.readEntireDatabase(args.getCountryCodes()); LOGGER.info("Json dump was created: {}", filename); @@ -117,10 +117,14 @@ private static void startJsonDump(CommandLineArgs args) { */ private static void startNominatimImport(CommandLineArgs args, Server esServer) { DatabaseProperties dbProperties; - NominatimConnector nominatimConnector = new NominatimConnector(args.getHost(), args.getPort(), args.getDatabase(), args.getUser(), args.getPassword(), args.isUseGeometryColumn()); + NominatimConnector nominatimConnector = new NominatimConnector(args.getHost(), args.getPort(), args.getDatabase(), args.getUser(), args.getPassword(), args.getUseGeometryColumn()); Date importDate = nominatimConnector.getLastImportDate(); + LOGGER.info("Starting import with the following settings: " + + "\n Languages: {} \n Import Date: {} \n Support Structured Queries: {} \n Support Polygons: {}", + args.getLanguages(), importDate, args.getSupportStructuredQueries(), args.getUseGeometryColumn()); + try { - dbProperties = esServer.recreateIndex(args.getLanguages(), importDate, args.getSupportStructuredQueries()); // clear out previous data + dbProperties = esServer.recreateIndex(args.getLanguages(), importDate, args.getSupportStructuredQueries(), args.getUseGeometryColumn()); // clear out previous data } catch (IOException e) { throw new RuntimeException("Cannot setup index, elastic search config files not readable", e); } @@ -133,7 +137,7 @@ private static void startNominatimImport(CommandLineArgs args, Server esServer) } private static void startNominatimUpdateInit(CommandLineArgs args) { - NominatimUpdater nominatimUpdater = new NominatimUpdater(args.getHost(), args.getPort(), args.getDatabase(), args.getUser(), args.getPassword(), args.isUseGeometryColumn()); + NominatimUpdater nominatimUpdater = new NominatimUpdater(args.getHost(), args.getPort(), args.getDatabase(), args.getUser(), args.getPassword(), args.getUseGeometryColumn()); nominatimUpdater.initUpdates(args.getNominatimUpdateInit()); } @@ -160,7 +164,7 @@ private static NominatimUpdater setupNominatimUpdater(CommandLineArgs args, Serv DatabaseProperties dbProperties = new DatabaseProperties(); server.loadFromDatabase(dbProperties); - NominatimUpdater nominatimUpdater = new NominatimUpdater(args.getHost(), args.getPort(), args.getDatabase(), args.getUser(), args.getPassword(), args.isUseGeometryColumn()); + NominatimUpdater nominatimUpdater = new NominatimUpdater(args.getHost(), args.getPort(), args.getDatabase(), args.getUser(), args.getPassword(), args.getUseGeometryColumn()); nominatimUpdater.setUpdater(server.createUpdater(dbProperties.getLanguages(), args.getExtraTags())); return nominatimUpdater; } @@ -176,6 +180,10 @@ private static void startApi(CommandLineArgs args, Server server) throws IOExcep dbProperties.restrictLanguages(args.getLanguages()); } + LOGGER.info("Starting API with the following settings: " + + "\n Languages: {} \n Import Date: {} \n Support Structured Queries: {} \n Support Polygons: {}", + dbProperties.getLanguages(), dbProperties.getImportDate(), dbProperties.getSupportStructuredQueries(), dbProperties.getSupportPolygons()); + port(args.getListenPort()); ipAddress(args.getListenIp()); @@ -191,8 +199,8 @@ private static void startApi(CommandLineArgs args, Server server) throws IOExcep String[] langs = dbProperties.getLanguages(); SearchHandler searchHandler = server.createSearchHandler(langs, args.getQueryTimeout()); - get("api", new SearchRequestHandler("api", searchHandler, langs, args.getDefaultLanguage(), args.getMaxResults())); - get("api/", new SearchRequestHandler("api/", searchHandler, langs, args.getDefaultLanguage(), args.getMaxResults())); + get("api", new SearchRequestHandler("api", searchHandler, langs, args.getDefaultLanguage(), args.getMaxResults(), dbProperties.getSupportPolygons())); + get("api/", new SearchRequestHandler("api/", searchHandler, langs, args.getDefaultLanguage(), args.getMaxResults(), dbProperties.getSupportPolygons())); if (dbProperties.getSupportStructuredQueries()) { StructuredSearchHandler structured = server.createStructuredSearchHandler(langs, args.getQueryTimeout()); diff --git a/src/main/java/de/komoot/photon/CommandLineArgs.java b/src/main/java/de/komoot/photon/CommandLineArgs.java index b4bb93324..c2b383518 100644 --- a/src/main/java/de/komoot/photon/CommandLineArgs.java +++ b/src/main/java/de/komoot/photon/CommandLineArgs.java @@ -91,7 +91,7 @@ public class CommandLineArgs { @Parameter(names = "-max-reverse-results", description = "The maximum possible 'limit' parameter for reverse geocoding searches") private int maxReverseResults = 50; - @Parameter(names = "-use-geometry-column", description = "[import-only] Add the 'geometry' column from Nominatim on import (i.e. add Polygons for cities). WARNING: This will increase the Elasticsearch Index size! (~575GB for Planet)") + @Parameter(names = "-use-geometry-column", description = "[import-only] Add the 'geometry' column from Nominatim on import (i.e. add Polygons for cities, countries etc.). WARNING: This will increase the Elasticsearch Index size! (~575GB for Planet)") private boolean useGeometryColumn = false; public String[] getLanguages(boolean useDefaultIfEmpty) { @@ -208,7 +208,7 @@ public int getMaxResults() { return maxResults; } - public boolean isUseGeometryColumn() { + public boolean getUseGeometryColumn() { return useGeometryColumn; } } diff --git a/src/main/java/de/komoot/photon/DatabaseProperties.java b/src/main/java/de/komoot/photon/DatabaseProperties.java index 5ebfd4ee7..a10340e1d 100644 --- a/src/main/java/de/komoot/photon/DatabaseProperties.java +++ b/src/main/java/de/komoot/photon/DatabaseProperties.java @@ -28,6 +28,8 @@ public class DatabaseProperties { private boolean supportStructuredQueries; + private boolean supportPolygons; + /** * Return the list of languages for which the database is configured. * @return @@ -100,4 +102,23 @@ public DatabaseProperties setSupportStructuredQueries(boolean supportStructuredQ this.supportStructuredQueries = supportStructuredQueries; return this; } + + public boolean getSupportPolygons() { + return supportPolygons; + } + + public DatabaseProperties setSupportPolygons(boolean supportPolygons) { + this.supportPolygons = supportPolygons; + return this; + } + + @Override + public String toString() { + return "DatabaseProperties{" + + "languages=" + Arrays.toString(languages) + + ", importDate=" + importDate + + ", supportStructuredQueries=" + supportStructuredQueries + + ", supportPolygons=" + supportPolygons + + '}'; + } } diff --git a/src/main/java/de/komoot/photon/PhotonDoc.java b/src/main/java/de/komoot/photon/PhotonDoc.java index 17afeeda5..aa8c4c28a 100644 --- a/src/main/java/de/komoot/photon/PhotonDoc.java +++ b/src/main/java/de/komoot/photon/PhotonDoc.java @@ -45,6 +45,15 @@ public PhotonDoc(long placeId, String osmType, long osmId, String tagKey, String this.tagValue = tagValue; } + public PhotonDoc(long placeId, String osmType, long osmId, String tagKey, String tagValue, Geometry geometry) { + this.placeId = placeId; + this.osmType = osmType; + this.osmId = osmId; + this.tagKey = tagKey; + this.tagValue = tagValue; + this.geometry = geometry; + } + public PhotonDoc(PhotonDoc other) { this.placeId = other.placeId; this.osmType = other.osmType; diff --git a/src/main/java/de/komoot/photon/SearchRequestHandler.java b/src/main/java/de/komoot/photon/SearchRequestHandler.java index 909029245..0a96dd41e 100644 --- a/src/main/java/de/komoot/photon/SearchRequestHandler.java +++ b/src/main/java/de/komoot/photon/SearchRequestHandler.java @@ -21,17 +21,19 @@ public class SearchRequestHandler extends RouteImpl { private final PhotonRequestFactory photonRequestFactory; private final SearchHandler requestHandler; private final int maxResults; + private final boolean supportPolygons; - SearchRequestHandler(String path, SearchHandler dbHandler, String[] languages, String defaultLanguage, int maxResults) { + SearchRequestHandler(String path, SearchHandler dbHandler, String[] languages, String defaultLanguage, int maxResults, boolean supportPolygons) { super(path); List<String> supportedLanguages = Arrays.asList(languages); this.photonRequestFactory = new PhotonRequestFactory(supportedLanguages, defaultLanguage, maxResults); this.requestHandler = dbHandler; this.maxResults = maxResults; + this.supportPolygons = supportPolygons; } @Override - public String handle(Request request, Response response) { + public String handle(Request request, Response response) throws BadRequestException { PhotonRequest photonRequest = null; try { photonRequest = photonRequestFactory.create(request); @@ -56,6 +58,15 @@ public String handle(Request request, Response response) { debugInfo = requestHandler.dumpQuery(photonRequest); } - return new GeocodeJsonFormatter(photonRequest.getDebug(), photonRequest.getLanguage(), photonRequest.getPolygon()).convert(results, debugInfo); + boolean returnPolygon = supportPolygons; + if (photonRequest.isPolygonRequest()) { + returnPolygon = photonRequest.getReturnPolygon(); + } + + if (!supportPolygons && (photonRequest.isPolygonRequest() && photonRequest.getReturnPolygon())) { + throw new BadRequestException(400, "You're requesting a polygon, but polygons are not imported!"); + } + + return new GeocodeJsonFormatter(photonRequest.getDebug(), photonRequest.getLanguage(), returnPolygon).convert(results, debugInfo); } } \ No newline at end of file diff --git a/src/main/java/de/komoot/photon/StructuredSearchRequestHandler.java b/src/main/java/de/komoot/photon/StructuredSearchRequestHandler.java index e8aeafd36..a8b4bc7b6 100644 --- a/src/main/java/de/komoot/photon/StructuredSearchRequestHandler.java +++ b/src/main/java/de/komoot/photon/StructuredSearchRequestHandler.java @@ -51,6 +51,6 @@ public String handle(Request request, Response response) { debugInfo = requestHandler.dumpQuery(photonRequest); } */ - return new GeocodeJsonFormatter(photonRequest.getDebug(), photonRequest.getLanguage(), photonRequest.getPolygon()).convert(results, debugInfo); + return new GeocodeJsonFormatter(photonRequest.getDebug(), photonRequest.getLanguage(), photonRequest.getReturnPolygon()).convert(results, debugInfo); } } \ No newline at end of file diff --git a/src/main/java/de/komoot/photon/query/PhotonRequestBase.java b/src/main/java/de/komoot/photon/query/PhotonRequestBase.java index 5f5f9c99a..1e559be10 100644 --- a/src/main/java/de/komoot/photon/query/PhotonRequestBase.java +++ b/src/main/java/de/komoot/photon/query/PhotonRequestBase.java @@ -18,7 +18,8 @@ public class PhotonRequestBase private int zoom = 14; private Envelope bbox = null; private boolean debug = false; - private boolean polygon = false; + private boolean returnPolygon = true; + private boolean polygonRequest = false; private final List<TagFilter> osmTagFilters = new ArrayList<>(1); private Set<String> layerFilters = new HashSet<>(1); @@ -54,7 +55,7 @@ public String getLanguage() { public boolean getDebug() { return debug; } - public boolean getPolygon() { return polygon; } + public boolean getReturnPolygon() { return returnPolygon; } public List<TagFilter> getOsmTagFilters() { return osmTagFilters; @@ -104,7 +105,15 @@ void enableDebug() { this.debug = true; } - public void setPolygon(boolean polygon) { - this.polygon = polygon; + void setReturnPolygon(boolean returnPolygon) { + this.returnPolygon = returnPolygon; + } + + void setPolygonRequest(boolean polygonRequest) { + this.polygonRequest = polygonRequest; + } + + public boolean isPolygonRequest() { + return polygonRequest; } } diff --git a/src/main/java/de/komoot/photon/query/PhotonRequestFactory.java b/src/main/java/de/komoot/photon/query/PhotonRequestFactory.java index 5fb5886d8..d1ea41036 100644 --- a/src/main/java/de/komoot/photon/query/PhotonRequestFactory.java +++ b/src/main/java/de/komoot/photon/query/PhotonRequestFactory.java @@ -112,7 +112,10 @@ private void addCommonParameters(Request webRequest, PhotonRequestBase request) request.setLayerFilter(layerParamValidator.validate(layerFiltersQueryMap.values())); } - request.setPolygon(Boolean.parseBoolean(webRequest.queryParams("polygon"))); + if (webRequest.queryParams("polygon") != null) { + request.setPolygonRequest(true); + request.setReturnPolygon(Boolean.parseBoolean(webRequest.queryParams("polygon"))); + } } private Integer parseInt(Request webRequest, String param) throws BadRequestException { diff --git a/src/test/java/de/komoot/photon/ApiIntegrationTest.java b/src/test/java/de/komoot/photon/ApiIntegrationTest.java index ec9b224b1..00f5200f9 100644 --- a/src/test/java/de/komoot/photon/ApiIntegrationTest.java +++ b/src/test/java/de/komoot/photon/ApiIntegrationTest.java @@ -23,7 +23,7 @@ class ApiIntegrationTest extends ESBaseTester { @BeforeEach void setUp() throws Exception { - setUpES(); + setUpESWithPolygons(); Importer instance = makeImporter(); instance.add(createDoc(13.38886, 52.51704, 1000, 1000, "place", "city").importance(0.6), 0); instance.add(createDoc(13.39026, 52.54714, 1001, 1001, "place", "town").importance(0.3), 0); @@ -163,4 +163,25 @@ public void testApiStatus() throws Exception { assertEquals("Ok", json.getString("status")); assertEquals(prop.getImportDate().toInstant().toString(), json.getString("import_date")); } + + @Test + void testSearchAndGetPolygon() throws Exception { + App.main(new String[]{"-cluster", TEST_CLUSTER_NAME, "-listen-port", Integer.toString(LISTEN_PORT), "-transport-addresses", "127.0.0.1", "-use-geometry-column"}); + awaitInitialization(); + HttpURLConnection connection = (HttpURLConnection) new URL("http://127.0.0.1:" + port() + "/api?q=berlin&limit=1").openConnection(); + JSONObject json = new JSONObject( + new BufferedReader(new InputStreamReader(connection.getInputStream())).lines().collect(Collectors.joining("\n"))); + JSONArray features = json.getJSONArray("features"); + assertEquals(1, features.length()); + JSONObject feature = features.getJSONObject(0); + + JSONObject geometry = feature.getJSONObject("geometry"); + assertEquals("Polygon", geometry.getString("type")); + + JSONObject properties = feature.getJSONObject("properties"); + assertEquals("W", properties.getString("osm_type")); + assertEquals("place", properties.getString("osm_key")); + assertEquals("city", properties.getString("osm_value")); + assertEquals("berlin", properties.getString("name")); + } } diff --git a/src/test/java/de/komoot/photon/api/ApiLanguagesTest.java b/src/test/java/de/komoot/photon/api/ApiLanguagesTest.java index 9e821fe9f..91242b92d 100644 --- a/src/test/java/de/komoot/photon/api/ApiLanguagesTest.java +++ b/src/test/java/de/komoot/photon/api/ApiLanguagesTest.java @@ -48,7 +48,7 @@ protected PhotonDoc createDoc(int id, String key, String value, String... names) } private void importPlaces(String... languages) throws Exception { - setUpES(dataDirectory, languages); + setUpES(dataDirectory, false, languages); Importer instance = makeImporterWithLanguages(languages); instance.add(createDoc(1000, "place", "city", "name:en", "thething", "name:fr", "letruc", "name:ch", "dasding"), 0); diff --git a/src/test/java/de/komoot/photon/nominatim/NominatimConnectorDBTest.java b/src/test/java/de/komoot/photon/nominatim/NominatimConnectorDBTest.java index 5c75580f9..0011d0b1b 100644 --- a/src/test/java/de/komoot/photon/nominatim/NominatimConnectorDBTest.java +++ b/src/test/java/de/komoot/photon/nominatim/NominatimConnectorDBTest.java @@ -35,7 +35,7 @@ void setup() { .build(); - connector = new NominatimConnector(null, 0, null, null, null, new H2DataAdapter(), false); + connector = new NominatimConnector(null, 0, null, null, null, new H2DataAdapter(), true); importer = new CollectingImporter(); connector.setImporter(importer); @@ -336,6 +336,20 @@ void testNoCountry() { assertNull(importer.get(place).getCountryCode()); } + /** + * Places without a country can be imported. + */ + @Test + void testGeometry() { + PlacexTestRow place = new PlacexTestRow("building", "yes").name("Oosterbroek Zuivel").country("nl").add(jdbc); + + connector.readEntireDatabase(); + + assertEquals(1, importer.size()); + + assertNull(importer.get(place).getGeometry()); + } + @Test public void testGetImportDate() { Date importDate = connector.getLastImportDate(); diff --git a/src/test/java/de/komoot/photon/nominatim/testdb/PlacexTestRow.java b/src/test/java/de/komoot/photon/nominatim/testdb/PlacexTestRow.java index a72d6ee0c..36ffc68e7 100644 --- a/src/test/java/de/komoot/photon/nominatim/testdb/PlacexTestRow.java +++ b/src/test/java/de/komoot/photon/nominatim/testdb/PlacexTestRow.java @@ -24,6 +24,7 @@ public class PlacexTestRow { private Integer rankAddress = 30; private Integer rankSearch = 30; private String centroid; + private String geometry; private String countryCode = "us"; private Double importance = null; @@ -33,6 +34,7 @@ public PlacexTestRow(String key, String value) { this.value = value; osmId = placeId; centroid = "POINT (1.0 34.0)"; + geometry = "POLYGON ((6.4440619 52.1969454, 6.4441094 52.1969158, 6.4441408 52.1969347, 6.4441138 52.1969516, 6.4440933 52.1969643, 6.4440619 52.1969454))"; } public static PlacexTestRow make_street(String name) { diff --git a/src/test/java/de/komoot/photon/query/PhotonRequestFactoryTest.java b/src/test/java/de/komoot/photon/query/PhotonRequestFactoryTest.java index 93a6ba85b..1afc529d4 100644 --- a/src/test/java/de/komoot/photon/query/PhotonRequestFactoryTest.java +++ b/src/test/java/de/komoot/photon/query/PhotonRequestFactoryTest.java @@ -211,4 +211,12 @@ void testWithBadLayerFilters() throws Exception { assertTrue(exception.getMessage().contains(expectedMessageFragment), String.format("Error message doesn not contain '%s': %s", expectedMessageFragment, exception.getMessage())); } + + @Test + void testWithoutPolygon() throws Exception { + Request mockRequest = createRequestWithQueryParams("q", "berlin", "polygon", "false"); + PhotonRequest photonRequest = createPhotonRequest(mockRequest); + + assertFalse(photonRequest.getReturnPolygon()); + } } \ No newline at end of file diff --git a/src/test/java/de/komoot/photon/query/QueryByLanguageTest.java b/src/test/java/de/komoot/photon/query/QueryByLanguageTest.java index 33bae735c..acce52e76 100644 --- a/src/test/java/de/komoot/photon/query/QueryByLanguageTest.java +++ b/src/test/java/de/komoot/photon/query/QueryByLanguageTest.java @@ -25,7 +25,7 @@ class QueryByLanguageTest extends ESBaseTester { private Importer setup(String... languages) throws IOException { languageList = languages; - setUpES(dataDirectory, languages); + setUpES(dataDirectory, false, languages); return makeImporterWithLanguages(languages); } diff --git a/src/test/java/de/komoot/photon/query/QueryFilterLayerTest.java b/src/test/java/de/komoot/photon/query/QueryFilterLayerTest.java index bff0a3373..5ad8d9380 100644 --- a/src/test/java/de/komoot/photon/query/QueryFilterLayerTest.java +++ b/src/test/java/de/komoot/photon/query/QueryFilterLayerTest.java @@ -26,7 +26,7 @@ class QueryFilterLayerTest extends ESBaseTester { @BeforeAll void setUp() throws Exception { - setUpES(instanceTestDirectory, "en", "de", "fr"); + setUpES(instanceTestDirectory, false, "en", "de", "fr"); Importer instance = makeImporter(); int id = 0; diff --git a/src/test/java/de/komoot/photon/query/QueryFilterTagValueTest.java b/src/test/java/de/komoot/photon/query/QueryFilterTagValueTest.java index ea3859d4e..1bd2641bd 100644 --- a/src/test/java/de/komoot/photon/query/QueryFilterTagValueTest.java +++ b/src/test/java/de/komoot/photon/query/QueryFilterTagValueTest.java @@ -36,7 +36,7 @@ class QueryFilterTagValueTest extends ESBaseTester { @BeforeAll void setUp() throws Exception { - setUpES(instanceTestDirectory, "en", "de", "fr"); + setUpES(instanceTestDirectory, false, "en", "de", "fr"); Importer instance = makeImporter(); double lon = 13.38886; double lat = 52.51704; diff --git a/src/test/java/de/komoot/photon/query/QueryPolygonTest.java b/src/test/java/de/komoot/photon/query/QueryPolygonTest.java new file mode 100644 index 000000000..3350d023f --- /dev/null +++ b/src/test/java/de/komoot/photon/query/QueryPolygonTest.java @@ -0,0 +1,58 @@ +package de.komoot.photon.query; + +import de.komoot.photon.ESBaseTester; +import de.komoot.photon.Importer; +import de.komoot.photon.PhotonDoc; +import de.komoot.photon.searcher.PhotonResult; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.locationtech.jts.io.ParseException; +import org.locationtech.jts.io.WKTReader; + +import java.io.IOException; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static org.junit.jupiter.api.Assertions.assertAll; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +/** + * Tests that the database backend produces queries which can find all + * expected results. These tests do not check relevance. + */ +class QueryPolygonTest extends ESBaseTester { + private int testDocId = 10000; + + @BeforeEach + void setup() throws IOException { + setUpESWithPolygons(); + } + + private PhotonDoc createDoc(String... names) throws ParseException { + Map<String, String> nameMap = new HashMap<>(); + + for (int i = 0; i < names.length - 1; i += 2) { + nameMap.put(names[i], names[i+1]); + } + + ++testDocId; + return new PhotonDoc(testDocId, "N", testDocId, "place", "city", new WKTReader().read("POLYGON ((1 1, 1 2, 2 1, 1 1))")).names(nameMap); + } + + private List<PhotonResult> search(String query) { + return getServer().createSearchHandler(new String[]{"en"}, 1).search(new PhotonRequest(query, "en")); + } + + + @Test + void testSearchGetPolygon() throws IOException, ParseException { + Importer instance = makeImporter(); + instance.add(createDoc("name", "Muffle Flu"), 0); + instance.finish(); + refresh(); + assertNotNull(search("muffle flu").get(0).get("geometry")); + } +} diff --git a/src/test/java/de/komoot/photon/query/QueryReverseFilterLayerTest.java b/src/test/java/de/komoot/photon/query/QueryReverseFilterLayerTest.java index c649fddf4..883dc8a3b 100644 --- a/src/test/java/de/komoot/photon/query/QueryReverseFilterLayerTest.java +++ b/src/test/java/de/komoot/photon/query/QueryReverseFilterLayerTest.java @@ -28,7 +28,7 @@ class QueryReverseFilterLayerTest extends ESBaseTester { @BeforeAll void setup() throws IOException { - setUpES(instanceTestDirectory, "en"); + setUpES(instanceTestDirectory, false,"en"); Importer instance = makeImporter(); diff --git a/src/test/java/de/komoot/photon/query/QueryReverseFilterTagValueTest.java b/src/test/java/de/komoot/photon/query/QueryReverseFilterTagValueTest.java index 6fb8b5274..0dd82932f 100644 --- a/src/test/java/de/komoot/photon/query/QueryReverseFilterTagValueTest.java +++ b/src/test/java/de/komoot/photon/query/QueryReverseFilterTagValueTest.java @@ -22,6 +22,7 @@ import de.komoot.photon.PhotonDoc; import de.komoot.photon.searcher.PhotonResult; import de.komoot.photon.searcher.TagFilter; +import org.locationtech.jts.io.ParseException; @TestInstance(TestInstance.Lifecycle.PER_CLASS) class QueryReverseFilterTagValueTest extends ESBaseTester { @@ -39,8 +40,8 @@ class QueryReverseFilterTagValueTest extends ESBaseTester { "railway", "station"}; @BeforeAll - void setup() throws IOException { - setUpES(instanceTestDirectory, "en", "de", "fr"); + void setup() throws IOException, ParseException { + setUpES(instanceTestDirectory, false, "en", "de", "fr"); Importer instance = makeImporter(); double lon = 13.38886; double lat = 52.51704; diff --git a/src/test/java/de/komoot/photon/query/QueryReverseTest.java b/src/test/java/de/komoot/photon/query/QueryReverseTest.java index 0210bdae9..0c841bdcf 100644 --- a/src/test/java/de/komoot/photon/query/QueryReverseTest.java +++ b/src/test/java/de/komoot/photon/query/QueryReverseTest.java @@ -12,6 +12,7 @@ import org.junit.jupiter.api.io.TempDir; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; +import org.locationtech.jts.io.ParseException; import java.io.IOException; import java.nio.file.Path; @@ -26,8 +27,8 @@ class QueryReverseTest extends ESBaseTester { private static Path instanceTestDirectory; @BeforeAll - void setup() throws IOException { - setUpES(instanceTestDirectory, "en"); + void setup() throws IOException, ParseException { + setUpES(instanceTestDirectory, false, "en"); Importer instance = makeImporter(); instance.add(createDoc(10,10, 100, 100, "place", "house"), 0); From 41c20c420862b06b104859894fe98e165083f69e Mon Sep 17 00:00:00 2001 From: Your Name <you@example.com> Date: Thu, 5 Sep 2024 21:03:04 +0200 Subject: [PATCH 08/18] bugfix: minor fixes --- .../src/main/java/de/komoot/photon/Server.java | 1 - src/main/java/de/komoot/photon/App.java | 12 ++++++------ src/main/java/de/komoot/photon/CommandLineArgs.java | 8 ++++---- .../photon/nominatim/NominatimConnectorDBTest.java | 10 ++-------- .../photon/nominatim/testdb/PlacexTestRow.java | 6 +++--- 5 files changed, 15 insertions(+), 22 deletions(-) diff --git a/app/es_embedded/src/main/java/de/komoot/photon/Server.java b/app/es_embedded/src/main/java/de/komoot/photon/Server.java index bd983b3fc..63555e14d 100644 --- a/app/es_embedded/src/main/java/de/komoot/photon/Server.java +++ b/app/es_embedded/src/main/java/de/komoot/photon/Server.java @@ -182,7 +182,6 @@ public DatabaseProperties recreateIndex(String[] languages, Date importDate, boo .setImportDate(importDate) .setSupportPolygons(supportPolygons); - LOGGER.info(dbProperties.toString()); saveToDatabase(dbProperties); return dbProperties; diff --git a/src/main/java/de/komoot/photon/App.java b/src/main/java/de/komoot/photon/App.java index 00aea759c..576197f98 100644 --- a/src/main/java/de/komoot/photon/App.java +++ b/src/main/java/de/komoot/photon/App.java @@ -102,7 +102,7 @@ private static void startJsonDump(CommandLineArgs args) { try { final String filename = args.getJsonDump(); final JsonDumper jsonDumper = new JsonDumper(filename, args.getLanguages(), args.getExtraTags()); - NominatimConnector nominatimConnector = new NominatimConnector(args.getHost(), args.getPort(), args.getDatabase(), args.getUser(), args.getPassword(), args.getUseGeometryColumn()); + NominatimConnector nominatimConnector = new NominatimConnector(args.getHost(), args.getPort(), args.getDatabase(), args.getUser(), args.getPassword(), args.getImportGeometryColumn()); nominatimConnector.setImporter(jsonDumper); nominatimConnector.readEntireDatabase(args.getCountryCodes()); LOGGER.info("Json dump was created: {}", filename); @@ -117,14 +117,14 @@ private static void startJsonDump(CommandLineArgs args) { */ private static void startNominatimImport(CommandLineArgs args, Server esServer) { DatabaseProperties dbProperties; - NominatimConnector nominatimConnector = new NominatimConnector(args.getHost(), args.getPort(), args.getDatabase(), args.getUser(), args.getPassword(), args.getUseGeometryColumn()); + NominatimConnector nominatimConnector = new NominatimConnector(args.getHost(), args.getPort(), args.getDatabase(), args.getUser(), args.getPassword(), args.getImportGeometryColumn()); Date importDate = nominatimConnector.getLastImportDate(); LOGGER.info("Starting import with the following settings: " + "\n Languages: {} \n Import Date: {} \n Support Structured Queries: {} \n Support Polygons: {}", - args.getLanguages(), importDate, args.getSupportStructuredQueries(), args.getUseGeometryColumn()); + args.getLanguages(), importDate, args.getSupportStructuredQueries(), args.getImportGeometryColumn()); try { - dbProperties = esServer.recreateIndex(args.getLanguages(), importDate, args.getSupportStructuredQueries(), args.getUseGeometryColumn()); // clear out previous data + dbProperties = esServer.recreateIndex(args.getLanguages(), importDate, args.getSupportStructuredQueries(), args.getImportGeometryColumn()); // clear out previous data } catch (IOException e) { throw new RuntimeException("Cannot setup index, elastic search config files not readable", e); } @@ -137,7 +137,7 @@ private static void startNominatimImport(CommandLineArgs args, Server esServer) } private static void startNominatimUpdateInit(CommandLineArgs args) { - NominatimUpdater nominatimUpdater = new NominatimUpdater(args.getHost(), args.getPort(), args.getDatabase(), args.getUser(), args.getPassword(), args.getUseGeometryColumn()); + NominatimUpdater nominatimUpdater = new NominatimUpdater(args.getHost(), args.getPort(), args.getDatabase(), args.getUser(), args.getPassword(), args.getImportGeometryColumn()); nominatimUpdater.initUpdates(args.getNominatimUpdateInit()); } @@ -164,7 +164,7 @@ private static NominatimUpdater setupNominatimUpdater(CommandLineArgs args, Serv DatabaseProperties dbProperties = new DatabaseProperties(); server.loadFromDatabase(dbProperties); - NominatimUpdater nominatimUpdater = new NominatimUpdater(args.getHost(), args.getPort(), args.getDatabase(), args.getUser(), args.getPassword(), args.getUseGeometryColumn()); + NominatimUpdater nominatimUpdater = new NominatimUpdater(args.getHost(), args.getPort(), args.getDatabase(), args.getUser(), args.getPassword(), args.getImportGeometryColumn()); nominatimUpdater.setUpdater(server.createUpdater(dbProperties.getLanguages(), args.getExtraTags())); return nominatimUpdater; } diff --git a/src/main/java/de/komoot/photon/CommandLineArgs.java b/src/main/java/de/komoot/photon/CommandLineArgs.java index c2b383518..41ae5386e 100644 --- a/src/main/java/de/komoot/photon/CommandLineArgs.java +++ b/src/main/java/de/komoot/photon/CommandLineArgs.java @@ -91,8 +91,8 @@ public class CommandLineArgs { @Parameter(names = "-max-reverse-results", description = "The maximum possible 'limit' parameter for reverse geocoding searches") private int maxReverseResults = 50; - @Parameter(names = "-use-geometry-column", description = "[import-only] Add the 'geometry' column from Nominatim on import (i.e. add Polygons for cities, countries etc.). WARNING: This will increase the Elasticsearch Index size! (~575GB for Planet)") - private boolean useGeometryColumn = false; + @Parameter(names = "-import-geometry-column", description = "[import-only] Add the 'geometry' column from Nominatim on import (i.e. add Polygons for cities, countries etc.). WARNING: This will increase the Elasticsearch Index size! (~575GB for Planet)") + private boolean importGeometryColumn = false; public String[] getLanguages(boolean useDefaultIfEmpty) { if (useDefaultIfEmpty && languages.length == 0) { @@ -208,8 +208,8 @@ public int getMaxResults() { return maxResults; } - public boolean getUseGeometryColumn() { - return useGeometryColumn; + public boolean getImportGeometryColumn() { + return importGeometryColumn; } } diff --git a/src/test/java/de/komoot/photon/nominatim/NominatimConnectorDBTest.java b/src/test/java/de/komoot/photon/nominatim/NominatimConnectorDBTest.java index 0011d0b1b..49fa4abe1 100644 --- a/src/test/java/de/komoot/photon/nominatim/NominatimConnectorDBTest.java +++ b/src/test/java/de/komoot/photon/nominatim/NominatimConnectorDBTest.java @@ -335,19 +335,13 @@ void testNoCountry() { assertNull(importer.get(place).getCountryCode()); } - - /** - * Places without a country can be imported. - */ + @Test void testGeometry() { PlacexTestRow place = new PlacexTestRow("building", "yes").name("Oosterbroek Zuivel").country("nl").add(jdbc); - connector.readEntireDatabase(); - assertEquals(1, importer.size()); - - assertNull(importer.get(place).getGeometry()); + assertNotNull(importer.get(place).getGeometry()); } @Test diff --git a/src/test/java/de/komoot/photon/nominatim/testdb/PlacexTestRow.java b/src/test/java/de/komoot/photon/nominatim/testdb/PlacexTestRow.java index 36ffc68e7..92dd0d134 100644 --- a/src/test/java/de/komoot/photon/nominatim/testdb/PlacexTestRow.java +++ b/src/test/java/de/komoot/photon/nominatim/testdb/PlacexTestRow.java @@ -114,9 +114,9 @@ public PlacexTestRow parent(PlacexTestRow row) { public PlacexTestRow add(JdbcTemplate jdbc) { jdbc.update("INSERT INTO placex (place_id, parent_place_id, osm_type, osm_id, class, type, rank_search, rank_address," - + " centroid, name, country_code, importance, address, indexed_status)" - + "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ? FORMAT JSON, ?, ?, ? FORMAT JSON, 0)", - placeId, parentPlaceId, osmType, osmId, key, value, rankSearch, rankAddress, centroid, + + " centroid, geometry, name, country_code, importance, address, indexed_status)" + + "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ? FORMAT JSON, ?, ?, ? FORMAT JSON, 0)", + placeId, parentPlaceId, osmType, osmId, key, value, rankSearch, rankAddress, centroid, geometry, asJson(names), countryCode, importance, asJson(address)); return this; From cef09d7d967f0c3d708a225640b71a783d431871 Mon Sep 17 00:00:00 2001 From: Your Name <you@example.com> Date: Thu, 5 Sep 2024 21:06:37 +0200 Subject: [PATCH 09/18] bugfix: typo --- src/test/java/de/komoot/photon/ApiIntegrationTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/de/komoot/photon/ApiIntegrationTest.java b/src/test/java/de/komoot/photon/ApiIntegrationTest.java index 00f5200f9..5a89d0758 100644 --- a/src/test/java/de/komoot/photon/ApiIntegrationTest.java +++ b/src/test/java/de/komoot/photon/ApiIntegrationTest.java @@ -166,7 +166,7 @@ public void testApiStatus() throws Exception { @Test void testSearchAndGetPolygon() throws Exception { - App.main(new String[]{"-cluster", TEST_CLUSTER_NAME, "-listen-port", Integer.toString(LISTEN_PORT), "-transport-addresses", "127.0.0.1", "-use-geometry-column"}); + App.main(new String[]{"-cluster", TEST_CLUSTER_NAME, "-listen-port", Integer.toString(LISTEN_PORT), "-transport-addresses", "127.0.0.1", "-import-geometry-column"}); awaitInitialization(); HttpURLConnection connection = (HttpURLConnection) new URL("http://127.0.0.1:" + port() + "/api?q=berlin&limit=1").openConnection(); JSONObject json = new JSONObject( From 161c9674863b6010e37688984444e503eec63cff Mon Sep 17 00:00:00 2001 From: Your Name <you@example.com> Date: Sat, 16 Nov 2024 14:42:43 +0100 Subject: [PATCH 10/18] fix: OpenSearch implementation of Geometry column --- .../src/test/java/de/komoot/photon/ESBaseTester.java | 10 +++++++--- .../src/test/java/de/komoot/photon/ServerTest.java | 3 ++- .../komoot/photon/opensearch/StructuredQueryTest.java | 2 +- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/app/opensearch/src/test/java/de/komoot/photon/ESBaseTester.java b/app/opensearch/src/test/java/de/komoot/photon/ESBaseTester.java index 572c747cd..4a722f101 100644 --- a/app/opensearch/src/test/java/de/komoot/photon/ESBaseTester.java +++ b/app/opensearch/src/test/java/de/komoot/photon/ESBaseTester.java @@ -45,13 +45,17 @@ protected PhotonResult getById(String id) { } public void setUpES() throws IOException { - setUpES(dataDirectory, "en"); + setUpES(dataDirectory, false,"en"); } - public void setUpES(Path testDirectory, String... languages) throws IOException { + public void setUpESWithPolygons() throws IOException { + setUpES(dataDirectory, true,"en"); + } + + public void setUpES(Path testDirectory, boolean supportPolygons, String... languages) throws IOException { server = new OpenSearchTestServer(testDirectory.toString()); server.startTestServer(TEST_CLUSTER_NAME); - server.recreateIndex(languages, new Date(), true, true); + server.recreateIndex(languages, new Date(), true, supportPolygons); server.refreshIndexes(); } diff --git a/app/opensearch/src/test/java/de/komoot/photon/ServerTest.java b/app/opensearch/src/test/java/de/komoot/photon/ServerTest.java index ac14173f0..abace3cf6 100644 --- a/app/opensearch/src/test/java/de/komoot/photon/ServerTest.java +++ b/app/opensearch/src/test/java/de/komoot/photon/ServerTest.java @@ -16,7 +16,8 @@ void testSaveAndLoadFromDatabase() throws IOException { Date now = new Date(); DatabaseProperties prop = new DatabaseProperties(new String[]{"en", "de", "fr"}, now, - false); + false, + false); getServer().saveToDatabase(prop); prop = getServer().loadFromDatabase(); diff --git a/app/opensearch/src/test/java/de/komoot/photon/opensearch/StructuredQueryTest.java b/app/opensearch/src/test/java/de/komoot/photon/opensearch/StructuredQueryTest.java index b4c96cafc..335e7eb46 100644 --- a/app/opensearch/src/test/java/de/komoot/photon/opensearch/StructuredQueryTest.java +++ b/app/opensearch/src/test/java/de/komoot/photon/opensearch/StructuredQueryTest.java @@ -45,7 +45,7 @@ private static int getRank(AddressType type) { @BeforeEach void setUp() throws Exception { - setUpES(instanceTestDirectory, LANGUAGE, "de", "fr"); + setUpES(instanceTestDirectory, false, LANGUAGE, "de", "fr"); Importer instance = makeImporter(); var country = new PhotonDoc(0, "R", 0, "place", "country") From 583d575046a04493671342a62265d386d586ac51 Mon Sep 17 00:00:00 2001 From: Your Name <you@example.com> Date: Sat, 16 Nov 2024 14:57:13 +0100 Subject: [PATCH 11/18] fix: remarks of lonvia --- src/main/java/de/komoot/photon/App.java | 4 +-- .../komoot/photon/SearchRequestHandler.java | 17 +++++------- .../StructuredSearchRequestHandler.java | 11 +++++++- .../photon/nominatim/NominatimImporter.java | 27 +++++++++++++------ .../photon/nominatim/NominatimUpdater.java | 10 +++++-- .../photon/query/PhotonRequestFactory.java | 17 +++++++++++- 6 files changed, 62 insertions(+), 24 deletions(-) diff --git a/src/main/java/de/komoot/photon/App.java b/src/main/java/de/komoot/photon/App.java index 8bf3b2663..5258bf69c 100644 --- a/src/main/java/de/komoot/photon/App.java +++ b/src/main/java/de/komoot/photon/App.java @@ -276,8 +276,8 @@ private static void startApi(CommandLineArgs args, Server server) throws IOExcep if (dbProperties.getSupportStructuredQueries()) { StructuredSearchHandler structured = server.createStructuredSearchHandler(langs, args.getQueryTimeout()); - get("structured", new StructuredSearchRequestHandler("structured", structured, langs, args.getDefaultLanguage(), args.getMaxResults())); - get("structured/", new StructuredSearchRequestHandler("structured/", structured, langs, args.getDefaultLanguage(), args.getMaxResults())); + get("structured", new StructuredSearchRequestHandler("structured", structured, langs, args.getDefaultLanguage(), args.getMaxResults(), dbProperties.getSupportPolygons())); + get("structured/", new StructuredSearchRequestHandler("structured/", structured, langs, args.getDefaultLanguage(), args.getMaxResults(), dbProperties.getSupportPolygons())); } ReverseHandler reverseHandler = server.createReverseHandler(args.getQueryTimeout()); diff --git a/src/main/java/de/komoot/photon/SearchRequestHandler.java b/src/main/java/de/komoot/photon/SearchRequestHandler.java index 0a96dd41e..82dbaa85d 100644 --- a/src/main/java/de/komoot/photon/SearchRequestHandler.java +++ b/src/main/java/de/komoot/photon/SearchRequestHandler.java @@ -43,6 +43,12 @@ public String handle(Request request, Response response) throws BadRequestExcept throw halt(e.getHttpStatus(), json.toString()); } + if (!supportPolygons && (photonRequest.isPolygonRequest() && photonRequest.getReturnPolygon())) { + JSONObject json = new JSONObject(); + json.put("message", "You're requesting a polygon, but polygons are not imported!"); + throw halt(400, json.toString()); + } + List<PhotonResult> results = requestHandler.search(photonRequest); // Further filtering @@ -58,15 +64,6 @@ public String handle(Request request, Response response) throws BadRequestExcept debugInfo = requestHandler.dumpQuery(photonRequest); } - boolean returnPolygon = supportPolygons; - if (photonRequest.isPolygonRequest()) { - returnPolygon = photonRequest.getReturnPolygon(); - } - - if (!supportPolygons && (photonRequest.isPolygonRequest() && photonRequest.getReturnPolygon())) { - throw new BadRequestException(400, "You're requesting a polygon, but polygons are not imported!"); - } - - return new GeocodeJsonFormatter(photonRequest.getDebug(), photonRequest.getLanguage(), returnPolygon).convert(results, debugInfo); + return new GeocodeJsonFormatter(photonRequest.getDebug(), photonRequest.getLanguage(), photonRequest.getReturnPolygon()).convert(results, debugInfo); } } \ No newline at end of file diff --git a/src/main/java/de/komoot/photon/StructuredSearchRequestHandler.java b/src/main/java/de/komoot/photon/StructuredSearchRequestHandler.java index 293c3dc5f..6b066acc6 100644 --- a/src/main/java/de/komoot/photon/StructuredSearchRequestHandler.java +++ b/src/main/java/de/komoot/photon/StructuredSearchRequestHandler.java @@ -17,12 +17,14 @@ public class StructuredSearchRequestHandler extends RouteImpl { private final PhotonRequestFactory photonRequestFactory; private final StructuredSearchHandler requestHandler; + private final boolean supportPolygons; - StructuredSearchRequestHandler(String path, StructuredSearchHandler dbHandler, String[] languages, String defaultLanguage, int maxResults) { + StructuredSearchRequestHandler(String path, StructuredSearchHandler dbHandler, String[] languages, String defaultLanguage, int maxResults, boolean supportPolygons) { super(path); List<String> supportedLanguages = Arrays.asList(languages); this.photonRequestFactory = new PhotonRequestFactory(supportedLanguages, defaultLanguage, maxResults); this.requestHandler = dbHandler; + this.supportPolygons = supportPolygons; } @Override @@ -36,6 +38,12 @@ public String handle(Request request, Response response) { throw halt(e.getHttpStatus(), json.toString()); } + if (!supportPolygons && (photonRequest.isPolygonRequest() && photonRequest.getReturnPolygon())) { + JSONObject json = new JSONObject(); + json.put("message", "You're requesting a polygon, but polygons are not imported!"); + throw halt(400, json.toString()); + } + List<PhotonResult> results = requestHandler.search(photonRequest); // Further filtering @@ -46,6 +54,7 @@ public String handle(Request request, Response response) { results = results.subList(0, photonRequest.getLimit()); } + String debugInfo = null; /* if (photonRequest.getDebug()) { debugInfo = requestHandler.dumpQuery(photonRequest); diff --git a/src/main/java/de/komoot/photon/nominatim/NominatimImporter.java b/src/main/java/de/komoot/photon/nominatim/NominatimImporter.java index d7ba5990f..be661adc9 100644 --- a/src/main/java/de/komoot/photon/nominatim/NominatimImporter.java +++ b/src/main/java/de/komoot/photon/nominatim/NominatimImporter.java @@ -56,11 +56,17 @@ public void readCountry(String countryCode, ImportThread importThread) { addressCache.loadCountryAddresses(template, dbutils, countryCode); final PlaceRowMapper placeRowMapper = new PlaceRowMapper(dbutils, useGeometryColumn); + String query = "SELECT place_id, osm_type, osm_id, class, type, name, postcode," + + " address, extratags, ST_Envelope(geometry) AS bbox, parent_place_id," + + " linked_place_id, rank_address, rank_search, importance, country_code, centroid, "; + + if (useGeometryColumn) { + query += "geometry,"; + } + // First read ranks below 30, independent places template.query( - "SELECT place_id, osm_type, osm_id, class, type, name, postcode," + - " address, extratags, ST_Envelope(geometry) AS bbox, parent_place_id," + - " linked_place_id, rank_address, rank_search, importance, country_code, centroid, geometry," + + query + dbutils.jsonArrayFromSelect( "address_place_id", "FROM place_addressline pa " + @@ -88,12 +94,17 @@ public void readCountry(String countryCode, ImportThread importThread) { }); // Next get all POIs/housenumbers. + query = "SELECT p.place_id, p.osm_type, p.osm_id, p.class, p.type, p.name, p.postcode," + + " p.address, p.extratags, ST_Envelope(p.geometry) AS bbox, p.parent_place_id," + + " p.linked_place_id, p.rank_address, p.rank_search, p.importance, p.country_code, p.centroid, " + + " parent.class as parent_class, parent.type as parent_type," + + " parent.rank_address as parent_rank_address, parent.name as parent_name, "; + + if (useGeometryColumn) { + query += "p.geometry, "; + } template.query( - "SELECT p.place_id, p.osm_type, p.osm_id, p.class, p.type, p.name, p.postcode," + - " p.address, p.extratags, ST_Envelope(p.geometry) AS bbox, p.parent_place_id," + - " p.linked_place_id, p.rank_address, p.rank_search, p.importance, p.country_code, p.centroid, p.geometry," + - " parent.class as parent_class, parent.type as parent_type," + - " parent.rank_address as parent_rank_address, parent.name as parent_name, " + + query + dbutils.jsonArrayFromSelect( "address_place_id", "FROM place_addressline pa " + diff --git a/src/main/java/de/komoot/photon/nominatim/NominatimUpdater.java b/src/main/java/de/komoot/photon/nominatim/NominatimUpdater.java index 9472974fe..25f06d023 100644 --- a/src/main/java/de/komoot/photon/nominatim/NominatimUpdater.java +++ b/src/main/java/de/komoot/photon/nominatim/NominatimUpdater.java @@ -17,7 +17,7 @@ public class NominatimUpdater extends NominatimConnector { private static final org.slf4j.Logger LOGGER = org.slf4j.LoggerFactory.getLogger(NominatimUpdater.class); - private static final String SELECT_COLS_PLACEX = "SELECT place_id, osm_type, osm_id, class, type, name, postcode, address, extratags, ST_Envelope(geometry) AS bbox, parent_place_id, linked_place_id, rank_address, rank_search, importance, country_code, centroid, geometry"; + private static final String SELECT_COLS_PLACEX = "SELECT place_id, osm_type, osm_id, class, type, name, postcode, address, extratags, ST_Envelope(geometry) AS bbox, parent_place_id, linked_place_id, rank_address, rank_search, importance, country_code, centroid"; private static final String SELECT_COLS_ADDRESS = "SELECT p.name, p.class, p.type, p.rank_address"; private static final String SELECT_OSMLINE_OLD_STYLE = "SELECT place_id, osm_id, parent_place_id, startnumber, endnumber, interpolationtype, postcode, country_code, linegeo"; private static final String SELECT_OSMLINE_NEW_STYLE = "SELECT place_id, osm_id, parent_place_id, startnumber, endnumber, step, postcode, country_code, linegeo"; @@ -267,8 +267,14 @@ private List<UpdateRow> getPlaces(String table) { public List<PhotonDoc> getByPlaceId(long placeId) { + String query = SELECT_COLS_PLACEX + " FROM placex WHERE place_id = ? and indexed_status = 0"; + + if (useGeometryColumn) { + query += ", geometry"; + } + List<NominatimResult> result = template.query( - SELECT_COLS_PLACEX + " FROM placex WHERE place_id = ? and indexed_status = 0", + query, placeToNominatimResult, placeId); return result.isEmpty() ? null : result.get(0).getDocsWithHousenumber(); diff --git a/src/main/java/de/komoot/photon/query/PhotonRequestFactory.java b/src/main/java/de/komoot/photon/query/PhotonRequestFactory.java index d1ea41036..80dbc41b7 100644 --- a/src/main/java/de/komoot/photon/query/PhotonRequestFactory.java +++ b/src/main/java/de/komoot/photon/query/PhotonRequestFactory.java @@ -114,7 +114,7 @@ private void addCommonParameters(Request webRequest, PhotonRequestBase request) if (webRequest.queryParams("polygon") != null) { request.setPolygonRequest(true); - request.setReturnPolygon(Boolean.parseBoolean(webRequest.queryParams("polygon"))); + request.setReturnPolygon(parseBoolean(webRequest, webRequest.queryParams("polygon"))); } } @@ -151,4 +151,19 @@ private Double parseDouble(Request webRequest, String param) throws BadRequestEx return outVal; } + + private Boolean parseBoolean(Request webRequest, String param) throws BadRequestException { + Boolean booleanVal = null; + String value = webRequest.queryParams(param); + + if (value != null && !value.isEmpty()) { + try { + booleanVal = Boolean.valueOf(value); + } catch (NumberFormatException e) { + throw new BadRequestException(400, String.format("Invalid parameter '%s': must be a boolean", param)); + } + } + + return booleanVal; + } } From 0abb0b4007f5dceffde0e6ceeb1bc37d8f3bbbb7 Mon Sep 17 00:00:00 2001 From: Your Name <you@example.com> Date: Sat, 16 Nov 2024 15:13:59 +0100 Subject: [PATCH 12/18] fix: unit tests --- .../java/de/komoot/photon/nominatim/NominatimUpdater.java | 4 +++- .../java/de/komoot/photon/query/PhotonRequestFactory.java | 8 ++------ .../komoot/photon/nominatim/NominatimUpdaterDBTest.java | 2 +- 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/src/main/java/de/komoot/photon/nominatim/NominatimUpdater.java b/src/main/java/de/komoot/photon/nominatim/NominatimUpdater.java index 25f06d023..1c591656a 100644 --- a/src/main/java/de/komoot/photon/nominatim/NominatimUpdater.java +++ b/src/main/java/de/komoot/photon/nominatim/NominatimUpdater.java @@ -267,12 +267,14 @@ private List<UpdateRow> getPlaces(String table) { public List<PhotonDoc> getByPlaceId(long placeId) { - String query = SELECT_COLS_PLACEX + " FROM placex WHERE place_id = ? and indexed_status = 0"; + String query = SELECT_COLS_PLACEX; if (useGeometryColumn) { query += ", geometry"; } + query += " FROM placex WHERE place_id = ? and indexed_status = 0"; + List<NominatimResult> result = template.query( query, placeToNominatimResult, placeId); diff --git a/src/main/java/de/komoot/photon/query/PhotonRequestFactory.java b/src/main/java/de/komoot/photon/query/PhotonRequestFactory.java index 80dbc41b7..434de8b29 100644 --- a/src/main/java/de/komoot/photon/query/PhotonRequestFactory.java +++ b/src/main/java/de/komoot/photon/query/PhotonRequestFactory.java @@ -114,7 +114,7 @@ private void addCommonParameters(Request webRequest, PhotonRequestBase request) if (webRequest.queryParams("polygon") != null) { request.setPolygonRequest(true); - request.setReturnPolygon(parseBoolean(webRequest, webRequest.queryParams("polygon"))); + request.setReturnPolygon(parseBoolean(webRequest, "polygon")); } } @@ -157,11 +157,7 @@ private Boolean parseBoolean(Request webRequest, String param) throws BadRequest String value = webRequest.queryParams(param); if (value != null && !value.isEmpty()) { - try { - booleanVal = Boolean.valueOf(value); - } catch (NumberFormatException e) { - throw new BadRequestException(400, String.format("Invalid parameter '%s': must be a boolean", param)); - } + booleanVal = Boolean.valueOf(value); } return booleanVal; diff --git a/src/test/java/de/komoot/photon/nominatim/NominatimUpdaterDBTest.java b/src/test/java/de/komoot/photon/nominatim/NominatimUpdaterDBTest.java index 354151075..228fb476f 100644 --- a/src/test/java/de/komoot/photon/nominatim/NominatimUpdaterDBTest.java +++ b/src/test/java/de/komoot/photon/nominatim/NominatimUpdaterDBTest.java @@ -29,7 +29,7 @@ void setup() { .build(); - connector = new NominatimUpdater(null, 0, null, null, null, new H2DataAdapter(), true); + connector = new NominatimUpdater(null, 0, null, null, null, new H2DataAdapter(), false); updater = new CollectingUpdater(); connector.setUpdater(updater); From db58e9a33a7d456319bc4f4ddfa84ec664db4336 Mon Sep 17 00:00:00 2001 From: red-fenix <red-fenix@xor.nu> Date: Sat, 14 Dec 2024 12:00:31 +0100 Subject: [PATCH 13/18] fix: opensearch implementation --- .../photon/elasticsearch/ElasticResult.java | 5 +++ .../elasticsearch/ElasticGetIdResult.java | 4 +++ app/opensearch/build.gradle | 2 +- .../main/java/de/komoot/photon/Server.java | 5 ++- .../photon/opensearch/OpenSearchResult.java | 8 ++++- .../OpenSearchResultDeserializer.java | 17 ++++++++- .../opensearch/PhotonDocSerializer.java | 35 +++++++++++++++++-- .../java/de/komoot/photon/ESBaseTester.java | 8 +++-- .../photon/opensearch/ImporterTest.java | 8 +++-- src/main/java/de/komoot/photon/PhotonDoc.java | 2 ++ .../photon/searcher/GeocodeJsonFormatter.java | 18 +++++++--- .../komoot/photon/searcher/PhotonResult.java | 5 ++- .../de/komoot/photon/ApiIntegrationTest.java | 4 +-- .../komoot/photon/query/QueryPolygonTest.java | 11 +++--- .../photon/searcher/MockPhotonResult.java | 11 ++++++ 15 files changed, 120 insertions(+), 23 deletions(-) diff --git a/app/es_embedded/src/main/java/de/komoot/photon/elasticsearch/ElasticResult.java b/app/es_embedded/src/main/java/de/komoot/photon/elasticsearch/ElasticResult.java index 55b8ed5cf..4e2d6e588 100644 --- a/app/es_embedded/src/main/java/de/komoot/photon/elasticsearch/ElasticResult.java +++ b/app/es_embedded/src/main/java/de/komoot/photon/elasticsearch/ElasticResult.java @@ -66,6 +66,11 @@ public double[] getCoordinates() { return new double[]{coordinate.get(Constants.LON), coordinate.get(Constants.LAT)}; } + @Override + public double[][] getGeometry() { + return null; + } + @Override public double[] getExtent() { final Map<String, Object> extent = (Map<String, Object>) result.getSource().get("extent"); diff --git a/app/es_embedded/src/test/java/de/komoot/photon/elasticsearch/ElasticGetIdResult.java b/app/es_embedded/src/test/java/de/komoot/photon/elasticsearch/ElasticGetIdResult.java index a7579cd40..891c062b5 100644 --- a/app/es_embedded/src/test/java/de/komoot/photon/elasticsearch/ElasticGetIdResult.java +++ b/app/es_embedded/src/test/java/de/komoot/photon/elasticsearch/ElasticGetIdResult.java @@ -34,6 +34,10 @@ public double[] getCoordinates() { throw new NotImplementedException(); } + public double[][] getGeometry() { + throw new NotImplementedException(); + } + @Override public double[] getExtent() { throw new NotImplementedException(); diff --git a/app/opensearch/build.gradle b/app/opensearch/build.gradle index 344457b36..2be69292f 100644 --- a/app/opensearch/build.gradle +++ b/app/opensearch/build.gradle @@ -22,7 +22,7 @@ dependencies { implementation 'org.apache.httpcomponents.client5:httpclient5:5.4.1' implementation 'com.fasterxml.jackson.core:jackson-databind:2.18.2' - implementation('org.codelibs.opensearch:opensearch-runner:2.18.0.0') { + implementation('org.codelibs.opensearch:opensearch-runner:2.18.0.1') { exclude(module: 'repository-url') exclude(module: 'reindex-client') exclude(module: 'rank-eval-client') diff --git a/app/opensearch/src/main/java/de/komoot/photon/Server.java b/app/opensearch/src/main/java/de/komoot/photon/Server.java index 485a024df..554ca6600 100644 --- a/app/opensearch/src/main/java/de/komoot/photon/Server.java +++ b/app/opensearch/src/main/java/de/komoot/photon/Server.java @@ -35,7 +35,9 @@ public class Server { public static final String OPENSEARCH_MODULES = "org.opensearch.transport.Netty4Plugin," - + "org.opensearch.analysis.common.CommonAnalysisPlugin"; + + "org.opensearch.analysis.common.CommonAnalysisPlugin," + + "org.opensearch.geo.GeoModulePlugin," + + "org.opensearch.geospatial.plugin.GeospatialPlugin"; protected OpenSearchClient client; private OpenSearchRunner runner = null; @@ -87,6 +89,7 @@ private HttpHost[] startInternal(String clusterName) { .clusterName(clusterName) .numOfNode(1) .moduleTypes(OPENSEARCH_MODULES) + .pluginTypes(OPENSEARCH_MODULES) ); runner.ensureYellow(); diff --git a/app/opensearch/src/main/java/de/komoot/photon/opensearch/OpenSearchResult.java b/app/opensearch/src/main/java/de/komoot/photon/opensearch/OpenSearchResult.java index 3cbf95069..2a2fab4bb 100644 --- a/app/opensearch/src/main/java/de/komoot/photon/opensearch/OpenSearchResult.java +++ b/app/opensearch/src/main/java/de/komoot/photon/opensearch/OpenSearchResult.java @@ -11,14 +11,16 @@ public class OpenSearchResult implements PhotonResult { private double score = 0.0; private final double[] extent; private final double[] coordinates; + private final double[][] geometry; private final Map<String, Object> infos; private final Map<String, Map<String, String>> localeTags; - OpenSearchResult(double[] extent, double[] coordinates, Map<String, Object> infos, Map<String, Map<String, String>> localeTags) { + OpenSearchResult(double[] extent, double[] coordinates, Map<String, Object> infos, Map<String, Map<String, String>> localeTags, double[][] geometry) { this.extent = extent; this.coordinates = coordinates; this.infos = infos; this.localeTags = localeTags; + this.geometry = geometry; } public OpenSearchResult setScore(double score) { @@ -61,6 +63,10 @@ public double[] getCoordinates() { return coordinates; } + public double[][] getGeometry() { + return geometry; + } + @Override public double[] getExtent() { return extent; diff --git a/app/opensearch/src/main/java/de/komoot/photon/opensearch/OpenSearchResultDeserializer.java b/app/opensearch/src/main/java/de/komoot/photon/opensearch/OpenSearchResultDeserializer.java index 1573aa393..a51f24e87 100644 --- a/app/opensearch/src/main/java/de/komoot/photon/opensearch/OpenSearchResultDeserializer.java +++ b/app/opensearch/src/main/java/de/komoot/photon/opensearch/OpenSearchResultDeserializer.java @@ -3,6 +3,7 @@ import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.databind.DeserializationContext; import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.deser.std.StdDeserializer; import com.fasterxml.jackson.databind.node.ArrayNode; import com.fasterxml.jackson.databind.node.ObjectNode; @@ -28,6 +29,7 @@ public OpenSearchResult deserialize(JsonParser p, DeserializationContext ctxt) t final Map<String, Object> tags = new HashMap<>(); final Map<String, Map<String, String>> localeTags = new HashMap<>(); + double[][] geometry = extractGeometry((ObjectNode) node.get("geometry")); var fields = node.fields(); while (fields.hasNext()) { @@ -55,7 +57,7 @@ public OpenSearchResult deserialize(JsonParser p, DeserializationContext ctxt) t } } - return new OpenSearchResult(extent, coordinates, tags, localeTags); + return new OpenSearchResult(extent, coordinates, tags, localeTags, geometry); } private double[] extractExtent(ObjectNode node) { @@ -79,4 +81,17 @@ private double[] extractCoordinate(ObjectNode node) { return new double[]{node.get(Constants.LON).doubleValue(), node.get(Constants.LAT).doubleValue()}; } + private double[][] extractGeometry(ObjectNode node) { + if (node == null) { + return PhotonResult.INVALID_GEOMETRY; + } + + double[][] coordinates = new double[node.get("coordinates").get(0).size()][]; + for(int i=0; i<node.get("coordinates").get(0).size(); i++) { + double[] coordinate = new double[] {node.get("coordinates").get(0).get(i).get(0).doubleValue(), node.get("coordinates").get(0).get(i).get(1).doubleValue()}; + coordinates[i] = coordinate; + } + + return coordinates; + } } diff --git a/app/opensearch/src/main/java/de/komoot/photon/opensearch/PhotonDocSerializer.java b/app/opensearch/src/main/java/de/komoot/photon/opensearch/PhotonDocSerializer.java index 3c9792593..5710d868a 100644 --- a/app/opensearch/src/main/java/de/komoot/photon/opensearch/PhotonDocSerializer.java +++ b/app/opensearch/src/main/java/de/komoot/photon/opensearch/PhotonDocSerializer.java @@ -6,10 +6,12 @@ import de.komoot.photon.Constants; import de.komoot.photon.PhotonDoc; import de.komoot.photon.Utils; +import org.locationtech.jts.geom.Coordinate; import org.locationtech.jts.geom.Envelope; import org.locationtech.jts.io.geojson.GeoJsonWriter; import java.io.IOException; +import java.io.StringWriter; import java.util.HashMap; import java.util.HashSet; import java.util.Map; @@ -50,8 +52,37 @@ public void serialize(PhotonDoc value, JsonGenerator gen, SerializerProvider pro } if (value.getGeometry() != null) { - GeoJsonWriter g = new GeoJsonWriter(); - gen.writeObjectField("geometry", g.write(value.getGeometry())); +// gen.writeStringField("geometry", g.write(value.getGeometry())); + + gen.writeObjectFieldStart("geometry"); + gen.writeStringField("type", "Polygon"); + + gen.writeArrayFieldStart("coordinates"); + + gen.writeStartArray(); + + for (Coordinate c: value.getGeometry().getCoordinates()) { + gen.writeStartArray(); + gen.writeNumber(c.x); + gen.writeNumber(c.y); + gen.writeEndArray(); + } + gen.writeEndArray(); + +// gen.writeStartArray(); +// gen.writeNumber(bbox.getMaxX()); +// gen.writeNumber(bbox.getMinY()); +// gen.writeEndArray(); + + gen.writeEndArray(); + gen.writeEndObject(); + +// gen.writeObjectFieldStart("crs"); +// gen.writeStringField("type", "name"); +// gen.writeObjectFieldStart("properties"); +// gen.writeStringField("name", "EPSG:0"); +// gen.writeEndObject(); +// gen.writeEndObject(); } if (value.getHouseNumber() != null) { diff --git a/app/opensearch/src/test/java/de/komoot/photon/ESBaseTester.java b/app/opensearch/src/test/java/de/komoot/photon/ESBaseTester.java index 4a722f101..ff2652ce1 100644 --- a/app/opensearch/src/test/java/de/komoot/photon/ESBaseTester.java +++ b/app/opensearch/src/test/java/de/komoot/photon/ESBaseTester.java @@ -9,12 +9,15 @@ import org.locationtech.jts.geom.Coordinate; import org.locationtech.jts.geom.GeometryFactory; import org.locationtech.jts.geom.PrecisionModel; +import org.locationtech.jts.io.ParseException; +import org.locationtech.jts.io.WKTReader; import java.io.IOException; import java.nio.file.Path; import java.util.Collections; import java.util.Date; + public class ESBaseTester { public static final String TEST_CLUSTER_NAME = "photon-test"; protected static final GeometryFactory FACTORY = new GeometryFactory(new PrecisionModel(), 4326); @@ -24,11 +27,12 @@ public class ESBaseTester { private OpenSearchTestServer server; - protected PhotonDoc createDoc(double lon, double lat, int id, int osmId, String key, String value) { + protected PhotonDoc createDoc(double lon, double lat, int id, int osmId, String key, String value) throws ParseException { final var location = FACTORY.createPoint(new Coordinate(lon, lat)); return new PhotonDoc(id, "W", osmId, key, value) .names(Collections.singletonMap("name", "berlin")) - .centroid(location); + .centroid(location) + .geometry(new WKTReader().read("POLYGON ((6.4440619 52.1969454, 6.4441094 52.1969158, 6.4441408 52.1969347, 6.4441138 52.1969516, 6.4440933 52.1969643, 6.4440619 52.1969454))")); } @AfterEach diff --git a/app/opensearch/src/test/java/de/komoot/photon/opensearch/ImporterTest.java b/app/opensearch/src/test/java/de/komoot/photon/opensearch/ImporterTest.java index f1b2a8687..221efc7fc 100644 --- a/app/opensearch/src/test/java/de/komoot/photon/opensearch/ImporterTest.java +++ b/app/opensearch/src/test/java/de/komoot/photon/opensearch/ImporterTest.java @@ -6,6 +6,8 @@ import de.komoot.photon.searcher.PhotonResult; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.locationtech.jts.io.ParseException; +import org.locationtech.jts.io.WKTReader; import java.io.IOException; import java.util.Collections; @@ -18,14 +20,14 @@ class ImporterTest extends ESBaseTester { @BeforeEach public void setUp() throws IOException { - setUpES(); + setUpESWithPolygons(); } @Test - void testAddSimpleDoc() { + void testAddSimpleDoc() throws ParseException { Importer instance = makeImporterWithExtra(""); - instance.add(new PhotonDoc(1234, "N", 1000, "place", "city") + instance.add(new PhotonDoc(1234, "N", 1000, "place", "city", new WKTReader().read("POLYGON ((1 1, 1 2, 2 1, 1 1))")) .extraTags(Collections.singletonMap("maxspeed", "100")), 0); instance.finish(); diff --git a/src/main/java/de/komoot/photon/PhotonDoc.java b/src/main/java/de/komoot/photon/PhotonDoc.java index 2114cd0e3..cbf1bc606 100644 --- a/src/main/java/de/komoot/photon/PhotonDoc.java +++ b/src/main/java/de/komoot/photon/PhotonDoc.java @@ -6,6 +6,7 @@ import org.locationtech.jts.geom.Point; import de.komoot.photon.nominatim.model.AddressType; import org.locationtech.jts.geom.Polygon; +import org.locationtech.spatial4j.shape.ShapeFactory; import org.slf4j.Logger; import java.util.*; @@ -357,6 +358,7 @@ public String getHouseNumber() { public Point getCentroid() { return this.centroid; } + public Geometry getGeometry() { return this.geometry; } diff --git a/src/main/java/de/komoot/photon/searcher/GeocodeJsonFormatter.java b/src/main/java/de/komoot/photon/searcher/GeocodeJsonFormatter.java index 243571870..f7568852a 100644 --- a/src/main/java/de/komoot/photon/searcher/GeocodeJsonFormatter.java +++ b/src/main/java/de/komoot/photon/searcher/GeocodeJsonFormatter.java @@ -28,11 +28,19 @@ public String convert(List<PhotonResult> results, String debugInfo) { final JSONArray features = new JSONArray(results.size()); for (PhotonResult result : results) { - if (useGeometryColumn && result.get("geometry") != null) { - features.put(new JSONObject() - .put("type", "Feature") - .put("properties", getResultProperties(result)) - .put("geometry", result.get("geometry"))); + if (useGeometryColumn && (result.get("geometry") != null || result.getGeometry() != null)) { + if (result.get("geometry") != null) { + features.put(new JSONObject() + .put("type", "Feature") + .put("properties", getResultProperties(result)) + .put("geometry", result.get("geometry"))); + } else { + var geom = new JSONObject().put("type", "Polygon").put("coordinates", result.getGeometry()); + features.put(new JSONObject() + .put("type", "Feature") + .put("properties", getResultProperties(result)) + .put("geometry", geom)); + } } else { final double[] coordinates = result.getCoordinates(); diff --git a/src/main/java/de/komoot/photon/searcher/PhotonResult.java b/src/main/java/de/komoot/photon/searcher/PhotonResult.java index d14e20cce..5064a96a2 100644 --- a/src/main/java/de/komoot/photon/searcher/PhotonResult.java +++ b/src/main/java/de/komoot/photon/searcher/PhotonResult.java @@ -9,6 +9,7 @@ */ public interface PhotonResult { final double[] INVALID_COORDINATES = new double[]{0, 0}; + final double[][] INVALID_GEOMETRY = new double[][]{{0, 0}}; /** * Get the value for the given field. @@ -24,7 +25,9 @@ public interface PhotonResult { Map<String, String> getMap(String key); double[] getCoordinates(); - + + double[][] getGeometry(); + double[] getExtent(); double getScore(); diff --git a/src/test/java/de/komoot/photon/ApiIntegrationTest.java b/src/test/java/de/komoot/photon/ApiIntegrationTest.java index ce9f2132d..f434d2808 100644 --- a/src/test/java/de/komoot/photon/ApiIntegrationTest.java +++ b/src/test/java/de/komoot/photon/ApiIntegrationTest.java @@ -26,7 +26,7 @@ class ApiIntegrationTest extends ESBaseTester { @BeforeEach void setUp() throws Exception { - setUpESWithPolygons(); + setUpES(); Importer instance = makeImporter(); instance.add(createDoc(13.38886, 52.51704, 1000, 1000, "place", "city").importance(0.6), 0); instance.add(createDoc(13.39026, 52.54714, 1001, 1001, "place", "town").importance(0.3), 0); @@ -149,7 +149,7 @@ void testApiStatus() throws Exception { @Test void testSearchAndGetPolygon() throws Exception { - App.main(new String[]{"-cluster", TEST_CLUSTER_NAME, "-listen-port", Integer.toString(LISTEN_PORT), "-transport-addresses", "127.0.0.1", "-import-geometry-column"}); + App.main(new String[]{"-cluster", TEST_CLUSTER_NAME, "-listen-port", Integer.toString(LISTEN_PORT), "-transport-addresses", "127.0.0.1"}); awaitInitialization(); HttpURLConnection connection = (HttpURLConnection) new URL("http://127.0.0.1:" + port() + "/api?q=berlin&limit=1").openConnection(); JSONObject json = new JSONObject( diff --git a/src/test/java/de/komoot/photon/query/QueryPolygonTest.java b/src/test/java/de/komoot/photon/query/QueryPolygonTest.java index 3350d023f..ffd31b197 100644 --- a/src/test/java/de/komoot/photon/query/QueryPolygonTest.java +++ b/src/test/java/de/komoot/photon/query/QueryPolygonTest.java @@ -10,13 +10,10 @@ import org.locationtech.jts.io.WKTReader; import java.io.IOException; -import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; -import static org.junit.jupiter.api.Assertions.assertAll; -import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; /** @@ -53,6 +50,12 @@ void testSearchGetPolygon() throws IOException, ParseException { instance.add(createDoc("name", "Muffle Flu"), 0); instance.finish(); refresh(); - assertNotNull(search("muffle flu").get(0).get("geometry")); + List<PhotonResult> s = search("muffle flu"); + + if (s.get(0).getClass().getName().equals("de.komoot.photon.opensearch.OpenSearchResult")) { + assertNotNull(s.get(0).getGeometry()); + } else { + assertNotNull(s.get(0).get("geometry")); + } } } diff --git a/src/test/java/de/komoot/photon/searcher/MockPhotonResult.java b/src/test/java/de/komoot/photon/searcher/MockPhotonResult.java index 02efcc581..649528cde 100644 --- a/src/test/java/de/komoot/photon/searcher/MockPhotonResult.java +++ b/src/test/java/de/komoot/photon/searcher/MockPhotonResult.java @@ -9,6 +9,7 @@ public class MockPhotonResult implements PhotonResult { final Map<String, Object> data = new HashMap<>(); final double[] coordinates = new double[]{42, 21}; + final double[][] geometry = {{6.4440619,52.1969454},{6.4441094,52.1969158},{6.4441408,52.1969347},{6.4441138,52.1969516},{6.4440933,52.1969643},{6.4440619,52.1969454}}; final double[] extent = new double[]{0, 1, 2, 3}; final Map<String, String> localized = new HashMap<>(); @@ -32,6 +33,16 @@ public double[] getCoordinates() { return coordinates; } + @Override + public double[][] getGeometry() { + return geometry; + } + +// @Override +// public double[][] getGeometry() { +// return geometry; +// } + @Override public double[] getExtent() { return extent; From 17c2c0ddcfecb86785b6d4f0510b478deafd62ef Mon Sep 17 00:00:00 2001 From: red-fenix <red-fenix@xor.nu> Date: Sun, 12 Jan 2025 11:04:55 +0100 Subject: [PATCH 14/18] fix: latest remarks of lonvia --- .../main/java/de/komoot/photon/Server.java | 2 +- .../photon/elasticsearch/ElasticResult.java | 11 ++++++ .../elasticsearch/ElasticGetIdResult.java | 6 ++++ .../main/java/de/komoot/photon/Server.java | 12 +++---- .../de/komoot/photon/opensearch/Importer.java | 2 ++ .../photon/opensearch/OpenSearchResult.java | 10 +++++- .../OpenSearchResultDeserializer.java | 34 ++++++++++++++++--- .../opensearch/PhotonDocSerializer.java | 28 +++++---------- .../java/de/komoot/photon/ESBaseTester.java | 5 ++- .../opensearch/OpenSearchTestServer.java | 3 +- src/main/java/de/komoot/photon/PhotonDoc.java | 2 -- .../komoot/photon/SearchRequestHandler.java | 8 ++--- .../StructuredSearchRequestHandler.java | 4 +-- .../photon/nominatim/NominatimUpdater.java | 4 +-- .../photon/query/PhotonRequestBase.java | 10 +----- .../photon/query/PhotonRequestFactory.java | 15 +++++--- .../photon/searcher/GeocodeJsonFormatter.java | 2 +- .../komoot/photon/searcher/GeometryType.java | 17 ++++++++++ .../komoot/photon/searcher/PhotonResult.java | 2 ++ .../de/komoot/photon/ApiIntegrationTest.java | 27 ++++++++++++++- .../query/PhotonRequestFactoryTest.java | 10 +++++- .../komoot/photon/query/QueryPolygonTest.java | 25 ++++++++++++-- .../photon/searcher/MockPhotonResult.java | 9 ++--- 23 files changed, 177 insertions(+), 71 deletions(-) create mode 100644 src/main/java/de/komoot/photon/searcher/GeometryType.java diff --git a/app/es_embedded/src/main/java/de/komoot/photon/Server.java b/app/es_embedded/src/main/java/de/komoot/photon/Server.java index dfb06f903..22582f22a 100644 --- a/app/es_embedded/src/main/java/de/komoot/photon/Server.java +++ b/app/es_embedded/src/main/java/de/komoot/photon/Server.java @@ -290,7 +290,7 @@ public DatabaseProperties loadFromDatabase() { return new DatabaseProperties(langString == null ? null : langString.split(","), importDateString == null ? null : Date.from(Instant.parse(importDateString)), false, - supportPolygons == null ? null : Boolean.parseBoolean(supportPolygons)); + Boolean.parseBoolean(supportPolygons)); } public Importer createImporter(String[] languages, String[] extraTags) { diff --git a/app/es_embedded/src/main/java/de/komoot/photon/elasticsearch/ElasticResult.java b/app/es_embedded/src/main/java/de/komoot/photon/elasticsearch/ElasticResult.java index 4e2d6e588..9145f303a 100644 --- a/app/es_embedded/src/main/java/de/komoot/photon/elasticsearch/ElasticResult.java +++ b/app/es_embedded/src/main/java/de/komoot/photon/elasticsearch/ElasticResult.java @@ -1,6 +1,7 @@ package de.komoot.photon.elasticsearch; import de.komoot.photon.Constants; +import de.komoot.photon.searcher.GeometryType; import de.komoot.photon.searcher.PhotonResult; import org.elasticsearch.search.SearchHit; import org.slf4j.Logger; @@ -66,8 +67,18 @@ public double[] getCoordinates() { return new double[]{coordinate.get(Constants.LON), coordinate.get(Constants.LAT)}; } + @Override + public GeometryType getGeometryType() { + final Map<String, Object> geometry = (Map<String, Object>) result.getSource().get("geometry"); + + return GeometryType.valueOf((String) geometry.get("type")); + } + @Override public double[][] getGeometry() { + final Map<String, Object> geometry = (Map<String, Object>) result.getSource().get("geometry"); + final List<List<Double>> coords = (List<List<Double>>) geometry.get("coordinates"); + return null; } diff --git a/app/es_embedded/src/test/java/de/komoot/photon/elasticsearch/ElasticGetIdResult.java b/app/es_embedded/src/test/java/de/komoot/photon/elasticsearch/ElasticGetIdResult.java index 891c062b5..73ffbb10d 100644 --- a/app/es_embedded/src/test/java/de/komoot/photon/elasticsearch/ElasticGetIdResult.java +++ b/app/es_embedded/src/test/java/de/komoot/photon/elasticsearch/ElasticGetIdResult.java @@ -1,5 +1,6 @@ package de.komoot.photon.elasticsearch; +import de.komoot.photon.searcher.GeometryType; import de.komoot.photon.searcher.PhotonResult; import org.apache.commons.lang3.NotImplementedException; import org.elasticsearch.action.get.GetResponse; @@ -34,6 +35,11 @@ public double[] getCoordinates() { throw new NotImplementedException(); } + @Override + public GeometryType getGeometryType() { + throw new NotImplementedException(); + } + public double[][] getGeometry() { throw new NotImplementedException(); } diff --git a/app/opensearch/src/main/java/de/komoot/photon/Server.java b/app/opensearch/src/main/java/de/komoot/photon/Server.java index 554ca6600..d753a84ed 100644 --- a/app/opensearch/src/main/java/de/komoot/photon/Server.java +++ b/app/opensearch/src/main/java/de/komoot/photon/Server.java @@ -33,11 +33,11 @@ public class Server { private static final Logger LOGGER = org.slf4j.LoggerFactory.getLogger(Server.class); - public static final String OPENSEARCH_MODULES = - "org.opensearch.transport.Netty4Plugin," - + "org.opensearch.analysis.common.CommonAnalysisPlugin," - + "org.opensearch.geo.GeoModulePlugin," - + "org.opensearch.geospatial.plugin.GeospatialPlugin"; +// public static final String OPENSEARCH_MODULES = +// "org.opensearch.transport.Netty4Plugin," +// + "org.opensearch.analysis.common.CommonAnalysisPlugin," +// + "org.opensearch.geo.GeoModulePlugin," +// + "org.opensearch.geospatial.plugin.GeospatialPlugin"; protected OpenSearchClient client; private OpenSearchRunner runner = null; @@ -88,8 +88,6 @@ private HttpHost[] startInternal(String clusterName) { .basePath(dataDirectory) .clusterName(clusterName) .numOfNode(1) - .moduleTypes(OPENSEARCH_MODULES) - .pluginTypes(OPENSEARCH_MODULES) ); runner.ensureYellow(); diff --git a/app/opensearch/src/main/java/de/komoot/photon/opensearch/Importer.java b/app/opensearch/src/main/java/de/komoot/photon/opensearch/Importer.java index c3e7b31bf..00616e85a 100644 --- a/app/opensearch/src/main/java/de/komoot/photon/opensearch/Importer.java +++ b/app/opensearch/src/main/java/de/komoot/photon/opensearch/Importer.java @@ -4,6 +4,8 @@ import org.opensearch.client.opensearch.OpenSearchClient; import org.opensearch.client.opensearch._types.Time; import org.opensearch.client.opensearch.core.BulkRequest; +import org.opensearch.client.opensearch.core.bulk.BulkOperation; +import org.opensearch.client.opensearch.core.bulk.BulkResponseItem; import org.slf4j.Logger; import java.io.IOException; diff --git a/app/opensearch/src/main/java/de/komoot/photon/opensearch/OpenSearchResult.java b/app/opensearch/src/main/java/de/komoot/photon/opensearch/OpenSearchResult.java index 2a2fab4bb..74a36149f 100644 --- a/app/opensearch/src/main/java/de/komoot/photon/opensearch/OpenSearchResult.java +++ b/app/opensearch/src/main/java/de/komoot/photon/opensearch/OpenSearchResult.java @@ -1,5 +1,6 @@ package de.komoot.photon.opensearch; +import de.komoot.photon.searcher.GeometryType; import de.komoot.photon.searcher.PhotonResult; import org.json.JSONObject; @@ -12,15 +13,17 @@ public class OpenSearchResult implements PhotonResult { private final double[] extent; private final double[] coordinates; private final double[][] geometry; + private final GeometryType geometryType; private final Map<String, Object> infos; private final Map<String, Map<String, String>> localeTags; - OpenSearchResult(double[] extent, double[] coordinates, Map<String, Object> infos, Map<String, Map<String, String>> localeTags, double[][] geometry) { + OpenSearchResult(double[] extent, double[] coordinates, Map<String, Object> infos, Map<String, Map<String, String>> localeTags, double[][] geometry, GeometryType geometryType) { this.extent = extent; this.coordinates = coordinates; this.infos = infos; this.localeTags = localeTags; this.geometry = geometry; + this.geometryType = geometryType; } public OpenSearchResult setScore(double score) { @@ -63,6 +66,11 @@ public double[] getCoordinates() { return coordinates; } + @Override + public GeometryType getGeometryType() { + return geometryType; + } + public double[][] getGeometry() { return geometry; } diff --git a/app/opensearch/src/main/java/de/komoot/photon/opensearch/OpenSearchResultDeserializer.java b/app/opensearch/src/main/java/de/komoot/photon/opensearch/OpenSearchResultDeserializer.java index a51f24e87..26431173c 100644 --- a/app/opensearch/src/main/java/de/komoot/photon/opensearch/OpenSearchResultDeserializer.java +++ b/app/opensearch/src/main/java/de/komoot/photon/opensearch/OpenSearchResultDeserializer.java @@ -3,11 +3,11 @@ import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.databind.DeserializationContext; import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.deser.std.StdDeserializer; import com.fasterxml.jackson.databind.node.ArrayNode; import com.fasterxml.jackson.databind.node.ObjectNode; import de.komoot.photon.Constants; +import de.komoot.photon.searcher.GeometryType; import de.komoot.photon.searcher.PhotonResult; import java.io.IOException; @@ -29,7 +29,19 @@ public OpenSearchResult deserialize(JsonParser p, DeserializationContext ctxt) t final Map<String, Object> tags = new HashMap<>(); final Map<String, Map<String, String>> localeTags = new HashMap<>(); - double[][] geometry = extractGeometry((ObjectNode) node.get("geometry")); + + double[][] geometry = new double[0][]; + GeometryType geometryType = GeometryType.UNKNOWN; + + if (node.get("geometry") != null && node.get("geometry").get("type") != null) { + if (node.get("geometry").get("type").asText().equals("Polygon")) { + geometry = extractPolygon((ObjectNode) node.get("geometry")); + geometryType = GeometryType.POLYGON; + } else if (node.get("geometry").get("type").asText().equals("LineString")) { + geometry = extractLineString((ObjectNode) node.get("geometry")); + geometryType = GeometryType.LINESTRING; + } + } var fields = node.fields(); while (fields.hasNext()) { @@ -57,7 +69,7 @@ public OpenSearchResult deserialize(JsonParser p, DeserializationContext ctxt) t } } - return new OpenSearchResult(extent, coordinates, tags, localeTags, geometry); + return new OpenSearchResult(extent, coordinates, tags, localeTags, geometry, geometryType); } private double[] extractExtent(ObjectNode node) { @@ -81,7 +93,7 @@ private double[] extractCoordinate(ObjectNode node) { return new double[]{node.get(Constants.LON).doubleValue(), node.get(Constants.LAT).doubleValue()}; } - private double[][] extractGeometry(ObjectNode node) { + private double[][] extractPolygon(ObjectNode node) { if (node == null) { return PhotonResult.INVALID_GEOMETRY; } @@ -94,4 +106,18 @@ private double[][] extractGeometry(ObjectNode node) { return coordinates; } + + private double[][] extractLineString(ObjectNode node) { + if (node == null) { + return PhotonResult.INVALID_GEOMETRY; + } + + double[][] coordinates = new double[node.get("coordinates").size()][]; + for(int i=0; i<node.get("coordinates").size(); i++) { + double[] coordinate = new double[] {node.get("coordinates").get(i).get(0).doubleValue(), node.get("coordinates").get(i).get(1).doubleValue()}; + coordinates[i] = coordinate; + } + + return coordinates; + } } diff --git a/app/opensearch/src/main/java/de/komoot/photon/opensearch/PhotonDocSerializer.java b/app/opensearch/src/main/java/de/komoot/photon/opensearch/PhotonDocSerializer.java index 5710d868a..903fd3998 100644 --- a/app/opensearch/src/main/java/de/komoot/photon/opensearch/PhotonDocSerializer.java +++ b/app/opensearch/src/main/java/de/komoot/photon/opensearch/PhotonDocSerializer.java @@ -52,14 +52,14 @@ public void serialize(PhotonDoc value, JsonGenerator gen, SerializerProvider pro } if (value.getGeometry() != null) { -// gen.writeStringField("geometry", g.write(value.getGeometry())); - gen.writeObjectFieldStart("geometry"); - gen.writeStringField("type", "Polygon"); + gen.writeStringField("type", value.getGeometry().getGeometryType()); gen.writeArrayFieldStart("coordinates"); - gen.writeStartArray(); + if (value.getGeometry().getGeometryType().equals("Polygon")) { + gen.writeStartArray(); + } for (Coordinate c: value.getGeometry().getCoordinates()) { gen.writeStartArray(); @@ -67,22 +67,12 @@ public void serialize(PhotonDoc value, JsonGenerator gen, SerializerProvider pro gen.writeNumber(c.y); gen.writeEndArray(); } - gen.writeEndArray(); - -// gen.writeStartArray(); -// gen.writeNumber(bbox.getMaxX()); -// gen.writeNumber(bbox.getMinY()); -// gen.writeEndArray(); - - gen.writeEndArray(); - gen.writeEndObject(); + if (value.getGeometry().getGeometryType().equals("Polygon")) { + gen.writeEndArray(); + } -// gen.writeObjectFieldStart("crs"); -// gen.writeStringField("type", "name"); -// gen.writeObjectFieldStart("properties"); -// gen.writeStringField("name", "EPSG:0"); -// gen.writeEndObject(); -// gen.writeEndObject(); + gen.writeEndArray(); // end 'coordinates' + gen.writeEndObject(); // end 'geometry' } if (value.getHouseNumber() != null) { diff --git a/app/opensearch/src/test/java/de/komoot/photon/ESBaseTester.java b/app/opensearch/src/test/java/de/komoot/photon/ESBaseTester.java index ff2652ce1..7b531e77e 100644 --- a/app/opensearch/src/test/java/de/komoot/photon/ESBaseTester.java +++ b/app/opensearch/src/test/java/de/komoot/photon/ESBaseTester.java @@ -2,7 +2,6 @@ import de.komoot.photon.opensearch.Importer; import de.komoot.photon.opensearch.OpenSearchTestServer; -import de.komoot.photon.opensearch.Updater; import de.komoot.photon.searcher.PhotonResult; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.io.TempDir; @@ -49,11 +48,11 @@ protected PhotonResult getById(String id) { } public void setUpES() throws IOException { - setUpES(dataDirectory, false,"en"); + setUpES(dataDirectory, false, "en"); } public void setUpESWithPolygons() throws IOException { - setUpES(dataDirectory, true,"en"); + setUpES(dataDirectory, true, "en"); } public void setUpES(Path testDirectory, boolean supportPolygons, String... languages) throws IOException { diff --git a/app/opensearch/src/test/java/de/komoot/photon/opensearch/OpenSearchTestServer.java b/app/opensearch/src/test/java/de/komoot/photon/opensearch/OpenSearchTestServer.java index 161f60715..6c65537a8 100644 --- a/app/opensearch/src/test/java/de/komoot/photon/opensearch/OpenSearchTestServer.java +++ b/app/opensearch/src/test/java/de/komoot/photon/opensearch/OpenSearchTestServer.java @@ -38,8 +38,7 @@ public void build(final int number, final Settings.Builder settingsBuilder) { .basePath(instanceDir) .clusterName(clusterName) .numOfNode(1) - .baseHttpPort(9200) - .moduleTypes(OPENSEARCH_MODULES)); + .baseHttpPort(9200)); // wait for yellow status runner.ensureYellow(); diff --git a/src/main/java/de/komoot/photon/PhotonDoc.java b/src/main/java/de/komoot/photon/PhotonDoc.java index cbf1bc606..37f91dcb0 100644 --- a/src/main/java/de/komoot/photon/PhotonDoc.java +++ b/src/main/java/de/komoot/photon/PhotonDoc.java @@ -5,8 +5,6 @@ import org.locationtech.jts.geom.Geometry; import org.locationtech.jts.geom.Point; import de.komoot.photon.nominatim.model.AddressType; -import org.locationtech.jts.geom.Polygon; -import org.locationtech.spatial4j.shape.ShapeFactory; import org.slf4j.Logger; import java.util.*; diff --git a/src/main/java/de/komoot/photon/SearchRequestHandler.java b/src/main/java/de/komoot/photon/SearchRequestHandler.java index 82dbaa85d..041e96567 100644 --- a/src/main/java/de/komoot/photon/SearchRequestHandler.java +++ b/src/main/java/de/komoot/photon/SearchRequestHandler.java @@ -20,15 +20,13 @@ public class SearchRequestHandler extends RouteImpl { private final PhotonRequestFactory photonRequestFactory; private final SearchHandler requestHandler; - private final int maxResults; private final boolean supportPolygons; SearchRequestHandler(String path, SearchHandler dbHandler, String[] languages, String defaultLanguage, int maxResults, boolean supportPolygons) { super(path); List<String> supportedLanguages = Arrays.asList(languages); - this.photonRequestFactory = new PhotonRequestFactory(supportedLanguages, defaultLanguage, maxResults); + this.photonRequestFactory = new PhotonRequestFactory(supportedLanguages, defaultLanguage, maxResults, supportPolygons); this.requestHandler = dbHandler; - this.maxResults = maxResults; this.supportPolygons = supportPolygons; } @@ -43,9 +41,9 @@ public String handle(Request request, Response response) throws BadRequestExcept throw halt(e.getHttpStatus(), json.toString()); } - if (!supportPolygons && (photonRequest.isPolygonRequest() && photonRequest.getReturnPolygon())) { + if (!supportPolygons && photonRequest.getReturnPolygon()) { JSONObject json = new JSONObject(); - json.put("message", "You're requesting a polygon, but polygons are not imported!"); + json.put("message", "You're explicitly requesting a polygon, but polygons are not imported!"); throw halt(400, json.toString()); } diff --git a/src/main/java/de/komoot/photon/StructuredSearchRequestHandler.java b/src/main/java/de/komoot/photon/StructuredSearchRequestHandler.java index 6b066acc6..ec5e99ab5 100644 --- a/src/main/java/de/komoot/photon/StructuredSearchRequestHandler.java +++ b/src/main/java/de/komoot/photon/StructuredSearchRequestHandler.java @@ -22,7 +22,7 @@ public class StructuredSearchRequestHandler extends RouteImpl { StructuredSearchRequestHandler(String path, StructuredSearchHandler dbHandler, String[] languages, String defaultLanguage, int maxResults, boolean supportPolygons) { super(path); List<String> supportedLanguages = Arrays.asList(languages); - this.photonRequestFactory = new PhotonRequestFactory(supportedLanguages, defaultLanguage, maxResults); + this.photonRequestFactory = new PhotonRequestFactory(supportedLanguages, defaultLanguage, maxResults, supportPolygons); this.requestHandler = dbHandler; this.supportPolygons = supportPolygons; } @@ -38,7 +38,7 @@ public String handle(Request request, Response response) { throw halt(e.getHttpStatus(), json.toString()); } - if (!supportPolygons && (photonRequest.isPolygonRequest() && photonRequest.getReturnPolygon())) { + if (!supportPolygons && photonRequest.getReturnPolygon()) { JSONObject json = new JSONObject(); json.put("message", "You're requesting a polygon, but polygons are not imported!"); throw halt(400, json.toString()); diff --git a/src/main/java/de/komoot/photon/nominatim/NominatimUpdater.java b/src/main/java/de/komoot/photon/nominatim/NominatimUpdater.java index 1c591656a..9e278a416 100644 --- a/src/main/java/de/komoot/photon/nominatim/NominatimUpdater.java +++ b/src/main/java/de/komoot/photon/nominatim/NominatimUpdater.java @@ -78,8 +78,8 @@ public class NominatimUpdater extends NominatimConnector { private boolean useGeometryColumn; - public NominatimUpdater(String host, int port, String database, String username, String passwordboolean, boolean useGeometryColumn) { - this(host, port, database, username, passwordboolean, new PostgisDataAdapter(), useGeometryColumn); + public NominatimUpdater(String host, int port, String database, String username, String password, boolean useGeometryColumn) { + this(host, port, database, username, password, new PostgisDataAdapter(), useGeometryColumn); } public NominatimUpdater(String host, int port, String database, String username, String password, DBDataAdapter dataAdapter, boolean useGeometryColumn) { diff --git a/src/main/java/de/komoot/photon/query/PhotonRequestBase.java b/src/main/java/de/komoot/photon/query/PhotonRequestBase.java index 1e559be10..c8ba8aeae 100644 --- a/src/main/java/de/komoot/photon/query/PhotonRequestBase.java +++ b/src/main/java/de/komoot/photon/query/PhotonRequestBase.java @@ -18,8 +18,7 @@ public class PhotonRequestBase private int zoom = 14; private Envelope bbox = null; private boolean debug = false; - private boolean returnPolygon = true; - private boolean polygonRequest = false; + private boolean returnPolygon = false; private final List<TagFilter> osmTagFilters = new ArrayList<>(1); private Set<String> layerFilters = new HashSet<>(1); @@ -109,11 +108,4 @@ void setReturnPolygon(boolean returnPolygon) { this.returnPolygon = returnPolygon; } - void setPolygonRequest(boolean polygonRequest) { - this.polygonRequest = polygonRequest; - } - - public boolean isPolygonRequest() { - return polygonRequest; - } } diff --git a/src/main/java/de/komoot/photon/query/PhotonRequestFactory.java b/src/main/java/de/komoot/photon/query/PhotonRequestFactory.java index 434de8b29..2c265b88c 100644 --- a/src/main/java/de/komoot/photon/query/PhotonRequestFactory.java +++ b/src/main/java/de/komoot/photon/query/PhotonRequestFactory.java @@ -17,6 +17,7 @@ public class PhotonRequestFactory { private final BoundingBoxParamConverter bboxParamConverter; private final LayerParamValidator layerParamValidator; private final int maxResults; + private final boolean supportPolygons; private static final HashSet<String> REQUEST_QUERY_PARAMS = new HashSet<>(Arrays.asList("lang", "q", "lon", "lat", "limit", "osm_tag", "location_bias_scale", "bbox", "debug", "zoom", "layer", "polygon")); @@ -28,11 +29,12 @@ public class PhotonRequestFactory { "countrycode", "state", "county", "city", "postcode", "district", "housenumber", "street")); - public PhotonRequestFactory(List<String> supportedLanguages, String defaultLanguage, int maxResults) { + public PhotonRequestFactory(List<String> supportedLanguages, String defaultLanguage, int maxResults, boolean supportPolygons) { this.languageResolver = new RequestLanguageResolver(supportedLanguages, defaultLanguage); this.bboxParamConverter = new BoundingBoxParamConverter(); this.layerParamValidator = new LayerParamValidator(); this.maxResults = maxResults; + this.supportPolygons = supportPolygons; } public StructuredPhotonRequest createStructured(Request webRequest) throws BadRequestException { @@ -112,8 +114,11 @@ private void addCommonParameters(Request webRequest, PhotonRequestBase request) request.setLayerFilter(layerParamValidator.validate(layerFiltersQueryMap.values())); } + // If the database supports polygons, return them by default. + request.setReturnPolygon(supportPolygons); + + // Check if the user explicitly doesn't want a polygon. if (webRequest.queryParams("polygon") != null) { - request.setPolygonRequest(true); request.setReturnPolygon(parseBoolean(webRequest, "polygon")); } } @@ -152,12 +157,12 @@ private Double parseDouble(Request webRequest, String param) throws BadRequestEx return outVal; } - private Boolean parseBoolean(Request webRequest, String param) throws BadRequestException { - Boolean booleanVal = null; + private Boolean parseBoolean(Request webRequest, String param) { + boolean booleanVal = false; String value = webRequest.queryParams(param); if (value != null && !value.isEmpty()) { - booleanVal = Boolean.valueOf(value); + booleanVal = Boolean.parseBoolean(value); } return booleanVal; diff --git a/src/main/java/de/komoot/photon/searcher/GeocodeJsonFormatter.java b/src/main/java/de/komoot/photon/searcher/GeocodeJsonFormatter.java index f7568852a..467436aea 100644 --- a/src/main/java/de/komoot/photon/searcher/GeocodeJsonFormatter.java +++ b/src/main/java/de/komoot/photon/searcher/GeocodeJsonFormatter.java @@ -35,7 +35,7 @@ public String convert(List<PhotonResult> results, String debugInfo) { .put("properties", getResultProperties(result)) .put("geometry", result.get("geometry"))); } else { - var geom = new JSONObject().put("type", "Polygon").put("coordinates", result.getGeometry()); + var geom = new JSONObject().put("type", result.getGeometryType().getName()).put("coordinates", result.getGeometry()); features.put(new JSONObject() .put("type", "Feature") .put("properties", getResultProperties(result)) diff --git a/src/main/java/de/komoot/photon/searcher/GeometryType.java b/src/main/java/de/komoot/photon/searcher/GeometryType.java new file mode 100644 index 000000000..c508deba2 --- /dev/null +++ b/src/main/java/de/komoot/photon/searcher/GeometryType.java @@ -0,0 +1,17 @@ +package de.komoot.photon.searcher; + +public enum GeometryType { + UNKNOWN("unknown"), + POLYGON("Polygon"), + LINESTRING("LineString"); + + private final String name; + + GeometryType(String name) { + this.name = name; + } + + public String getName() { + return name; + } +} diff --git a/src/main/java/de/komoot/photon/searcher/PhotonResult.java b/src/main/java/de/komoot/photon/searcher/PhotonResult.java index 5064a96a2..2e02bec08 100644 --- a/src/main/java/de/komoot/photon/searcher/PhotonResult.java +++ b/src/main/java/de/komoot/photon/searcher/PhotonResult.java @@ -26,6 +26,8 @@ public interface PhotonResult { double[] getCoordinates(); + GeometryType getGeometryType(); + double[][] getGeometry(); double[] getExtent(); diff --git a/src/test/java/de/komoot/photon/ApiIntegrationTest.java b/src/test/java/de/komoot/photon/ApiIntegrationTest.java index f434d2808..4eaceb109 100644 --- a/src/test/java/de/komoot/photon/ApiIntegrationTest.java +++ b/src/test/java/de/komoot/photon/ApiIntegrationTest.java @@ -7,12 +7,14 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.CsvSource; +import org.locationtech.jts.io.WKTReader; import java.io.BufferedReader; import java.io.InputStreamReader; import java.net.HttpURLConnection; import java.net.URL; import java.net.URLConnection; +import java.util.Collections; import java.util.stream.Collectors; import static org.junit.jupiter.api.Assertions.*; @@ -26,10 +28,11 @@ class ApiIntegrationTest extends ESBaseTester { @BeforeEach void setUp() throws Exception { - setUpES(); + setUpESWithPolygons(); Importer instance = makeImporter(); instance.add(createDoc(13.38886, 52.51704, 1000, 1000, "place", "city").importance(0.6), 0); instance.add(createDoc(13.39026, 52.54714, 1001, 1001, "place", "town").importance(0.3), 0); + instance.add(createDoc(13.39026, 52.54714, 1002, 1002, "place", "city").importance(0.3).names(Collections.singletonMap("name", "linestring")).geometry(new WKTReader().read("LINESTRING (30 10, 10 30, 40 40)")), 0); instance.finish(); refresh(); } @@ -167,4 +170,26 @@ void testSearchAndGetPolygon() throws Exception { assertEquals("city", properties.getString("osm_value")); assertEquals("berlin", properties.getString("name")); } + + @Test + void testSearchAndGetLineString() throws Exception { + App.main(new String[]{"-cluster", TEST_CLUSTER_NAME, "-listen-port", Integer.toString(LISTEN_PORT), "-transport-addresses", "127.0.0.1"}); + awaitInitialization(); + HttpURLConnection connection = (HttpURLConnection) new URL("http://127.0.0.1:" + port() + "/api?q=linestring&limit=1").openConnection(); + JSONObject json = new JSONObject( + new BufferedReader(new InputStreamReader(connection.getInputStream())).lines().collect(Collectors.joining("\n"))); + JSONArray features = json.getJSONArray("features"); + assertEquals(1, features.length()); + JSONObject feature = features.getJSONObject(0); + + JSONObject geometry = feature.getJSONObject("geometry"); + assertEquals("LineString", geometry.getString("type")); + assertEquals("[[30,10],[10,30],[40,40]]", geometry.getJSONArray("coordinates").toString()); + + JSONObject properties = feature.getJSONObject("properties"); + assertEquals("W", properties.getString("osm_type")); + assertEquals("place", properties.getString("osm_key")); + assertEquals("city", properties.getString("osm_value")); + assertEquals("linestring", properties.getString("name")); + } } diff --git a/src/test/java/de/komoot/photon/query/PhotonRequestFactoryTest.java b/src/test/java/de/komoot/photon/query/PhotonRequestFactoryTest.java index fbfa009ac..00760695c 100644 --- a/src/test/java/de/komoot/photon/query/PhotonRequestFactoryTest.java +++ b/src/test/java/de/komoot/photon/query/PhotonRequestFactoryTest.java @@ -56,7 +56,7 @@ private void requestWithLayers(Request mockRequest, String... layers) { } private PhotonRequest createPhotonRequest(Request mockRequest) throws BadRequestException { - PhotonRequestFactory factory = new PhotonRequestFactory(Collections.singletonList("en"), "en", 10); + PhotonRequestFactory factory = new PhotonRequestFactory(Collections.singletonList("en"), "en", 10, true); return factory.create(mockRequest); } @@ -211,6 +211,14 @@ void testWithBadLayerFilters() { String.format("Error message doesn not contain '%s': %s", expectedMessageFragment, exception.getMessage())); } + @Test + void testWithPolygon() throws Exception { + Request mockRequest = createRequestWithQueryParams("q", "berlin"); + PhotonRequest photonRequest = createPhotonRequest(mockRequest); + + assertTrue(photonRequest.getReturnPolygon()); + } + @Test void testWithoutPolygon() throws Exception { Request mockRequest = createRequestWithQueryParams("q", "berlin", "polygon", "false"); diff --git a/src/test/java/de/komoot/photon/query/QueryPolygonTest.java b/src/test/java/de/komoot/photon/query/QueryPolygonTest.java index ffd31b197..3bcfe0021 100644 --- a/src/test/java/de/komoot/photon/query/QueryPolygonTest.java +++ b/src/test/java/de/komoot/photon/query/QueryPolygonTest.java @@ -6,6 +6,8 @@ import de.komoot.photon.searcher.PhotonResult; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.locationtech.jts.geom.Coordinate; +import org.locationtech.jts.geom.Point; import org.locationtech.jts.io.ParseException; import org.locationtech.jts.io.WKTReader; @@ -36,7 +38,7 @@ private PhotonDoc createDoc(String... names) throws ParseException { } ++testDocId; - return new PhotonDoc(testDocId, "N", testDocId, "place", "city", new WKTReader().read("POLYGON ((1 1, 1 2, 2 1, 1 1))")).names(nameMap); + return new PhotonDoc(testDocId, "N", testDocId, "place", "city").names(nameMap); } private List<PhotonResult> search(String query) { @@ -47,7 +49,26 @@ private List<PhotonResult> search(String query) { @Test void testSearchGetPolygon() throws IOException, ParseException { Importer instance = makeImporter(); - instance.add(createDoc("name", "Muffle Flu"), 0); + Point location = FACTORY.createPoint(new Coordinate(1.0, 2.34)); + PhotonDoc doc = createDoc("name", "Muffle Flu").geometry(new WKTReader().read("POLYGON ((30 10, 40 40, 20 40, 10 20, 30 10))")).centroid(location); + instance.add(doc, 0); + instance.finish(); + refresh(); + List<PhotonResult> s = search("muffle flu"); + + if (s.get(0).getClass().getName().equals("de.komoot.photon.opensearch.OpenSearchResult")) { + assertNotNull(s.get(0).getGeometry()); + } else { + assertNotNull(s.get(0).get("geometry")); + } + } + + @Test + void testSearchGetLineString() throws IOException, ParseException { + Importer instance = makeImporter(); + Point location = FACTORY.createPoint(new Coordinate(1.0, 2.34)); + PhotonDoc doc = createDoc("name", "Muffle Flu").geometry(new WKTReader().read("LINESTRING (30 10, 10 30, 40 40)")).centroid(location); + instance.add(doc, 0); instance.finish(); refresh(); List<PhotonResult> s = search("muffle flu"); diff --git a/src/test/java/de/komoot/photon/searcher/MockPhotonResult.java b/src/test/java/de/komoot/photon/searcher/MockPhotonResult.java index 649528cde..b63f9c3ce 100644 --- a/src/test/java/de/komoot/photon/searcher/MockPhotonResult.java +++ b/src/test/java/de/komoot/photon/searcher/MockPhotonResult.java @@ -10,6 +10,7 @@ public class MockPhotonResult implements PhotonResult { final Map<String, Object> data = new HashMap<>(); final double[] coordinates = new double[]{42, 21}; final double[][] geometry = {{6.4440619,52.1969454},{6.4441094,52.1969158},{6.4441408,52.1969347},{6.4441138,52.1969516},{6.4440933,52.1969643},{6.4440619,52.1969454}}; + final GeometryType geometryType = GeometryType.POLYGON; final double[] extent = new double[]{0, 1, 2, 3}; final Map<String, String> localized = new HashMap<>(); @@ -38,10 +39,10 @@ public double[][] getGeometry() { return geometry; } -// @Override -// public double[][] getGeometry() { -// return geometry; -// } + @Override + public GeometryType getGeometryType() { + return geometryType; + } @Override public double[] getExtent() { From eff3b06d00eea965b0743be7598badd28a2e7dda Mon Sep 17 00:00:00 2001 From: red-fenix <red-fenix@xor.nu> Date: Sun, 23 Feb 2025 11:05:58 +0100 Subject: [PATCH 15/18] feat: add support for all polygon types --- .../photon/elasticsearch/ElasticResult.java | 15 +------ .../elasticsearch/PhotonDocConverter.java | 8 +--- .../elasticsearch/ElasticGetIdResult.java | 8 +--- .../de/komoot/photon/opensearch/Importer.java | 9 +++- .../photon/opensearch/OpenSearchResult.java | 14 ++---- .../OpenSearchResultDeserializer.java | 45 ++----------------- .../opensearch/PhotonDocSerializer.java | 31 +++---------- .../photon/opensearch/ImporterTest.java | 2 +- src/main/java/de/komoot/photon/Constants.java | 1 + src/main/java/de/komoot/photon/PhotonDoc.java | 25 +++++++++++ .../photon/nominatim/NominatimImporter.java | 2 +- .../photon/nominatim/PostgisDataAdapter.java | 1 + .../nominatim/model/PlaceRowMapper.java | 6 ++- .../photon/searcher/GeocodeJsonFormatter.java | 9 ++-- .../komoot/photon/searcher/GeometryType.java | 17 ------- .../komoot/photon/searcher/PhotonResult.java | 5 +-- .../searcher/GeocodeJsonFormatterTest.java | 6 +-- .../photon/searcher/MockPhotonResult.java | 10 +---- 18 files changed, 72 insertions(+), 142 deletions(-) delete mode 100644 src/main/java/de/komoot/photon/searcher/GeometryType.java diff --git a/app/es_embedded/src/main/java/de/komoot/photon/elasticsearch/ElasticResult.java b/app/es_embedded/src/main/java/de/komoot/photon/elasticsearch/ElasticResult.java index 9145f303a..d92a38d30 100644 --- a/app/es_embedded/src/main/java/de/komoot/photon/elasticsearch/ElasticResult.java +++ b/app/es_embedded/src/main/java/de/komoot/photon/elasticsearch/ElasticResult.java @@ -1,7 +1,6 @@ package de.komoot.photon.elasticsearch; import de.komoot.photon.Constants; -import de.komoot.photon.searcher.GeometryType; import de.komoot.photon.searcher.PhotonResult; import org.elasticsearch.search.SearchHit; import org.slf4j.Logger; @@ -68,18 +67,8 @@ public double[] getCoordinates() { } @Override - public GeometryType getGeometryType() { - final Map<String, Object> geometry = (Map<String, Object>) result.getSource().get("geometry"); - - return GeometryType.valueOf((String) geometry.get("type")); - } - - @Override - public double[][] getGeometry() { - final Map<String, Object> geometry = (Map<String, Object>) result.getSource().get("geometry"); - final List<List<Double>> coords = (List<List<Double>>) geometry.get("coordinates"); - - return null; + public String getGeometry() { + return (String) result.getSource().get("geometry"); } @Override diff --git a/app/es_embedded/src/main/java/de/komoot/photon/elasticsearch/PhotonDocConverter.java b/app/es_embedded/src/main/java/de/komoot/photon/elasticsearch/PhotonDocConverter.java index 00d37a6e1..f5580d9f9 100644 --- a/app/es_embedded/src/main/java/de/komoot/photon/elasticsearch/PhotonDocConverter.java +++ b/app/es_embedded/src/main/java/de/komoot/photon/elasticsearch/PhotonDocConverter.java @@ -4,16 +4,12 @@ import de.komoot.photon.PhotonDoc; import de.komoot.photon.nominatim.model.AddressType; -import org.elasticsearch.common.xcontent.NamedXContentRegistry; -import org.elasticsearch.common.xcontent.XContentBuilder; -import org.elasticsearch.common.xcontent.XContentFactory; -import org.elasticsearch.common.xcontent.XContentParser; +import org.elasticsearch.common.xcontent.*; import org.elasticsearch.common.xcontent.json.JsonXContent; import org.locationtech.jts.geom.Envelope; import org.locationtech.jts.io.geojson.GeoJsonWriter; import java.io.IOException; -import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.Map; @@ -46,7 +42,7 @@ public static XContentBuilder convert(PhotonDoc doc, String[] languages, String[ if (doc.getGeometry() != null) { GeoJsonWriter g = new GeoJsonWriter(); - + XContentParser parser = JsonXContent .jsonXContent .createParser(NamedXContentRegistry.EMPTY, g.write(doc.getGeometry())); diff --git a/app/es_embedded/src/test/java/de/komoot/photon/elasticsearch/ElasticGetIdResult.java b/app/es_embedded/src/test/java/de/komoot/photon/elasticsearch/ElasticGetIdResult.java index 73ffbb10d..6a4fb487c 100644 --- a/app/es_embedded/src/test/java/de/komoot/photon/elasticsearch/ElasticGetIdResult.java +++ b/app/es_embedded/src/test/java/de/komoot/photon/elasticsearch/ElasticGetIdResult.java @@ -1,6 +1,5 @@ package de.komoot.photon.elasticsearch; -import de.komoot.photon.searcher.GeometryType; import de.komoot.photon.searcher.PhotonResult; import org.apache.commons.lang3.NotImplementedException; import org.elasticsearch.action.get.GetResponse; @@ -35,12 +34,7 @@ public double[] getCoordinates() { throw new NotImplementedException(); } - @Override - public GeometryType getGeometryType() { - throw new NotImplementedException(); - } - - public double[][] getGeometry() { + public String getGeometry() { throw new NotImplementedException(); } diff --git a/app/opensearch/src/main/java/de/komoot/photon/opensearch/Importer.java b/app/opensearch/src/main/java/de/komoot/photon/opensearch/Importer.java index 00616e85a..dc488cbc2 100644 --- a/app/opensearch/src/main/java/de/komoot/photon/opensearch/Importer.java +++ b/app/opensearch/src/main/java/de/komoot/photon/opensearch/Importer.java @@ -56,7 +56,14 @@ private void saveDocuments() { var response = client.bulk(bulkRequest.build()); if (response.errors()) { - LOGGER.error("Error during bulk import."); + for (BulkResponseItem bri: response.items()) { + LOGGER.error("Error during bulk import."); + if (bri.error() != null) { + LOGGER.error(bri.error().reason()); + LOGGER.error(bri.error().type()); + LOGGER.error(bri.error().stackTrace()); + } + } } } catch (IOException e) { LOGGER.error("Error during bulk import", e); diff --git a/app/opensearch/src/main/java/de/komoot/photon/opensearch/OpenSearchResult.java b/app/opensearch/src/main/java/de/komoot/photon/opensearch/OpenSearchResult.java index 74a36149f..0bfeba686 100644 --- a/app/opensearch/src/main/java/de/komoot/photon/opensearch/OpenSearchResult.java +++ b/app/opensearch/src/main/java/de/komoot/photon/opensearch/OpenSearchResult.java @@ -1,6 +1,5 @@ package de.komoot.photon.opensearch; -import de.komoot.photon.searcher.GeometryType; import de.komoot.photon.searcher.PhotonResult; import org.json.JSONObject; @@ -12,18 +11,16 @@ public class OpenSearchResult implements PhotonResult { private double score = 0.0; private final double[] extent; private final double[] coordinates; - private final double[][] geometry; - private final GeometryType geometryType; + private final String geometry; private final Map<String, Object> infos; private final Map<String, Map<String, String>> localeTags; - OpenSearchResult(double[] extent, double[] coordinates, Map<String, Object> infos, Map<String, Map<String, String>> localeTags, double[][] geometry, GeometryType geometryType) { + OpenSearchResult(double[] extent, double[] coordinates, Map<String, Object> infos, Map<String, Map<String, String>> localeTags, String geometry) { this.extent = extent; this.coordinates = coordinates; this.infos = infos; this.localeTags = localeTags; this.geometry = geometry; - this.geometryType = geometryType; } public OpenSearchResult setScore(double score) { @@ -66,12 +63,7 @@ public double[] getCoordinates() { return coordinates; } - @Override - public GeometryType getGeometryType() { - return geometryType; - } - - public double[][] getGeometry() { + public String getGeometry() { return geometry; } diff --git a/app/opensearch/src/main/java/de/komoot/photon/opensearch/OpenSearchResultDeserializer.java b/app/opensearch/src/main/java/de/komoot/photon/opensearch/OpenSearchResultDeserializer.java index 26431173c..7f822f365 100644 --- a/app/opensearch/src/main/java/de/komoot/photon/opensearch/OpenSearchResultDeserializer.java +++ b/app/opensearch/src/main/java/de/komoot/photon/opensearch/OpenSearchResultDeserializer.java @@ -7,7 +7,6 @@ import com.fasterxml.jackson.databind.node.ArrayNode; import com.fasterxml.jackson.databind.node.ObjectNode; import de.komoot.photon.Constants; -import de.komoot.photon.searcher.GeometryType; import de.komoot.photon.searcher.PhotonResult; import java.io.IOException; @@ -30,17 +29,9 @@ public OpenSearchResult deserialize(JsonParser p, DeserializationContext ctxt) t final Map<String, Object> tags = new HashMap<>(); final Map<String, Map<String, String>> localeTags = new HashMap<>(); - double[][] geometry = new double[0][]; - GeometryType geometryType = GeometryType.UNKNOWN; - - if (node.get("geometry") != null && node.get("geometry").get("type") != null) { - if (node.get("geometry").get("type").asText().equals("Polygon")) { - geometry = extractPolygon((ObjectNode) node.get("geometry")); - geometryType = GeometryType.POLYGON; - } else if (node.get("geometry").get("type").asText().equals("LineString")) { - geometry = extractLineString((ObjectNode) node.get("geometry")); - geometryType = GeometryType.LINESTRING; - } + String geometry = null; + if (node.get("geometry") != null) { + geometry = node.get("geometry").toString(); } var fields = node.fields(); @@ -69,7 +60,7 @@ public OpenSearchResult deserialize(JsonParser p, DeserializationContext ctxt) t } } - return new OpenSearchResult(extent, coordinates, tags, localeTags, geometry, geometryType); + return new OpenSearchResult(extent, coordinates, tags, localeTags, geometry); } private double[] extractExtent(ObjectNode node) { @@ -92,32 +83,4 @@ private double[] extractCoordinate(ObjectNode node) { return new double[]{node.get(Constants.LON).doubleValue(), node.get(Constants.LAT).doubleValue()}; } - - private double[][] extractPolygon(ObjectNode node) { - if (node == null) { - return PhotonResult.INVALID_GEOMETRY; - } - - double[][] coordinates = new double[node.get("coordinates").get(0).size()][]; - for(int i=0; i<node.get("coordinates").get(0).size(); i++) { - double[] coordinate = new double[] {node.get("coordinates").get(0).get(i).get(0).doubleValue(), node.get("coordinates").get(0).get(i).get(1).doubleValue()}; - coordinates[i] = coordinate; - } - - return coordinates; - } - - private double[][] extractLineString(ObjectNode node) { - if (node == null) { - return PhotonResult.INVALID_GEOMETRY; - } - - double[][] coordinates = new double[node.get("coordinates").size()][]; - for(int i=0; i<node.get("coordinates").size(); i++) { - double[] coordinate = new double[] {node.get("coordinates").get(i).get(0).doubleValue(), node.get("coordinates").get(i).get(1).doubleValue()}; - coordinates[i] = coordinate; - } - - return coordinates; - } } diff --git a/app/opensearch/src/main/java/de/komoot/photon/opensearch/PhotonDocSerializer.java b/app/opensearch/src/main/java/de/komoot/photon/opensearch/PhotonDocSerializer.java index 903fd3998..73e511793 100644 --- a/app/opensearch/src/main/java/de/komoot/photon/opensearch/PhotonDocSerializer.java +++ b/app/opensearch/src/main/java/de/komoot/photon/opensearch/PhotonDocSerializer.java @@ -6,12 +6,10 @@ import de.komoot.photon.Constants; import de.komoot.photon.PhotonDoc; import de.komoot.photon.Utils; -import org.locationtech.jts.geom.Coordinate; import org.locationtech.jts.geom.Envelope; import org.locationtech.jts.io.geojson.GeoJsonWriter; import java.io.IOException; -import java.io.StringWriter; import java.util.HashMap; import java.util.HashSet; import java.util.Map; @@ -51,28 +49,13 @@ public void serialize(PhotonDoc value, JsonGenerator gen, SerializerProvider pro gen.writeEndObject(); } - if (value.getGeometry() != null) { - gen.writeObjectFieldStart("geometry"); - gen.writeStringField("type", value.getGeometry().getGeometryType()); + if (value.getGeometry() != null && !value.getGeometry().getGeometryType().equals("Point")) { + // Convert JTS Geometry to GeoJSON + GeoJsonWriter geoJsonWriter = new GeoJsonWriter(); + String geoJson = geoJsonWriter.write(value.getGeometry()); - gen.writeArrayFieldStart("coordinates"); - - if (value.getGeometry().getGeometryType().equals("Polygon")) { - gen.writeStartArray(); - } - - for (Coordinate c: value.getGeometry().getCoordinates()) { - gen.writeStartArray(); - gen.writeNumber(c.x); - gen.writeNumber(c.y); - gen.writeEndArray(); - } - if (value.getGeometry().getGeometryType().equals("Polygon")) { - gen.writeEndArray(); - } - - gen.writeEndArray(); // end 'coordinates' - gen.writeEndObject(); // end 'geometry' + gen.writeFieldName("geometry"); + gen.writeRawValue(geoJson); } if (value.getHouseNumber() != null) { @@ -109,7 +92,7 @@ public void serialize(PhotonDoc value, JsonGenerator gen, SerializerProvider pro gen.writeEndObject(); } - private void writeName(JsonGenerator gen, PhotonDoc doc, String[] languages) throws IOException { + private void writeName(JsonGenerator gen, PhotonDoc doc, String[] languages) throws IOException { Map<String, String> fNames = new HashMap<>(); doc.copyName(fNames, "default", "name"); diff --git a/app/opensearch/src/test/java/de/komoot/photon/opensearch/ImporterTest.java b/app/opensearch/src/test/java/de/komoot/photon/opensearch/ImporterTest.java index 221efc7fc..418ac741a 100644 --- a/app/opensearch/src/test/java/de/komoot/photon/opensearch/ImporterTest.java +++ b/app/opensearch/src/test/java/de/komoot/photon/opensearch/ImporterTest.java @@ -27,7 +27,7 @@ public void setUp() throws IOException { void testAddSimpleDoc() throws ParseException { Importer instance = makeImporterWithExtra(""); - instance.add(new PhotonDoc(1234, "N", 1000, "place", "city", new WKTReader().read("POLYGON ((1 1, 1 2, 2 1, 1 1))")) + instance.add(new PhotonDoc(1234, "N", 1000, "place", "city", new WKTReader().read("MULTIPOLYGON (((6.111933 51.2659309, 6.1119417 51.2659247, 6.1119554 51.2659249, 6.1119868 51.2659432, 6.111964 51.2659591, 6.1119333 51.2659391, 6.111933 51.2659309)))")) .extraTags(Collections.singletonMap("maxspeed", "100")), 0); instance.finish(); diff --git a/src/main/java/de/komoot/photon/Constants.java b/src/main/java/de/komoot/photon/Constants.java index 50957da07..fc3a9f268 100644 --- a/src/main/java/de/komoot/photon/Constants.java +++ b/src/main/java/de/komoot/photon/Constants.java @@ -24,4 +24,5 @@ public class Constants { public static final String OSM_VALUE = "osm_value"; public static final String OBJECT_TYPE = "type"; public static final String CLASSIFICATION = "classification"; + public static final String GEOMETRY = "geometry"; } diff --git a/src/main/java/de/komoot/photon/PhotonDoc.java b/src/main/java/de/komoot/photon/PhotonDoc.java index 37f91dcb0..a9eda8978 100644 --- a/src/main/java/de/komoot/photon/PhotonDoc.java +++ b/src/main/java/de/komoot/photon/PhotonDoc.java @@ -360,4 +360,29 @@ public Point getCentroid() { public Geometry getGeometry() { return this.geometry; } + + @Override + public String toString() { + return "PhotonDoc{" + + "placeId=" + placeId + + ", osmType='" + osmType + '\'' + + ", osmId=" + osmId + + ", tagKey='" + tagKey + '\'' + + ", tagValue='" + tagValue + '\'' + + ", name=" + name + + ", postcode='" + postcode + '\'' + + ", extratags=" + extratags + + ", bbox=" + bbox + + ", parentPlaceId=" + parentPlaceId + + ", importance=" + importance + + ", countryCode='" + countryCode + '\'' + + ", linkedPlaceId=" + linkedPlaceId + + ", rankAddress=" + rankAddress + + ", addressParts=" + addressParts + + ", context=" + context + + ", houseNumber='" + houseNumber + '\'' + + ", centroid=" + centroid + + ", geometry=" + geometry + + '}'; + } } diff --git a/src/main/java/de/komoot/photon/nominatim/NominatimImporter.java b/src/main/java/de/komoot/photon/nominatim/NominatimImporter.java index be661adc9..1d48b2034 100644 --- a/src/main/java/de/komoot/photon/nominatim/NominatimImporter.java +++ b/src/main/java/de/komoot/photon/nominatim/NominatimImporter.java @@ -101,7 +101,7 @@ public void readCountry(String countryCode, ImportThread importThread) { " parent.rank_address as parent_rank_address, parent.name as parent_name, "; if (useGeometryColumn) { - query += "p.geometry, "; + query += "p.geometry as geometry, "; } template.query( query + diff --git a/src/main/java/de/komoot/photon/nominatim/PostgisDataAdapter.java b/src/main/java/de/komoot/photon/nominatim/PostgisDataAdapter.java index 37e6718fc..051d9cc47 100644 --- a/src/main/java/de/komoot/photon/nominatim/PostgisDataAdapter.java +++ b/src/main/java/de/komoot/photon/nominatim/PostgisDataAdapter.java @@ -37,6 +37,7 @@ public Geometry extractGeometry(ResultSet rs, String columnName) throws SQLExcep return new WKTReader().read(sb.toString()); } catch (ParseException e) { // ignore + System.out.println(e); } } diff --git a/src/main/java/de/komoot/photon/nominatim/model/PlaceRowMapper.java b/src/main/java/de/komoot/photon/nominatim/model/PlaceRowMapper.java index 191b27888..543b3cef7 100644 --- a/src/main/java/de/komoot/photon/nominatim/model/PlaceRowMapper.java +++ b/src/main/java/de/komoot/photon/nominatim/model/PlaceRowMapper.java @@ -43,7 +43,11 @@ public PhotonDoc mapRow(ResultSet rs, int rowNum) throws SQLException { .postcode(rs.getString("postcode")); if (useGeometryColumn) { - doc.geometry(dbutils.extractGeometry(rs, "geometry")); + try { + doc.geometry(dbutils.extractGeometry(rs, "geometry")); + } catch (IllegalArgumentException e) { + System.out.println("Could not get Geometry: " + e); + } } double importance = rs.getDouble("importance"); diff --git a/src/main/java/de/komoot/photon/searcher/GeocodeJsonFormatter.java b/src/main/java/de/komoot/photon/searcher/GeocodeJsonFormatter.java index 467436aea..cce1e9178 100644 --- a/src/main/java/de/komoot/photon/searcher/GeocodeJsonFormatter.java +++ b/src/main/java/de/komoot/photon/searcher/GeocodeJsonFormatter.java @@ -34,12 +34,15 @@ public String convert(List<PhotonResult> results, String debugInfo) { .put("type", "Feature") .put("properties", getResultProperties(result)) .put("geometry", result.get("geometry"))); - } else { - var geom = new JSONObject().put("type", result.getGeometryType().getName()).put("coordinates", result.getGeometry()); + } + else { + // We need to un-escape the JSON String first + JSONObject jsonObject = new JSONObject(result.getGeometry()); + features.put(new JSONObject() .put("type", "Feature") .put("properties", getResultProperties(result)) - .put("geometry", geom)); + .put("geometry", jsonObject)); } } else { final double[] coordinates = result.getCoordinates(); diff --git a/src/main/java/de/komoot/photon/searcher/GeometryType.java b/src/main/java/de/komoot/photon/searcher/GeometryType.java deleted file mode 100644 index c508deba2..000000000 --- a/src/main/java/de/komoot/photon/searcher/GeometryType.java +++ /dev/null @@ -1,17 +0,0 @@ -package de.komoot.photon.searcher; - -public enum GeometryType { - UNKNOWN("unknown"), - POLYGON("Polygon"), - LINESTRING("LineString"); - - private final String name; - - GeometryType(String name) { - this.name = name; - } - - public String getName() { - return name; - } -} diff --git a/src/main/java/de/komoot/photon/searcher/PhotonResult.java b/src/main/java/de/komoot/photon/searcher/PhotonResult.java index 2e02bec08..ffbcefd26 100644 --- a/src/main/java/de/komoot/photon/searcher/PhotonResult.java +++ b/src/main/java/de/komoot/photon/searcher/PhotonResult.java @@ -9,7 +9,6 @@ */ public interface PhotonResult { final double[] INVALID_COORDINATES = new double[]{0, 0}; - final double[][] INVALID_GEOMETRY = new double[][]{{0, 0}}; /** * Get the value for the given field. @@ -26,9 +25,7 @@ public interface PhotonResult { double[] getCoordinates(); - GeometryType getGeometryType(); - - double[][] getGeometry(); + String getGeometry(); double[] getExtent(); diff --git a/src/test/java/de/komoot/photon/searcher/GeocodeJsonFormatterTest.java b/src/test/java/de/komoot/photon/searcher/GeocodeJsonFormatterTest.java index c311c15e8..46519499d 100644 --- a/src/test/java/de/komoot/photon/searcher/GeocodeJsonFormatterTest.java +++ b/src/test/java/de/komoot/photon/searcher/GeocodeJsonFormatterTest.java @@ -51,7 +51,7 @@ void testConvertPolygonToGeojson() { for (int i = 0; i < features.length(); i++) { JSONObject feature = features.getJSONObject(i); assertEquals("Feature", feature.getString("type")); - assertEquals("Polygon", feature.getJSONObject("geometry").getString("type")); + assertEquals("MultiPolygon", feature.getJSONObject("geometry").getString("type")); assertEquals("leisure", feature.getJSONObject("properties").getString(Constants.OSM_KEY)); assertEquals("park", feature.getJSONObject("properties").getString(Constants.OSM_VALUE)); } @@ -76,9 +76,7 @@ private PhotonResult createDummyPolygonResult(String postCode, String name, Stri .putLocalized(Constants.NAME, "en", name) .put(Constants.OSM_KEY, osmKey) .put(Constants.OSM_VALUE, osmValue) - .put("geometry", new JSONObject() - .put("type", "Polygon") - .put("coordinates", new double[][]{{100.0, 0.0}, {101.0, 0.0}, {101.0, 1.0}, {100.0, 1.0}, {100.0, 0.0}})); + .put(Constants.GEOMETRY, new JSONObject("{\"type\":\"MultiPolygon\",\"coordinates\":[[[[-100.0,40.0],[-100.0,45.0],[-90.0,45.0],[-90.0,40.0],[-100.0,40.0]]],[[[-80.0,35.0],[-80.0,40.0],[-70.0,40.0],[-70.0,35.0],[-80.0,35.0]]]]}")); } } diff --git a/src/test/java/de/komoot/photon/searcher/MockPhotonResult.java b/src/test/java/de/komoot/photon/searcher/MockPhotonResult.java index b63f9c3ce..613f92947 100644 --- a/src/test/java/de/komoot/photon/searcher/MockPhotonResult.java +++ b/src/test/java/de/komoot/photon/searcher/MockPhotonResult.java @@ -9,8 +9,7 @@ public class MockPhotonResult implements PhotonResult { final Map<String, Object> data = new HashMap<>(); final double[] coordinates = new double[]{42, 21}; - final double[][] geometry = {{6.4440619,52.1969454},{6.4441094,52.1969158},{6.4441408,52.1969347},{6.4441138,52.1969516},{6.4440933,52.1969643},{6.4440619,52.1969454}}; - final GeometryType geometryType = GeometryType.POLYGON; + final String geometry = "{\"type\":\"MultiPolygon\",\"coordinates\":[[[[-100.0,40.0],[-100.0,45.0],[-90.0,45.0],[-90.0,40.0],[-100.0,40.0]]],[[[-80.0,35.0],[-80.0,40.0],[-70.0,40.0],[-70.0,35.0],[-80.0,35.0]]]]}"; final double[] extent = new double[]{0, 1, 2, 3}; final Map<String, String> localized = new HashMap<>(); @@ -35,15 +34,10 @@ public double[] getCoordinates() { } @Override - public double[][] getGeometry() { + public String getGeometry() { return geometry; } - @Override - public GeometryType getGeometryType() { - return geometryType; - } - @Override public double[] getExtent() { return extent; From a5ac9b4acd11e8ec8108bbaf74afa80715035b35 Mon Sep 17 00:00:00 2001 From: red-fenix <red-fenix@xor.nu> Date: Tue, 11 Mar 2025 20:59:00 +0100 Subject: [PATCH 16/18] bugfix: rename all "polygon" references to "geometry" --- .../src/main/java/de/komoot/photon/Server.java | 12 ++++++------ .../java/de/komoot/photon/ESBaseTester.java | 6 +++--- .../src/main/java/de/komoot/photon/Server.java | 6 +++--- .../photon/opensearch/DBPropertyEntry.java | 4 ++-- .../java/de/komoot/photon/ESBaseTester.java | 6 +++--- .../komoot/photon/opensearch/ImporterTest.java | 2 +- src/main/java/de/komoot/photon/App.java | 12 ++++++------ .../java/de/komoot/photon/CommandLineArgs.java | 2 +- .../de/komoot/photon/DatabaseProperties.java | 16 ++++++++-------- src/main/java/de/komoot/photon/PhotonDoc.java | 4 ++-- .../photon/ReverseSearchRequestHandler.java | 2 +- .../de/komoot/photon/SearchRequestHandler.java | 14 +++++++------- .../photon/StructuredSearchRequestHandler.java | 14 +++++++------- .../komoot/photon/query/PhotonRequestBase.java | 8 ++++---- .../photon/query/PhotonRequestFactory.java | 18 +++++++++--------- .../de/komoot/photon/query/ReverseRequest.java | 10 +++++----- .../photon/query/ReverseRequestFactory.java | 4 ++-- .../de/komoot/photon/ApiIntegrationTest.java | 4 ++-- .../photon/query/PhotonRequestFactoryTest.java | 10 +++++----- ...PolygonTest.java => QueryGeometryTest.java} | 4 ++-- .../searcher/GeocodeJsonFormatterTest.java | 16 ++++++++-------- 21 files changed, 87 insertions(+), 87 deletions(-) rename src/test/java/de/komoot/photon/query/{QueryPolygonTest.java => QueryGeometryTest.java} (97%) diff --git a/app/es_embedded/src/main/java/de/komoot/photon/Server.java b/app/es_embedded/src/main/java/de/komoot/photon/Server.java index 22582f22a..a52907891 100644 --- a/app/es_embedded/src/main/java/de/komoot/photon/Server.java +++ b/app/es_embedded/src/main/java/de/komoot/photon/Server.java @@ -56,7 +56,7 @@ public class Server { private static final String FIELD_VERSION = "database_version"; private static final String FIELD_LANGUAGES = "indexed_languages"; private static final String FIELD_IMPORT_DATE = "import_date"; - private static final String FIELD_SUPPORT_POLYGONS = "support_polygons"; + private static final String FIELD_SUPPORT_GEOMETRIES = "support_geometries"; private Node esNode; @@ -178,7 +178,7 @@ private void setupDirectories(URL directoryName) throws IOException, URISyntaxEx } - public DatabaseProperties recreateIndex(String[] languages, Date importDate, boolean supportStructuredQueries, boolean supportPolygons) throws IOException { + public DatabaseProperties recreateIndex(String[] languages, Date importDate, boolean supportStructuredQueries, boolean supportGeometries) throws IOException { deleteIndex(); loadIndexSettings().createIndex(esClient, PhotonIndex.NAME); @@ -188,7 +188,7 @@ public DatabaseProperties recreateIndex(String[] languages, Date importDate, boo DatabaseProperties dbProperties = new DatabaseProperties() .setLanguages(languages) .setImportDate(importDate) - .setSupportPolygons(supportPolygons); + .setSupportGeometries(supportGeometries); saveToDatabase(dbProperties); @@ -244,7 +244,7 @@ public void saveToDatabase(DatabaseProperties dbProperties) throws IOException .field(FIELD_VERSION, DATABASE_VERSION) .field(FIELD_LANGUAGES, String.join(",", dbProperties.getLanguages())) .field(FIELD_IMPORT_DATE, dbProperties.getImportDate() instanceof Date ? dbProperties.getImportDate().toInstant() : null) - .field(FIELD_SUPPORT_POLYGONS, Boolean.toString(dbProperties.getSupportPolygons())) + .field(FIELD_SUPPORT_GEOMETRIES, Boolean.toString(dbProperties.getSupportGeometries())) .endObject().endObject(); esClient.prepareIndex(PhotonIndex.NAME, PhotonIndex.TYPE). @@ -285,12 +285,12 @@ public DatabaseProperties loadFromDatabase() { String importDateString = properties.get(FIELD_IMPORT_DATE); - String supportPolygons = properties.get(FIELD_SUPPORT_POLYGONS); + String supportGeometries = properties.get(FIELD_SUPPORT_GEOMETRIES); return new DatabaseProperties(langString == null ? null : langString.split(","), importDateString == null ? null : Date.from(Instant.parse(importDateString)), false, - Boolean.parseBoolean(supportPolygons)); + Boolean.parseBoolean(supportGeometries)); } public Importer createImporter(String[] languages, String[] extraTags) { diff --git a/app/es_embedded/src/test/java/de/komoot/photon/ESBaseTester.java b/app/es_embedded/src/test/java/de/komoot/photon/ESBaseTester.java index dff5bdcd6..1ea9bd62a 100644 --- a/app/es_embedded/src/test/java/de/komoot/photon/ESBaseTester.java +++ b/app/es_embedded/src/test/java/de/komoot/photon/ESBaseTester.java @@ -50,7 +50,7 @@ public void setUpES() throws IOException { setUpES(dataDirectory, false,"en"); } - public void setUpESWithPolygons() throws IOException { + public void setUpESWithGeometry() throws IOException { setUpES(dataDirectory, true,"en"); } /** @@ -58,10 +58,10 @@ public void setUpESWithPolygons() throws IOException { * * @throws IOException */ - public void setUpES(Path testDirectory, boolean supportPolygons, String... languages) throws IOException { + public void setUpES(Path testDirectory, boolean supportGeometries, String... languages) throws IOException { server = new ElasticTestServer(testDirectory.toString()); server.start(TEST_CLUSTER_NAME, new String[]{}); - server.recreateIndex(languages, new Date(), false, supportPolygons); + server.recreateIndex(languages, new Date(), false, supportGeometries); refresh(); } diff --git a/app/opensearch/src/main/java/de/komoot/photon/Server.java b/app/opensearch/src/main/java/de/komoot/photon/Server.java index d753a84ed..fe555dd35 100644 --- a/app/opensearch/src/main/java/de/komoot/photon/Server.java +++ b/app/opensearch/src/main/java/de/komoot/photon/Server.java @@ -120,7 +120,7 @@ public void shutdown() { } } - public DatabaseProperties recreateIndex(String[] languages, Date importDate, boolean supportStructuredQueries, boolean supportPolygons) throws IOException { + public DatabaseProperties recreateIndex(String[] languages, Date importDate, boolean supportStructuredQueries, boolean supportGeometries) throws IOException { // delete any existing data if (client.indices().exists(e -> e.index(PhotonIndex.NAME)).value()) { client.indices().delete(d -> d.index(PhotonIndex.NAME)); @@ -130,7 +130,7 @@ public DatabaseProperties recreateIndex(String[] languages, Date importDate, boo (new IndexMapping(supportStructuredQueries)).addLanguages(languages).putMapping(client, PhotonIndex.NAME); - var dbProperties = new DatabaseProperties(languages, importDate, supportStructuredQueries, supportPolygons); + var dbProperties = new DatabaseProperties(languages, importDate, supportStructuredQueries, supportGeometries); saveToDatabase(dbProperties); return dbProperties; @@ -182,7 +182,7 @@ public DatabaseProperties loadFromDatabase() throws IOException { return new DatabaseProperties(dbEntry.source().languages, dbEntry.source().importDate, dbEntry.source().supportStructuredQueries, - dbEntry.source().supportPolygons); + dbEntry.source().supportGeometries); } public Importer createImporter(String[] languages, String[] extraTags) { diff --git a/app/opensearch/src/main/java/de/komoot/photon/opensearch/DBPropertyEntry.java b/app/opensearch/src/main/java/de/komoot/photon/opensearch/DBPropertyEntry.java index 795234dbe..21679cab3 100644 --- a/app/opensearch/src/main/java/de/komoot/photon/opensearch/DBPropertyEntry.java +++ b/app/opensearch/src/main/java/de/komoot/photon/opensearch/DBPropertyEntry.java @@ -9,7 +9,7 @@ public class DBPropertyEntry { public Date importDate; public String[] languages; public boolean supportStructuredQueries; - public boolean supportPolygons; + public boolean supportGeometries; public DBPropertyEntry() {} @@ -18,6 +18,6 @@ public DBPropertyEntry(DatabaseProperties props, String databaseVersion) { importDate = props.getImportDate(); languages = props.getLanguages(); supportStructuredQueries = props.getSupportStructuredQueries(); - supportPolygons = props.getSupportPolygons(); + supportGeometries = props.getSupportGeometries(); } } diff --git a/app/opensearch/src/test/java/de/komoot/photon/ESBaseTester.java b/app/opensearch/src/test/java/de/komoot/photon/ESBaseTester.java index 7b531e77e..98bf0979d 100644 --- a/app/opensearch/src/test/java/de/komoot/photon/ESBaseTester.java +++ b/app/opensearch/src/test/java/de/komoot/photon/ESBaseTester.java @@ -51,14 +51,14 @@ public void setUpES() throws IOException { setUpES(dataDirectory, false, "en"); } - public void setUpESWithPolygons() throws IOException { + public void setUpESWithGeometry() throws IOException { setUpES(dataDirectory, true, "en"); } - public void setUpES(Path testDirectory, boolean supportPolygons, String... languages) throws IOException { + public void setUpES(Path testDirectory, boolean supportGeometries, String... languages) throws IOException { server = new OpenSearchTestServer(testDirectory.toString()); server.startTestServer(TEST_CLUSTER_NAME); - server.recreateIndex(languages, new Date(), true, supportPolygons); + server.recreateIndex(languages, new Date(), true, supportGeometries); server.refreshIndexes(); } diff --git a/app/opensearch/src/test/java/de/komoot/photon/opensearch/ImporterTest.java b/app/opensearch/src/test/java/de/komoot/photon/opensearch/ImporterTest.java index 418ac741a..3ffcf1a64 100644 --- a/app/opensearch/src/test/java/de/komoot/photon/opensearch/ImporterTest.java +++ b/app/opensearch/src/test/java/de/komoot/photon/opensearch/ImporterTest.java @@ -20,7 +20,7 @@ class ImporterTest extends ESBaseTester { @BeforeEach public void setUp() throws IOException { - setUpESWithPolygons(); + setUpESWithGeometry(); } @Test diff --git a/src/main/java/de/komoot/photon/App.java b/src/main/java/de/komoot/photon/App.java index 5258bf69c..604fb20a0 100644 --- a/src/main/java/de/komoot/photon/App.java +++ b/src/main/java/de/komoot/photon/App.java @@ -253,8 +253,8 @@ private static void startApi(CommandLineArgs args, Server server) throws IOExcep } LOGGER.info("Starting API with the following settings: " + - "\n Languages: {} \n Import Date: {} \n Support Structured Queries: {} \n Support Polygons: {}", - dbProperties.getLanguages(), dbProperties.getImportDate(), dbProperties.getSupportStructuredQueries(), dbProperties.getSupportPolygons()); + "\n Languages: {} \n Import Date: {} \n Support Structured Queries: {} \n Support Geometries: {}", + dbProperties.getLanguages(), dbProperties.getImportDate(), dbProperties.getSupportStructuredQueries(), dbProperties.getSupportGeometries()); port(args.getListenPort()); ipAddress(args.getListenIp()); @@ -271,13 +271,13 @@ private static void startApi(CommandLineArgs args, Server server) throws IOExcep String[] langs = dbProperties.getLanguages(); SearchHandler searchHandler = server.createSearchHandler(langs, args.getQueryTimeout()); - get("api", new SearchRequestHandler("api", searchHandler, langs, args.getDefaultLanguage(), args.getMaxResults(), dbProperties.getSupportPolygons())); - get("api/", new SearchRequestHandler("api/", searchHandler, langs, args.getDefaultLanguage(), args.getMaxResults(), dbProperties.getSupportPolygons())); + get("api", new SearchRequestHandler("api", searchHandler, langs, args.getDefaultLanguage(), args.getMaxResults(), dbProperties.getSupportGeometries())); + get("api/", new SearchRequestHandler("api/", searchHandler, langs, args.getDefaultLanguage(), args.getMaxResults(), dbProperties.getSupportGeometries())); if (dbProperties.getSupportStructuredQueries()) { StructuredSearchHandler structured = server.createStructuredSearchHandler(langs, args.getQueryTimeout()); - get("structured", new StructuredSearchRequestHandler("structured", structured, langs, args.getDefaultLanguage(), args.getMaxResults(), dbProperties.getSupportPolygons())); - get("structured/", new StructuredSearchRequestHandler("structured/", structured, langs, args.getDefaultLanguage(), args.getMaxResults(), dbProperties.getSupportPolygons())); + get("structured", new StructuredSearchRequestHandler("structured", structured, langs, args.getDefaultLanguage(), args.getMaxResults(), dbProperties.getSupportGeometries())); + get("structured/", new StructuredSearchRequestHandler("structured/", structured, langs, args.getDefaultLanguage(), args.getMaxResults(), dbProperties.getSupportGeometries())); } ReverseHandler reverseHandler = server.createReverseHandler(args.getQueryTimeout()); diff --git a/src/main/java/de/komoot/photon/CommandLineArgs.java b/src/main/java/de/komoot/photon/CommandLineArgs.java index 1428d4f41..38df37a39 100644 --- a/src/main/java/de/komoot/photon/CommandLineArgs.java +++ b/src/main/java/de/komoot/photon/CommandLineArgs.java @@ -98,7 +98,7 @@ public class CommandLineArgs { @Parameter(names = "-max-reverse-results", description = "The maximum possible 'limit' parameter for reverse geocoding searches") private int maxReverseResults = 50; - @Parameter(names = "-import-geometry-column", description = "[import-only] Add the 'geometry' column from Nominatim on import (i.e. add Polygons for cities, countries etc.). WARNING: This will increase the Elasticsearch Index size! (~575GB for Planet)") + @Parameter(names = "-import-geometry-column", description = "[import-only] Add the 'geometry' column from Nominatim on import (i.e. add Polygons/Linestrings/Multipolygons etc. for cities, countries etc.). WARNING: This will increase the Elasticsearch Index size! (~575GB for Planet)") private boolean importGeometryColumn = false; public String[] getLanguages(boolean useDefaultIfEmpty) { diff --git a/src/main/java/de/komoot/photon/DatabaseProperties.java b/src/main/java/de/komoot/photon/DatabaseProperties.java index ca599648a..875cd1917 100644 --- a/src/main/java/de/komoot/photon/DatabaseProperties.java +++ b/src/main/java/de/komoot/photon/DatabaseProperties.java @@ -11,13 +11,13 @@ public class DatabaseProperties { private String[] languages; private Date importDate; private boolean supportStructuredQueries; - private boolean supportPolygons; + private boolean supportGeometries; - public DatabaseProperties(String[] languages, Date importDate, boolean supportStructuredQueries, boolean supportPolygons) { + public DatabaseProperties(String[] languages, Date importDate, boolean supportStructuredQueries, boolean supportGeometries) { this.languages = languages; this.importDate = importDate; this.supportStructuredQueries = supportStructuredQueries; - this.supportPolygons = supportPolygons; + this.supportGeometries = supportGeometries; } public DatabaseProperties() { @@ -100,12 +100,12 @@ public DatabaseProperties setSupportStructuredQueries(boolean supportStructuredQ return this; } - public boolean getSupportPolygons() { - return supportPolygons; + public boolean getSupportGeometries() { + return supportGeometries; } - public DatabaseProperties setSupportPolygons(boolean supportPolygons) { - this.supportPolygons = supportPolygons; + public DatabaseProperties setSupportGeometries(boolean supportGeometries) { + this.supportGeometries = supportGeometries; return this; } @@ -115,7 +115,7 @@ public String toString() { "languages=" + Arrays.toString(languages) + ", importDate=" + importDate + ", supportStructuredQueries=" + supportStructuredQueries + - ", supportPolygons=" + supportPolygons + + ", supportGeometries=" + supportGeometries + '}'; } } diff --git a/src/main/java/de/komoot/photon/PhotonDoc.java b/src/main/java/de/komoot/photon/PhotonDoc.java index a9eda8978..8403c9f82 100644 --- a/src/main/java/de/komoot/photon/PhotonDoc.java +++ b/src/main/java/de/komoot/photon/PhotonDoc.java @@ -98,8 +98,8 @@ public PhotonDoc centroid(Geometry centroid) { return this; } - public PhotonDoc geometry(Geometry polygon) { - this.geometry = (Geometry) polygon; + public PhotonDoc geometry(Geometry geometry) { + this.geometry = (Geometry) geometry; return this; } diff --git a/src/main/java/de/komoot/photon/ReverseSearchRequestHandler.java b/src/main/java/de/komoot/photon/ReverseSearchRequestHandler.java index 3416bd25b..e6cda6d63 100644 --- a/src/main/java/de/komoot/photon/ReverseSearchRequestHandler.java +++ b/src/main/java/de/komoot/photon/ReverseSearchRequestHandler.java @@ -53,6 +53,6 @@ public String handle(Request request, Response response) { debugInfo = requestHandler.dumpQuery(photonRequest); } - return new GeocodeJsonFormatter(false, photonRequest.getLanguage(), photonRequest.getPolygon()).convert(results, debugInfo); + return new GeocodeJsonFormatter(false, photonRequest.getLanguage(), photonRequest.getGeometry()).convert(results, debugInfo); } } diff --git a/src/main/java/de/komoot/photon/SearchRequestHandler.java b/src/main/java/de/komoot/photon/SearchRequestHandler.java index 041e96567..5a5d07bb1 100644 --- a/src/main/java/de/komoot/photon/SearchRequestHandler.java +++ b/src/main/java/de/komoot/photon/SearchRequestHandler.java @@ -20,14 +20,14 @@ public class SearchRequestHandler extends RouteImpl { private final PhotonRequestFactory photonRequestFactory; private final SearchHandler requestHandler; - private final boolean supportPolygons; + private final boolean supportGeometries; - SearchRequestHandler(String path, SearchHandler dbHandler, String[] languages, String defaultLanguage, int maxResults, boolean supportPolygons) { + SearchRequestHandler(String path, SearchHandler dbHandler, String[] languages, String defaultLanguage, int maxResults, boolean supportGeometries) { super(path); List<String> supportedLanguages = Arrays.asList(languages); - this.photonRequestFactory = new PhotonRequestFactory(supportedLanguages, defaultLanguage, maxResults, supportPolygons); + this.photonRequestFactory = new PhotonRequestFactory(supportedLanguages, defaultLanguage, maxResults, supportGeometries); this.requestHandler = dbHandler; - this.supportPolygons = supportPolygons; + this.supportGeometries = supportGeometries; } @Override @@ -41,9 +41,9 @@ public String handle(Request request, Response response) throws BadRequestExcept throw halt(e.getHttpStatus(), json.toString()); } - if (!supportPolygons && photonRequest.getReturnPolygon()) { + if (!supportGeometries && photonRequest.getReturnGeometry()) { JSONObject json = new JSONObject(); - json.put("message", "You're explicitly requesting a polygon, but polygons are not imported!"); + json.put("message", "You're explicitly requesting a geometry, but geometries are not imported!"); throw halt(400, json.toString()); } @@ -62,6 +62,6 @@ public String handle(Request request, Response response) throws BadRequestExcept debugInfo = requestHandler.dumpQuery(photonRequest); } - return new GeocodeJsonFormatter(photonRequest.getDebug(), photonRequest.getLanguage(), photonRequest.getReturnPolygon()).convert(results, debugInfo); + return new GeocodeJsonFormatter(photonRequest.getDebug(), photonRequest.getLanguage(), photonRequest.getReturnGeometry()).convert(results, debugInfo); } } \ No newline at end of file diff --git a/src/main/java/de/komoot/photon/StructuredSearchRequestHandler.java b/src/main/java/de/komoot/photon/StructuredSearchRequestHandler.java index ec5e99ab5..2c3a63853 100644 --- a/src/main/java/de/komoot/photon/StructuredSearchRequestHandler.java +++ b/src/main/java/de/komoot/photon/StructuredSearchRequestHandler.java @@ -17,14 +17,14 @@ public class StructuredSearchRequestHandler extends RouteImpl { private final PhotonRequestFactory photonRequestFactory; private final StructuredSearchHandler requestHandler; - private final boolean supportPolygons; + private final boolean supportGeometries; - StructuredSearchRequestHandler(String path, StructuredSearchHandler dbHandler, String[] languages, String defaultLanguage, int maxResults, boolean supportPolygons) { + StructuredSearchRequestHandler(String path, StructuredSearchHandler dbHandler, String[] languages, String defaultLanguage, int maxResults, boolean supportGeometries) { super(path); List<String> supportedLanguages = Arrays.asList(languages); - this.photonRequestFactory = new PhotonRequestFactory(supportedLanguages, defaultLanguage, maxResults, supportPolygons); + this.photonRequestFactory = new PhotonRequestFactory(supportedLanguages, defaultLanguage, maxResults, supportGeometries); this.requestHandler = dbHandler; - this.supportPolygons = supportPolygons; + this.supportGeometries = supportGeometries; } @Override @@ -38,9 +38,9 @@ public String handle(Request request, Response response) { throw halt(e.getHttpStatus(), json.toString()); } - if (!supportPolygons && photonRequest.getReturnPolygon()) { + if (!supportGeometries && photonRequest.getReturnGeometry()) { JSONObject json = new JSONObject(); - json.put("message", "You're requesting a polygon, but polygons are not imported!"); + json.put("message", "You're requesting a Geometry, but Geometries are not imported!"); throw halt(400, json.toString()); } @@ -60,6 +60,6 @@ public String handle(Request request, Response response) { debugInfo = requestHandler.dumpQuery(photonRequest); } */ - return new GeocodeJsonFormatter(photonRequest.getDebug(), photonRequest.getLanguage(), photonRequest.getReturnPolygon()).convert(results, debugInfo); + return new GeocodeJsonFormatter(photonRequest.getDebug(), photonRequest.getLanguage(), photonRequest.getReturnGeometry()).convert(results, debugInfo); } } \ No newline at end of file diff --git a/src/main/java/de/komoot/photon/query/PhotonRequestBase.java b/src/main/java/de/komoot/photon/query/PhotonRequestBase.java index c8ba8aeae..9b7803dee 100644 --- a/src/main/java/de/komoot/photon/query/PhotonRequestBase.java +++ b/src/main/java/de/komoot/photon/query/PhotonRequestBase.java @@ -18,7 +18,7 @@ public class PhotonRequestBase private int zoom = 14; private Envelope bbox = null; private boolean debug = false; - private boolean returnPolygon = false; + private boolean returnGeometry = false; private final List<TagFilter> osmTagFilters = new ArrayList<>(1); private Set<String> layerFilters = new HashSet<>(1); @@ -54,7 +54,7 @@ public String getLanguage() { public boolean getDebug() { return debug; } - public boolean getReturnPolygon() { return returnPolygon; } + public boolean getReturnGeometry() { return returnGeometry; } public List<TagFilter> getOsmTagFilters() { return osmTagFilters; @@ -104,8 +104,8 @@ void enableDebug() { this.debug = true; } - void setReturnPolygon(boolean returnPolygon) { - this.returnPolygon = returnPolygon; + void setReturnGeometry(boolean returnGeometry) { + this.returnGeometry = returnGeometry; } } diff --git a/src/main/java/de/komoot/photon/query/PhotonRequestFactory.java b/src/main/java/de/komoot/photon/query/PhotonRequestFactory.java index 2c265b88c..cb369384b 100644 --- a/src/main/java/de/komoot/photon/query/PhotonRequestFactory.java +++ b/src/main/java/de/komoot/photon/query/PhotonRequestFactory.java @@ -17,10 +17,10 @@ public class PhotonRequestFactory { private final BoundingBoxParamConverter bboxParamConverter; private final LayerParamValidator layerParamValidator; private final int maxResults; - private final boolean supportPolygons; + private final boolean supportGeometries; private static final HashSet<String> REQUEST_QUERY_PARAMS = new HashSet<>(Arrays.asList("lang", "q", "lon", "lat", - "limit", "osm_tag", "location_bias_scale", "bbox", "debug", "zoom", "layer", "polygon")); + "limit", "osm_tag", "location_bias_scale", "bbox", "debug", "zoom", "layer", "geometry")); private static final HashSet<String> STRUCTURED_ADDRESS_FIELDS = new HashSet<>(Arrays.asList("countrycode", "state", "county", "city", "postcode", "district", "housenumber", "street")); @@ -29,12 +29,12 @@ public class PhotonRequestFactory { "countrycode", "state", "county", "city", "postcode", "district", "housenumber", "street")); - public PhotonRequestFactory(List<String> supportedLanguages, String defaultLanguage, int maxResults, boolean supportPolygons) { + public PhotonRequestFactory(List<String> supportedLanguages, String defaultLanguage, int maxResults, boolean supportGeometries) { this.languageResolver = new RequestLanguageResolver(supportedLanguages, defaultLanguage); this.bboxParamConverter = new BoundingBoxParamConverter(); this.layerParamValidator = new LayerParamValidator(); this.maxResults = maxResults; - this.supportPolygons = supportPolygons; + this.supportGeometries = supportGeometries; } public StructuredPhotonRequest createStructured(Request webRequest) throws BadRequestException { @@ -114,12 +114,12 @@ private void addCommonParameters(Request webRequest, PhotonRequestBase request) request.setLayerFilter(layerParamValidator.validate(layerFiltersQueryMap.values())); } - // If the database supports polygons, return them by default. - request.setReturnPolygon(supportPolygons); + // If the database supports geometries, return them by default. + request.setReturnGeometry(supportGeometries); - // Check if the user explicitly doesn't want a polygon. - if (webRequest.queryParams("polygon") != null) { - request.setReturnPolygon(parseBoolean(webRequest, "polygon")); + // Check if the user explicitly doesn't want a geometry. + if (webRequest.queryParams("geometry") != null) { + request.setReturnGeometry(parseBoolean(webRequest, "geometry")); } } diff --git a/src/main/java/de/komoot/photon/query/ReverseRequest.java b/src/main/java/de/komoot/photon/query/ReverseRequest.java index 5fabf0cb0..217b63dfd 100644 --- a/src/main/java/de/komoot/photon/query/ReverseRequest.java +++ b/src/main/java/de/komoot/photon/query/ReverseRequest.java @@ -20,10 +20,10 @@ public class ReverseRequest implements Serializable { private final Set<String> layerFilters; private final List<TagFilter> osmTagFilters = new ArrayList<>(1); private final boolean debug; - private final boolean polygon; + private final boolean geometry; public ReverseRequest(Point location, String language, double radius, String queryStringFilter, int limit, - boolean locationDistanceSort, Set<String> layerFilter, boolean debug, boolean polygon) { + boolean locationDistanceSort, Set<String> layerFilter, boolean debug, boolean geometry) { this.location = location; this.language = language; this.radius = radius; @@ -32,7 +32,7 @@ public ReverseRequest(Point location, String language, double radius, String que this.locationDistanceSort = locationDistanceSort; this.layerFilters = layerFilter; this.debug = debug; - this.polygon = polygon; + this.geometry = geometry; } public Point getLocation() { @@ -71,8 +71,8 @@ public boolean getDebug() { return debug; } - public boolean getPolygon() { - return polygon; + public boolean getGeometry() { + return geometry; } ReverseRequest addOsmTagFilter(TagFilter filter) { diff --git a/src/main/java/de/komoot/photon/query/ReverseRequestFactory.java b/src/main/java/de/komoot/photon/query/ReverseRequestFactory.java index d3ae421dc..70a5c2fb4 100644 --- a/src/main/java/de/komoot/photon/query/ReverseRequestFactory.java +++ b/src/main/java/de/komoot/photon/query/ReverseRequestFactory.java @@ -16,7 +16,7 @@ public class ReverseRequestFactory { private static final HashSet<String> REQUEST_QUERY_PARAMS = new HashSet<>(Arrays.asList("lang", "lon", "lat", "radius", - "query_string_filter", "distance_sort", "limit", "layer", "osm_tag", "debug", "polygon")); + "query_string_filter", "distance_sort", "limit", "layer", "osm_tag", "debug", "geometry")); private static final LocationParamConverter mandatoryLocationParamConverter = new LocationParamConverter(true); private final RequestLanguageResolver languageResolver; @@ -86,7 +86,7 @@ public ReverseRequest create(Request webRequest) throws BadRequestException { } String queryStringFilter = webRequest.queryParams("query_string_filter"); - ReverseRequest request = new ReverseRequest(location, language, radius, queryStringFilter, limit, locationDistanceSort, layerFilter, enableDebug, Boolean.parseBoolean(webRequest.queryParams("polygon"))); + ReverseRequest request = new ReverseRequest(location, language, radius, queryStringFilter, limit, locationDistanceSort, layerFilter, enableDebug, Boolean.parseBoolean(webRequest.queryParams("geometry"))); QueryParamsMap tagFiltersQueryMap = webRequest.queryMap("osm_tag"); if (tagFiltersQueryMap.hasValue()) { diff --git a/src/test/java/de/komoot/photon/ApiIntegrationTest.java b/src/test/java/de/komoot/photon/ApiIntegrationTest.java index 4eaceb109..589dc9f8d 100644 --- a/src/test/java/de/komoot/photon/ApiIntegrationTest.java +++ b/src/test/java/de/komoot/photon/ApiIntegrationTest.java @@ -28,7 +28,7 @@ class ApiIntegrationTest extends ESBaseTester { @BeforeEach void setUp() throws Exception { - setUpESWithPolygons(); + setUpESWithGeometry(); Importer instance = makeImporter(); instance.add(createDoc(13.38886, 52.51704, 1000, 1000, "place", "city").importance(0.6), 0); instance.add(createDoc(13.39026, 52.54714, 1001, 1001, "place", "town").importance(0.3), 0); @@ -151,7 +151,7 @@ void testApiStatus() throws Exception { } @Test - void testSearchAndGetPolygon() throws Exception { + void testSearchAndGetGeometry() throws Exception { App.main(new String[]{"-cluster", TEST_CLUSTER_NAME, "-listen-port", Integer.toString(LISTEN_PORT), "-transport-addresses", "127.0.0.1"}); awaitInitialization(); HttpURLConnection connection = (HttpURLConnection) new URL("http://127.0.0.1:" + port() + "/api?q=berlin&limit=1").openConnection(); diff --git a/src/test/java/de/komoot/photon/query/PhotonRequestFactoryTest.java b/src/test/java/de/komoot/photon/query/PhotonRequestFactoryTest.java index 00760695c..8eff8e9aa 100644 --- a/src/test/java/de/komoot/photon/query/PhotonRequestFactoryTest.java +++ b/src/test/java/de/komoot/photon/query/PhotonRequestFactoryTest.java @@ -212,18 +212,18 @@ void testWithBadLayerFilters() { } @Test - void testWithPolygon() throws Exception { + void testWithGeometry() throws Exception { Request mockRequest = createRequestWithQueryParams("q", "berlin"); PhotonRequest photonRequest = createPhotonRequest(mockRequest); - assertTrue(photonRequest.getReturnPolygon()); + assertTrue(photonRequest.getReturnGeometry()); } @Test - void testWithoutPolygon() throws Exception { - Request mockRequest = createRequestWithQueryParams("q", "berlin", "polygon", "false"); + void testWithoutGeometry() throws Exception { + Request mockRequest = createRequestWithQueryParams("q", "berlin", "geometry", "false"); PhotonRequest photonRequest = createPhotonRequest(mockRequest); - assertFalse(photonRequest.getReturnPolygon()); + assertFalse(photonRequest.getReturnGeometry()); } } \ No newline at end of file diff --git a/src/test/java/de/komoot/photon/query/QueryPolygonTest.java b/src/test/java/de/komoot/photon/query/QueryGeometryTest.java similarity index 97% rename from src/test/java/de/komoot/photon/query/QueryPolygonTest.java rename to src/test/java/de/komoot/photon/query/QueryGeometryTest.java index 3bcfe0021..cff061e6a 100644 --- a/src/test/java/de/komoot/photon/query/QueryPolygonTest.java +++ b/src/test/java/de/komoot/photon/query/QueryGeometryTest.java @@ -22,12 +22,12 @@ * Tests that the database backend produces queries which can find all * expected results. These tests do not check relevance. */ -class QueryPolygonTest extends ESBaseTester { +class QueryGeometryTest extends ESBaseTester { private int testDocId = 10000; @BeforeEach void setup() throws IOException { - setUpESWithPolygons(); + setUpESWithGeometry(); } private PhotonDoc createDoc(String... names) throws ParseException { diff --git a/src/test/java/de/komoot/photon/searcher/GeocodeJsonFormatterTest.java b/src/test/java/de/komoot/photon/searcher/GeocodeJsonFormatterTest.java index 46519499d..71851aa17 100644 --- a/src/test/java/de/komoot/photon/searcher/GeocodeJsonFormatterTest.java +++ b/src/test/java/de/komoot/photon/searcher/GeocodeJsonFormatterTest.java @@ -35,15 +35,15 @@ void testConvertPointToGeojson() { } @Test - void testConvertPolygonToGeojson() { + void testConvertGeometryToGeojson() { GeocodeJsonFormatter formatter = new GeocodeJsonFormatter(false, "en", true); - List<PhotonResult> allPolygonResults = new ArrayList<>(); - allPolygonResults.add(createDummyPolygonResult("99999", "Park Foo", "leisure", "park")); - allPolygonResults.add(createDummyPolygonResult("88888", "Bar Park", "leisure", "park")); + List<PhotonResult> allResults = new ArrayList<>(); + allResults.add(createDummyGeometryResult("99999", "Park Foo", "leisure", "park")); + allResults.add(createDummyGeometryResult("88888", "Bar Park", "leisure", "park")); - // Test Polygon - String geojsonString = formatter.convert(allPolygonResults, null); + // Test Geometry + String geojsonString = formatter.convert(allResults, null); JSONObject jsonObj = new JSONObject(geojsonString); assertEquals("FeatureCollection", jsonObj.getString("type")); JSONArray features = jsonObj.getJSONArray("features"); @@ -69,8 +69,8 @@ private PhotonResult createDummyPointResult(String postCode, String name, String .put("coordinates", new double[]{42, 21})); } - private PhotonResult createDummyPolygonResult(String postCode, String name, String osmKey, - String osmValue) { + private PhotonResult createDummyGeometryResult(String postCode, String name, String osmKey, + String osmValue) { return new MockPhotonResult() .put(Constants.POSTCODE, postCode) .putLocalized(Constants.NAME, "en", name) From e6570c536813e5ca3f62a3b3b22270afadddffe5 Mon Sep 17 00:00:00 2001 From: red-fenix <red-fenix@xor.nu> Date: Wed, 12 Mar 2025 07:37:21 +0100 Subject: [PATCH 17/18] bugfix: set correct crs/srid on geometry import --- .../java/de/komoot/photon/nominatim/PostgisDataAdapter.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/java/de/komoot/photon/nominatim/PostgisDataAdapter.java b/src/main/java/de/komoot/photon/nominatim/PostgisDataAdapter.java index 051d9cc47..bb3a24708 100644 --- a/src/main/java/de/komoot/photon/nominatim/PostgisDataAdapter.java +++ b/src/main/java/de/komoot/photon/nominatim/PostgisDataAdapter.java @@ -34,7 +34,10 @@ public Geometry extractGeometry(ResultSet rs, String columnName) throws SQLExcep try { StringBuffer sb = new StringBuffer(); wkt.getGeometry().outerWKT(sb); - return new WKTReader().read(sb.toString()); + + Geometry geometry = new WKTReader().read(sb.toString()); + geometry.setSRID(4326); + return geometry; } catch (ParseException e) { // ignore System.out.println(e); From 9aa111794705c369dc507cab54c265fe5bdfff5e Mon Sep 17 00:00:00 2001 From: red-fenix <red-fenix@xor.nu> Date: Wed, 12 Mar 2025 18:02:49 +0100 Subject: [PATCH 18/18] bugfix: fix Photondoc constructors --- .../de/komoot/photon/elasticsearch/ImporterTest.java | 5 ++++- .../java/de/komoot/photon/opensearch/ImporterTest.java | 3 ++- src/main/java/de/komoot/photon/PhotonDoc.java | 9 --------- 3 files changed, 6 insertions(+), 11 deletions(-) diff --git a/app/es_embedded/src/test/java/de/komoot/photon/elasticsearch/ImporterTest.java b/app/es_embedded/src/test/java/de/komoot/photon/elasticsearch/ImporterTest.java index c5fafc91e..5fc6801d2 100644 --- a/app/es_embedded/src/test/java/de/komoot/photon/elasticsearch/ImporterTest.java +++ b/app/es_embedded/src/test/java/de/komoot/photon/elasticsearch/ImporterTest.java @@ -6,6 +6,8 @@ import de.komoot.photon.searcher.PhotonResult; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.locationtech.jts.io.ParseException; +import org.locationtech.jts.io.WKTReader; import java.io.IOException; import java.util.Collections; @@ -22,10 +24,11 @@ public void setUp() throws IOException { } @Test - void testAddSimpleDoc() { + void testAddSimpleDoc() throws ParseException { Importer instance = makeImporterWithExtra(""); instance.add(new PhotonDoc(1234, "N", 1000, "place", "city") + .geometry(new WKTReader().read("MULTIPOLYGON (((6.111933 51.2659309, 6.1119417 51.2659247, 6.1119554 51.2659249, 6.1119868 51.2659432, 6.111964 51.2659591, 6.1119333 51.2659391, 6.111933 51.2659309)))")) .extraTags(Collections.singletonMap("maxspeed", "100")), 0); instance.finish(); refresh(); diff --git a/app/opensearch/src/test/java/de/komoot/photon/opensearch/ImporterTest.java b/app/opensearch/src/test/java/de/komoot/photon/opensearch/ImporterTest.java index 3ffcf1a64..1fd9d1448 100644 --- a/app/opensearch/src/test/java/de/komoot/photon/opensearch/ImporterTest.java +++ b/app/opensearch/src/test/java/de/komoot/photon/opensearch/ImporterTest.java @@ -27,7 +27,8 @@ public void setUp() throws IOException { void testAddSimpleDoc() throws ParseException { Importer instance = makeImporterWithExtra(""); - instance.add(new PhotonDoc(1234, "N", 1000, "place", "city", new WKTReader().read("MULTIPOLYGON (((6.111933 51.2659309, 6.1119417 51.2659247, 6.1119554 51.2659249, 6.1119868 51.2659432, 6.111964 51.2659591, 6.1119333 51.2659391, 6.111933 51.2659309)))")) + instance.add(new PhotonDoc(1234, "N", 1000, "place", "city") + .geometry(new WKTReader().read("MULTIPOLYGON (((6.111933 51.2659309, 6.1119417 51.2659247, 6.1119554 51.2659249, 6.1119868 51.2659432, 6.111964 51.2659591, 6.1119333 51.2659391, 6.111933 51.2659309)))")) .extraTags(Collections.singletonMap("maxspeed", "100")), 0); instance.finish(); diff --git a/src/main/java/de/komoot/photon/PhotonDoc.java b/src/main/java/de/komoot/photon/PhotonDoc.java index 8403c9f82..ed834d66c 100644 --- a/src/main/java/de/komoot/photon/PhotonDoc.java +++ b/src/main/java/de/komoot/photon/PhotonDoc.java @@ -45,15 +45,6 @@ public PhotonDoc(long placeId, String osmType, long osmId, String tagKey, String this.tagValue = tagValue; } - public PhotonDoc(long placeId, String osmType, long osmId, String tagKey, String tagValue, Geometry geometry) { - this.placeId = placeId; - this.osmType = osmType; - this.osmId = osmId; - this.tagKey = tagKey; - this.tagValue = tagValue; - this.geometry = geometry; - } - public PhotonDoc(PhotonDoc other) { this.placeId = other.placeId; this.osmType = other.osmType;