This is an automated email from the ASF dual-hosted git repository.
perdjesk pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/incubator-baremaps.git
The following commit(s) were added to refs/heads/main by this push:
new 5ad51917 Experimental GeocoderOSM (#780)
5ad51917 is described below
commit 5ad519171dda6d66f48be96417b44c7682d95b8c
Author: Perdjesk <[email protected]>
AuthorDate: Wed Sep 13 10:59:01 2023 +0200
Experimental GeocoderOSM (#780)
An experimental geocoder using OSM data in a Lucene index
---
.../geocoderosm/GeocoderOsmConsumerEntity.java | 45 ++++++
.../geocoderosm/GeocoderOsmDocumentMapper.java | 100 +++++++++++++
.../baremaps/geocoderosm/GeocoderOsmQuery.java | 40 +++++
.../org/apache/baremaps/geocoderosm/OsmTags.java | 36 +++++
.../apache/baremaps/geocoderosm/package-info.java | 4 +
.../function/RelationGeometryBuilder.java | 6 +-
.../tasks/CreateGeocoderOpenStreetMap.java | 126 ++++++++++++++++
.../apache/baremaps/geocoderosm/OSMIndexTest.java | 162 +++++++++++++++++++++
baremaps-core/src/test/resources/log4j2-test.yaml | 13 ++
9 files changed, 530 insertions(+), 2 deletions(-)
diff --git
a/baremaps-core/src/main/java/org/apache/baremaps/geocoderosm/GeocoderOsmConsumerEntity.java
b/baremaps-core/src/main/java/org/apache/baremaps/geocoderosm/GeocoderOsmConsumerEntity.java
new file mode 100644
index 00000000..c8b32f1b
--- /dev/null
+++
b/baremaps-core/src/main/java/org/apache/baremaps/geocoderosm/GeocoderOsmConsumerEntity.java
@@ -0,0 +1,45 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express
+ * or implied. See the License for the specific language governing permissions
and limitations under
+ * the License.
+ */
+
+package org.apache.baremaps.geocoderosm;
+
+import java.util.function.Consumer;
+import org.apache.baremaps.openstreetmap.model.Element;
+import org.apache.baremaps.openstreetmap.model.Entity;
+import org.apache.lucene.index.IndexWriter;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class GeocoderOsmConsumerEntity implements Consumer<Entity> {
+ private final IndexWriter indexWriter;
+ private final GeocoderOsmDocumentMapper geocoderOsmDocumentMapper =
+ new GeocoderOsmDocumentMapper();
+
+ private static final Logger logger =
LoggerFactory.getLogger(GeocoderOsmConsumerEntity.class);
+
+ public GeocoderOsmConsumerEntity(IndexWriter indexWriter) {
+ this.indexWriter = indexWriter;
+ }
+
+ @Override
+ public void accept(Entity entity) {
+ try {
+ if (entity instanceof Element element) {
+ var document = geocoderOsmDocumentMapper.apply(element);
+ indexWriter.addDocument(document);
+ }
+ } catch (Exception e) {
+ // Tolerate the failure of processing an element, partial data failure
mode
+ logger.warn("The following OSM entity ({}) is not processed due to {}",
entity, e);
+ }
+ }
+}
diff --git
a/baremaps-core/src/main/java/org/apache/baremaps/geocoderosm/GeocoderOsmDocumentMapper.java
b/baremaps-core/src/main/java/org/apache/baremaps/geocoderosm/GeocoderOsmDocumentMapper.java
new file mode 100644
index 00000000..50117332
--- /dev/null
+++
b/baremaps-core/src/main/java/org/apache/baremaps/geocoderosm/GeocoderOsmDocumentMapper.java
@@ -0,0 +1,100 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express
+ * or implied. See the License for the specific language governing permissions
and limitations under
+ * the License.
+ */
+
+package org.apache.baremaps.geocoderosm;
+
+
+
+import java.util.function.Function;
+import org.apache.baremaps.openstreetmap.model.Element;
+import org.apache.baremaps.openstreetmap.model.Node;
+import org.apache.lucene.document.Document;
+import org.apache.lucene.document.Field;
+import org.apache.lucene.document.LatLonShape;
+import org.apache.lucene.document.NumericDocValuesField;
+import org.apache.lucene.document.StoredField;
+import org.apache.lucene.document.TextField;
+import org.apache.lucene.geo.Polygon;
+import org.locationtech.jts.geom.Geometry;
+import org.locationtech.jts.io.geojson.GeoJsonWriter;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+public class GeocoderOsmDocumentMapper implements Function<Element, Document> {
+ private static final Logger logger =
LoggerFactory.getLogger(GeocoderOsmDocumentMapper.class);
+
+ @Override
+ public Document apply(Element element) {
+ var document = new Document();
+ document.add(new StoredField("osm_id", element.id()));
+ document.add(new StoredField("osm_type",
element.getClass().getSimpleName()));
+
+ if (element.getTags().containsKey(OsmTags.NAME.key())) {
+ document.add(
+ new TextField(OsmTags.NAME.key(),
element.getTags().get(OsmTags.NAME.key()).toString(),
+ Field.Store.YES));
+ }
+
+ if (element instanceof Node node) {
+ document.add(LatLonShape.createIndexableFields("polygon", node.getLat(),
node.getLon())[0]);
+ document.add(new StoredField("latitude", node.getLat()));
+ document.add(new StoredField("longitude", node.getLon()));
+ }
+ if (element.getGeometry() != null
+ &&
element.getGeometry().getGeometryType().equals(Geometry.TYPENAME_LINESTRING)) {
+ logger.debug("Geometry linestring ignored as not supported by Lucene
Polygon.fromGeoJson: {}",
+ element);
+ }
+ if (element.getGeometry() != null
+ &&
!element.getGeometry().getGeometryType().equals(Geometry.TYPENAME_POINT)
+ &&
!element.getGeometry().getGeometryType().equals(Geometry.TYPENAME_LINESTRING)) {
+ // JTS to GeoJSON
+ var geojsonWriter = new GeoJsonWriter();
+ // Remove crs field in GeoJSON as the field content is incompatible
between
+ // Lucene Polygon.fromGeoJSON and GeoJsonWriter.
+ // Avoid "crs must be CRS84 from OGC, but saw: EPSG:4326"
+ // See:
+ //
https://github.com/apache/lucene/blob/ef42af65f27f7f078b1ab426de9f2b2fa214ad86/lucene/core/src/java/org/apache/lucene/geo/SimpleGeoJSONPolygonParser.java#L180
+ //
https://github.com/locationtech/jts/blob/ee59b591f15b5150516393d3ba0b49e46a113fc9/modules/io/common/src/main/java/org/locationtech/jts/io/geojson/GeoJsonWriter.java#L226
+ geojsonWriter.setEncodeCRS(false);
+ // Assume that Geometry is in EPSG:4326/WGS84 for Lucene
Polygon.fromGeoJSON
+ var geojson = geojsonWriter.write(element.getGeometry());
+
+ // GeoJSON to Lucene Polygon
+ try {
+ var polygons = Polygon.fromGeoJSON(geojson);
+
+ for (Polygon polygon : polygons) {
+ // LatLonShape.createIndexableFields can create multiple polygons
out of a single polygon
+ // through tesselation
+ for (Field field : LatLonShape.createIndexableFields("polygon",
polygon)) {
+ document.add(field);
+ }
+ }
+ } catch (Exception e) {
+ // ignore geometry
+ logger.debug("Geometry ({}) failed indexing caused by: {}",
+ element, e);
+ }
+
+ }
+
+ if (element.getTags().containsKey(OsmTags.POPULATION.key())) {
+ var population =
Long.parseLong(element.getTags().get(OsmTags.POPULATION.key()).toString());
+ document.add(new NumericDocValuesField(OsmTags.POPULATION.key(),
population));
+ document.add(new StoredField(OsmTags.POPULATION.key(), population));
+ }
+ return document;
+ }
+
+}
diff --git
a/baremaps-core/src/main/java/org/apache/baremaps/geocoderosm/GeocoderOsmQuery.java
b/baremaps-core/src/main/java/org/apache/baremaps/geocoderosm/GeocoderOsmQuery.java
new file mode 100644
index 00000000..85c8f69d
--- /dev/null
+++
b/baremaps-core/src/main/java/org/apache/baremaps/geocoderosm/GeocoderOsmQuery.java
@@ -0,0 +1,40 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express
+ * or implied. See the License for the specific language governing permissions
and limitations under
+ * the License.
+ */
+
+package org.apache.baremaps.geocoderosm;
+
+import org.apache.baremaps.geocoder.GeocoderConstants;
+import org.apache.lucene.queryparser.classic.QueryParser;
+import org.apache.lucene.queryparser.simple.SimpleQueryParser;
+import org.apache.lucene.search.BooleanClause;
+import org.apache.lucene.search.BooleanQuery;
+import org.apache.lucene.search.Query;
+
+public class GeocoderOsmQuery {
+
+ private final String query;
+
+ public GeocoderOsmQuery(String query) {
+ this.query = query;
+ }
+
+ public Query build() {
+ var builder = new BooleanQuery.Builder();
+ var queryTextEsc = QueryParser.escape(query);
+
+ var parser = new SimpleQueryParser(GeocoderConstants.ANALYZER,
OsmTags.NAME.key());
+ var termsQuery = parser.parse(queryTextEsc);
+ // at least one terms of the queryText must be present
+ builder.add(termsQuery, BooleanClause.Occur.MUST);
+ return builder.build();
+ }
+}
diff --git
a/baremaps-core/src/main/java/org/apache/baremaps/geocoderosm/OsmTags.java
b/baremaps-core/src/main/java/org/apache/baremaps/geocoderosm/OsmTags.java
new file mode 100644
index 00000000..22f2918c
--- /dev/null
+++ b/baremaps-core/src/main/java/org/apache/baremaps/geocoderosm/OsmTags.java
@@ -0,0 +1,36 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express
+ * or implied. See the License for the specific language governing permissions
and limitations under
+ * the License.
+ */
+
+package org.apache.baremaps.geocoderosm;
+
+public enum OsmTags {
+ NAME("name"),
+ PLACE("place"),
+ POPULATION("population"),
+ LATITUDE("latitude"),
+ LONGITUDE("longitude");
+
+ private final String key;
+
+ OsmTags(String key) {
+ this.key = key;
+ }
+
+ @Override
+ public String toString() {
+ return key;
+ }
+
+ public String key() {
+ return key;
+ }
+}
diff --git
a/baremaps-core/src/main/java/org/apache/baremaps/geocoderosm/package-info.java
b/baremaps-core/src/main/java/org/apache/baremaps/geocoderosm/package-info.java
new file mode 100644
index 00000000..6f6a4a30
--- /dev/null
+++
b/baremaps-core/src/main/java/org/apache/baremaps/geocoderosm/package-info.java
@@ -0,0 +1,4 @@
+/**
+ * Experimental Geocoder based on OpenStreetMap data using a Lucene index.
+ */
+package org.apache.baremaps.geocoderosm;
diff --git
a/baremaps-core/src/main/java/org/apache/baremaps/openstreetmap/function/RelationGeometryBuilder.java
b/baremaps-core/src/main/java/org/apache/baremaps/openstreetmap/function/RelationGeometryBuilder.java
index 1ca50afe..773a516f 100644
---
a/baremaps-core/src/main/java/org/apache/baremaps/openstreetmap/function/RelationGeometryBuilder.java
+++
b/baremaps-core/src/main/java/org/apache/baremaps/openstreetmap/function/RelationGeometryBuilder.java
@@ -59,12 +59,14 @@ public class RelationGeometryBuilder implements
Consumer<Relation> {
Map<String, Object> tags = relation.getTags();
- // Filter multipolygon geometries
- if (!"multipolygon".equals(tags.get("type"))) {
+ // Filter out type that are not multipolygon or boundary geometries
+ if (!("multipolygon".equals(tags.get("type"))
+ || "boundary".equals(tags.get("type")))) {
return;
}
// Filter coastline geometries
+ // TODO: document motivation for filtering out.
if ("coastline".equals(tags.get("natural"))) {
return;
}
diff --git
a/baremaps-core/src/main/java/org/apache/baremaps/workflow/tasks/CreateGeocoderOpenStreetMap.java
b/baremaps-core/src/main/java/org/apache/baremaps/workflow/tasks/CreateGeocoderOpenStreetMap.java
new file mode 100644
index 00000000..a3a7777e
--- /dev/null
+++
b/baremaps-core/src/main/java/org/apache/baremaps/workflow/tasks/CreateGeocoderOpenStreetMap.java
@@ -0,0 +1,126 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express
+ * or implied. See the License for the specific language governing permissions
and limitations under
+ * the License.
+ */
+
+package org.apache.baremaps.workflow.tasks;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.List;
+import org.apache.baremaps.database.collection.AppendOnlyBuffer;
+import org.apache.baremaps.database.collection.DataMap;
+import org.apache.baremaps.database.collection.MemoryAlignedDataList;
+import org.apache.baremaps.database.collection.MemoryAlignedDataMap;
+import org.apache.baremaps.database.collection.MonotonicDataMap;
+import org.apache.baremaps.database.memory.MemoryMappedDirectory;
+import org.apache.baremaps.database.type.LongDataType;
+import org.apache.baremaps.database.type.LongListDataType;
+import org.apache.baremaps.database.type.PairDataType;
+import org.apache.baremaps.database.type.geometry.LonLatDataType;
+import org.apache.baremaps.geocoder.GeocoderConstants;
+import org.apache.baremaps.geocoderosm.GeocoderOsmConsumerEntity;
+import org.apache.baremaps.openstreetmap.pbf.PbfEntityReader;
+import org.apache.baremaps.stream.StreamUtils;
+import org.apache.baremaps.utils.FileUtils;
+import org.apache.baremaps.workflow.Task;
+import org.apache.baremaps.workflow.WorkflowContext;
+import org.apache.lucene.index.IndexWriter;
+import org.apache.lucene.index.IndexWriterConfig;
+import org.apache.lucene.store.FSDirectory;
+import org.locationtech.jts.geom.Coordinate;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Experimental feature.
+ *
+ * @see org.apache.baremaps.geocoderosm
+ */
+public record CreateGeocoderOpenStreetMap(Path file, Path indexDirectory)
+ implements
+ Task {
+
+ private static final Logger logger =
LoggerFactory.getLogger(CreateGeocoderOpenStreetMap.class);
+
+ @Override
+ public void execute(WorkflowContext context) throws Exception {
+
+ var path = file.toAbsolutePath();
+
+
+ var cacheDir = Files.createTempDirectory(Paths.get("."), "cache_");
+
+ DataMap<Long, Coordinate> coordinateMap;
+ if (Files.size(path) > 1 << 30) {
+ var coordinateDir =
Files.createDirectories(cacheDir.resolve("coordinate_keys"));
+ coordinateMap = new MemoryAlignedDataMap<>(
+ new LonLatDataType(),
+ new MemoryMappedDirectory(coordinateDir));
+ } else {
+ var coordinateKeysDir =
Files.createDirectories(cacheDir.resolve("coordinate_keys"));
+ var coordinateValuesDir =
Files.createDirectories(cacheDir.resolve("coordinate_vals"));
+ coordinateMap =
+ new MonotonicDataMap<>(
+ new MemoryAlignedDataList<>(
+ new PairDataType<>(new LongDataType(), new LongDataType()),
+ new MemoryMappedDirectory(coordinateKeysDir)),
+ new AppendOnlyBuffer<>(
+ new LonLatDataType(),
+ new MemoryMappedDirectory(coordinateValuesDir)));
+ }
+
+ var referenceKeysDir =
Files.createDirectory(cacheDir.resolve("reference_keys"));
+ var referenceValuesDir =
Files.createDirectory(cacheDir.resolve("reference_vals"));
+ var referenceMap =
+ new MonotonicDataMap<>(
+ new MemoryAlignedDataList<>(
+ new PairDataType<>(new LongDataType(), new LongDataType()),
+ new MemoryMappedDirectory(referenceKeysDir)),
+ new AppendOnlyBuffer<>(
+ new LongListDataType(),
+ new MemoryMappedDirectory(referenceValuesDir)));
+
+ var directory = FSDirectory.open(indexDirectory);
+ var config = new IndexWriterConfig(GeocoderConstants.ANALYZER);
+
+ try (var indexWriter = new IndexWriter(directory, config)) {
+ var importer = new GeocoderOsmConsumerEntity(indexWriter);
+ execute(
+ path,
+ coordinateMap,
+ referenceMap,
+ importer);
+ }
+ FileUtils.deleteRecursively(cacheDir);
+ }
+
+ public static void execute(
+ Path path,
+ DataMap<Long, Coordinate> coordinateMap,
+ DataMap<Long, List<Long>> referenceMap,
+ GeocoderOsmConsumerEntity importer) throws IOException {
+
+ // configure the block reader
+ var reader = new PbfEntityReader()
+ .geometries(true)
+ // Must be to 4326 projection to avoid transformation before using
Lucene API
+ .projection(4326)
+ .coordinateMap(coordinateMap)
+ .referenceMap(referenceMap);
+
+ try (var input = Files.newInputStream(path)) {
+ StreamUtils.batch(reader.stream(input)).forEach(importer);
+ }
+
+ }
+}
diff --git
a/baremaps-core/src/test/java/org/apache/baremaps/geocoderosm/OSMIndexTest.java
b/baremaps-core/src/test/java/org/apache/baremaps/geocoderosm/OSMIndexTest.java
new file mode 100644
index 00000000..7ef9319a
--- /dev/null
+++
b/baremaps-core/src/test/java/org/apache/baremaps/geocoderosm/OSMIndexTest.java
@@ -0,0 +1,162 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express
+ * or implied. See the License for the specific language governing permissions
and limitations under
+ * the License.
+ */
+
+package org.apache.baremaps.geocoderosm;
+
+import static org.apache.baremaps.testing.TestFiles.LIECHTENSTEIN_OSM_PBF;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import org.apache.baremaps.utils.FileUtils;
+import org.apache.baremaps.workflow.WorkflowContext;
+import org.apache.baremaps.workflow.tasks.CreateGeocoderOpenStreetMap;
+import org.apache.lucene.document.Document;
+import org.apache.lucene.document.LatLonShape;
+import org.apache.lucene.document.ShapeField;
+import org.apache.lucene.geo.Polygon;
+import org.apache.lucene.search.BooleanClause;
+import org.apache.lucene.search.BooleanQuery;
+import org.apache.lucene.search.FieldExistsQuery;
+import org.apache.lucene.search.IndexSearcher;
+import org.apache.lucene.search.ScoreDoc;
+import org.apache.lucene.search.SearcherFactory;
+import org.apache.lucene.search.SearcherManager;
+import org.apache.lucene.store.MMapDirectory;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+
+
+@Disabled("prototype implementation")
+public class OSMIndexTest {
+
+ private static Path directory;
+ private static IndexSearcher searcher;
+
+ @BeforeAll
+ public static void beforeAll() throws Exception {
+ // Init the geocoder service
+ directory = Files.createTempDirectory(Paths.get("."), "geocoder_");
+
+ // Create the geonames index
+
+ var task = new CreateGeocoderOpenStreetMap(LIECHTENSTEIN_OSM_PBF,
directory);
+ task.execute(new WorkflowContext());
+ var dir = MMapDirectory.open(directory);
+ var searcherManager = new SearcherManager(dir, new SearcherFactory());
+ searcher = searcherManager.acquire();
+ }
+
+ @AfterAll
+ public static void afterAll() throws IOException {
+ FileUtils.deleteRecursively(directory);
+ }
+
+ @Test
+ void testCreateIndex() throws Exception {
+ var query =
+ new GeocoderOsmQuery("vaduz").build();
+ var topDocs = searcher.search(query, 1);
+ var doc =
searcher.doc(Arrays.stream(topDocs.scoreDocs).findFirst().get().doc);
+ assertEquals("Vaduz", doc.getField("name").stringValue());
+ System.out.println(doc);
+ }
+
+
+ /**
+ * Querying document which contains a point with lat/long
+ */
+ @Test
+ void testGeoQuery() throws Exception {
+ var vaduzLatLong = new double[] {47.1392862, 9.5227962};
+ var query = LatLonShape.newPointQuery("polygon",
ShapeField.QueryRelation.CONTAINS,
+ vaduzLatLong);
+ var topDocs = searcher.search(query, 10);
+ List<Document> docs = new ArrayList<>();
+ for (ScoreDoc scoreDoc : topDocs.scoreDocs) {
+ docs.add(searcher.doc(scoreDoc.doc));
+ }
+ // Vaduz OSM relation:1155956 is present in results
+ // https://www.openstreetmap.org/relation/1155956
+ var vaduz = docs.stream()
+ .filter(doc -> Long.parseLong(doc.getField("osm_id").stringValue()) ==
1155956).findFirst();
+ assertTrue(vaduz.isPresent());
+ assertEquals("1155956", vaduz.get().get("osm_id"));
+
+ docs.forEach(System.out::println);
+ }
+
+ /**
+ * Querying document within a polygon shape drawn as a bounding box around
Vaduz
+ */
+ @Test
+ void testPolygonQuery() throws Exception {
+ // Drawing box at https://geojson.io/#map=14.18/47.13807/9.5242
+ var bbox = """
+ {
+ "type": "FeatureCollection",
+ "features": [
+ {
+ "type": "Feature",
+ "properties": {},
+ "geometry": {
+ "coordinates": [
+ [
+ [
+ 9.503676237856723,
+ 47.14708630518214
+ ],
+ [
+ 9.503676237856723,
+ 47.1290480684377
+ ],
+ [
+ 9.544726212280978,
+ 47.1290480684377
+ ],
+ [
+ 9.544726212280978,
+ 47.14708630518214
+ ],
+ [
+ 9.503676237856723,
+ 47.14708630518214
+ ]
+ ]
+ ],
+ "type": "Polygon"
+ }
+ }
+ ]
+ }
+ """;
+ var polygon = Polygon.fromGeoJSON(bbox);
+ var query = LatLonShape.newPolygonQuery("polygon",
ShapeField.QueryRelation.WITHIN, polygon);
+ var booleanQuery = new BooleanQuery.Builder();
+ booleanQuery.add(query, BooleanClause.Occur.MUST);
+ // Filter to only include document which have a name field
+ booleanQuery.add(new FieldExistsQuery("name"), BooleanClause.Occur.MUST);
+ var topDocs = searcher.search(booleanQuery.build(), 100);
+ for (ScoreDoc scoreDoc : topDocs.scoreDocs) {
+ System.out.println(searcher.doc(scoreDoc.doc));
+ }
+ }
+
+}
diff --git a/baremaps-core/src/test/resources/log4j2-test.yaml
b/baremaps-core/src/test/resources/log4j2-test.yaml
new file mode 100644
index 00000000..3232a582
--- /dev/null
+++ b/baremaps-core/src/test/resources/log4j2-test.yaml
@@ -0,0 +1,13 @@
+Configuration:
+ status: warn
+ appenders:
+ Console:
+ name: STDOUT
+ target: SYSTEM_OUT
+ PatternLayout:
+ Pattern: "[%-5level] %d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %c{1} - %msg%n"
+ Loggers:
+ Root:
+ level: warn
+ AppenderRef:
+ ref: STDOUT