From b38b8b37a15786d76ee1b2716e589ff57af214c9 Mon Sep 17 00:00:00 2001 From: Mike Barry Date: Sun, 3 Mar 2024 20:40:17 -0500 Subject: [PATCH] encode node/way/relation in vector tile feature id --- .../planetiler/FeatureCollector.java | 32 +++++++++++++------ .../planetiler/FeatureCollectorTest.java | 24 ++++++++++++++ .../planetiler/PlanetilerTests.java | 6 ++-- .../com/onthegomap/planetiler/TestUtils.java | 19 +++++++++-- 4 files changed, 66 insertions(+), 15 deletions(-) diff --git a/planetiler-core/src/main/java/com/onthegomap/planetiler/FeatureCollector.java b/planetiler-core/src/main/java/com/onthegomap/planetiler/FeatureCollector.java index a136bd8e..f73f6907 100644 --- a/planetiler-core/src/main/java/com/onthegomap/planetiler/FeatureCollector.java +++ b/planetiler-core/src/main/java/com/onthegomap/planetiler/FeatureCollector.java @@ -6,6 +6,8 @@ import com.onthegomap.planetiler.geo.GeoUtils; import com.onthegomap.planetiler.geo.GeometryException; import com.onthegomap.planetiler.geo.GeometryType; import com.onthegomap.planetiler.reader.SourceFeature; +import com.onthegomap.planetiler.reader.osm.OsmElement; +import com.onthegomap.planetiler.reader.osm.OsmSourceFeature; import com.onthegomap.planetiler.render.FeatureRenderer; import com.onthegomap.planetiler.stats.Stats; import com.onthegomap.planetiler.util.CacheByZoom; @@ -59,7 +61,7 @@ public class FeatureCollector implements Iterable { * @return a feature that can be configured further. */ public Feature geometry(String layer, Geometry geometry) { - Feature feature = new Feature(layer, geometry, source.id()); + Feature feature = new Feature(layer, geometry, source); output.add(feature); return feature; } @@ -81,7 +83,7 @@ public class FeatureCollector implements Iterable { return geometry(layer, source.worldGeometry()); } catch (GeometryException e) { e.log(stats, "feature_point", "Error getting point geometry for " + source.id()); - return new Feature(layer, EMPTY_GEOM, source.id()); + return new Feature(layer, EMPTY_GEOM, source); } } @@ -101,7 +103,7 @@ public class FeatureCollector implements Iterable { return geometry(layer, source.line()); } catch (GeometryException e) { e.log(stats, "feature_line", "Error constructing line for " + source.id()); - return new Feature(layer, EMPTY_GEOM, source.id()); + return new Feature(layer, EMPTY_GEOM, source); } } @@ -121,7 +123,7 @@ public class FeatureCollector implements Iterable { return geometry(layer, source.polygon()); } catch (GeometryException e) { e.log(stats, "feature_polygon", "Error constructing polygon for " + source.id()); - return new Feature(layer, EMPTY_GEOM, source.id()); + return new Feature(layer, EMPTY_GEOM, source); } } @@ -136,7 +138,7 @@ public class FeatureCollector implements Iterable { return geometry(layer, source.centroid()); } catch (GeometryException e) { e.log(stats, "feature_centroid", "Error getting centroid for " + source.id()); - return new Feature(layer, EMPTY_GEOM, source.id()); + return new Feature(layer, EMPTY_GEOM, source); } } @@ -153,7 +155,7 @@ public class FeatureCollector implements Iterable { return geometry(layer, source.centroidIfConvex()); } catch (GeometryException e) { e.log(stats, "feature_centroid_if_convex", "Error constructing centroid if convex for " + source.id()); - return new Feature(layer, EMPTY_GEOM, source.id()); + return new Feature(layer, EMPTY_GEOM, source); } } @@ -169,7 +171,7 @@ public class FeatureCollector implements Iterable { return geometry(layer, source.pointOnSurface()); } catch (GeometryException e) { e.log(stats, "feature_point_on_surface", "Error constructing point on surface for " + source.id()); - return new Feature(layer, EMPTY_GEOM, source.id()); + return new Feature(layer, EMPTY_GEOM, source); } } @@ -191,7 +193,7 @@ public class FeatureCollector implements Iterable { return geometry(layer, source.innermostPoint(tolerance)); } catch (GeometryException e) { e.log(stats, "feature_innermost_point", "Error constructing innermost point for " + source.id()); - return new Feature(layer, EMPTY_GEOM, source.id()); + return new Feature(layer, EMPTY_GEOM, source); } } @@ -275,11 +277,21 @@ public class FeatureCollector implements Iterable { private String numPointsAttr = null; - private Feature(String layer, Geometry geom, long id) { + private Feature(String layer, Geometry geom, SourceFeature source) { this.layer = layer; this.geom = geom; this.geometryType = GeometryType.typeOf(geom); - this.id = id; + if (source instanceof OsmSourceFeature osmSourceFeature) { + long osmId = osmSourceFeature.originalElement().id(); + this.id = switch (osmSourceFeature.originalElement()) { + case OsmElement.Node node -> node.id() * 10 + 1; + case OsmElement.Way way -> way.id() * 10 + 2; + case OsmElement.Relation relation -> relation.id() * 10 + 3; + default -> osmId * 10; + }; + } else { + this.id = source.id(); + } if (geometryType == GeometryType.POINT) { minPixelSizeAtMaxZoom = 0; defaultMinPixelSize = 0; diff --git a/planetiler-core/src/test/java/com/onthegomap/planetiler/FeatureCollectorTest.java b/planetiler-core/src/test/java/com/onthegomap/planetiler/FeatureCollectorTest.java index e2001402..8f9d956a 100644 --- a/planetiler-core/src/test/java/com/onthegomap/planetiler/FeatureCollectorTest.java +++ b/planetiler-core/src/test/java/com/onthegomap/planetiler/FeatureCollectorTest.java @@ -653,4 +653,28 @@ class FeatureCollectorTest { ) ), collector); } + + @Test + void testOsmFeat() throws GeometryException { + var pointSourceFeature = newReaderFeature(newPoint(0, 0), Map.of()); + assertEquals(0, pointSourceFeature.area()); + assertEquals(0, pointSourceFeature.length()); + + var fc = factory.get(pointSourceFeature); + fc.line("layer").setZoomRange(0, 10); + fc.polygon("layer").setZoomRange(0, 10); + assertFalse(fc.iterator().hasNext(), "silently fail coercing to line/polygon"); + fc.point("layer").setZoomRange(0, 10); + fc.centroid("layer").setZoomRange(0, 10); + fc.pointOnSurface("layer").setZoomRange(0, 10); + var iter = fc.iterator(); + for (int i = 0; i < 3; i++) { + assertTrue(iter.hasNext(), "item " + i); + var item = iter.next(); + assertEquals(GeometryType.POINT, item.getGeometryType()); + assertEquals(newPoint(0.5, 0.5), item.getGeometry()); + } + + assertFalse(iter.hasNext()); + } } diff --git a/planetiler-core/src/test/java/com/onthegomap/planetiler/PlanetilerTests.java b/planetiler-core/src/test/java/com/onthegomap/planetiler/PlanetilerTests.java index 610af5d9..a1bf43b9 100644 --- a/planetiler-core/src/test/java/com/onthegomap/planetiler/PlanetilerTests.java +++ b/planetiler-core/src/test/java/com/onthegomap/planetiler/PlanetilerTests.java @@ -875,7 +875,7 @@ class PlanetilerTests { feature(newPoint(128, 128), Map.of( "attr", "value", "name", "name value" - )) + )).withId(11) ) ), results.tiles); } @@ -964,7 +964,7 @@ class PlanetilerTests { feature(newLineString(128, 128, 192, 192), Map.of( "attr", "value", "name", "name value" - )) + )).withId(32) ) ), results.tiles); } @@ -1089,7 +1089,7 @@ class PlanetilerTests { "attr", "value", "name", "name value", "relname", "rel name" - )) + )).withId(173) ) ), results.tiles); } diff --git a/planetiler-core/src/test/java/com/onthegomap/planetiler/TestUtils.java b/planetiler-core/src/test/java/com/onthegomap/planetiler/TestUtils.java index 603d28e5..6579fa09 100644 --- a/planetiler-core/src/test/java/com/onthegomap/planetiler/TestUtils.java +++ b/planetiler-core/src/test/java/com/onthegomap/planetiler/TestUtils.java @@ -279,7 +279,9 @@ public class TestUtils { case UNKNOWN -> throw new IllegalArgumentException("cannot decompress \"UNKNOWN\""); }; var decoded = VectorTile.decode(bytes).stream() - .map(feature -> feature(decodeSilently(feature.geometry()), feature.layer(), feature.attrs())).toList(); + .map( + feature -> feature(decodeSilently(feature.geometry()), feature.layer(), feature.attrs()).withId(feature.id())) + .toList(); tiles.put(tile.coord(), decoded); } return tiles; @@ -467,12 +469,21 @@ public class TestUtils { public record ComparableFeature( GeometryComparision geometry, String layer, - Map attrs + Map attrs, + Long id ) { + ComparableFeature( + GeometryComparision geometry, + String layer, + Map attrs + ) { + this(geometry, layer, attrs, null); + } @Override public boolean equals(Object o) { return o == this || (o instanceof ComparableFeature other && + (id == null || other.id == null || id.equals(other.id)) && geometry.equals(other.geometry) && attrs.equals(other.attrs) && (layer == null || other.layer == null || Objects.equals(layer, other.layer))); @@ -484,6 +495,10 @@ public class TestUtils { result = 31 * result + attrs.hashCode(); return result; } + + ComparableFeature withId(long id) { + return new ComparableFeature(geometry, layer, attrs, id); + } } public static ComparableFeature feature(Geometry geom, String layer, Map attrs) {