Encode OSM node/way/relation in vector tile feature id (#826)

pull/943/head
Michael Barry 2024-06-28 07:00:56 -04:00 zatwierdzone przez GitHub
rodzic 07afc77fbb
commit db0eb8afb8
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: B5690EEEBB952194
9 zmienionych plików z 79 dodań i 14 usunięć

Wyświetl plik

@ -64,7 +64,11 @@ public class FeatureCollector implements Iterable<FeatureCollector.Feature> {
* @return a feature that can be configured further. * @return a feature that can be configured further.
*/ */
public Feature geometry(String layer, Geometry geometry) { public Feature geometry(String layer, Geometry geometry) {
Feature feature = new Feature(layer, geometry, source.id()); // TODO args could also provide a list of source IDs to put into slot 4, 5, 6, etc..
// to differentiate between other sources besides just OSM and "other"
long vectorTileId = config.featureSourceIdMultiplier() < 4 ? source.id() :
source.vectorTileFeatureId(config.featureSourceIdMultiplier());
Feature feature = new Feature(layer, geometry, vectorTileId);
output.add(feature); output.add(feature);
return feature; return feature;
} }

Wyświetl plik

@ -445,7 +445,11 @@ public class Arguments {
*/ */
public int getInteger(String key, String description, int defaultValue) { public int getInteger(String key, String description, int defaultValue) {
String value = getArg(key, Integer.toString(defaultValue)); String value = getArg(key, Integer.toString(defaultValue));
int parsed = Integer.parseInt(value); int parsed = switch (value.toLowerCase(Locale.ROOT)) {
case "false" -> 0;
case "true" -> defaultValue;
default -> Integer.parseInt(value);
};
logArgValue(key, description, parsed); logArgValue(key, description, parsed);
return parsed; return parsed;
} }

Wyświetl plik

@ -60,7 +60,8 @@ public record PlanetilerConfig(
Path tmpDir, Path tmpDir,
Path tileWeights, Path tileWeights,
double maxPointBuffer, double maxPointBuffer,
boolean logJtsExceptions boolean logJtsExceptions,
int featureSourceIdMultiplier
) { ) {
public static final int MIN_MINZOOM = 0; public static final int MIN_MINZOOM = 0;
@ -213,7 +214,11 @@ public record PlanetilerConfig(
"clients that handle label collisions across tiles (most web and native clients). NOTE: Do not reduce if you need to support " + "clients that handle label collisions across tiles (most web and native clients). NOTE: Do not reduce if you need to support " +
"raster tile rendering", "raster tile rendering",
Double.POSITIVE_INFINITY), Double.POSITIVE_INFINITY),
arguments.getBoolean("log_jts_exceptions", "Emit verbose details to debug JTS geometry errors", false) arguments.getBoolean("log_jts_exceptions", "Emit verbose details to debug JTS geometry errors", false),
arguments.getInteger("feature_source_id_multiplier",
"Set vector tile feature IDs to (featureId * thisValue) + sourceId " +
"where sourceId is 1 for OSM nodes, 2 for ways, 3 for relations, and 0 for other sources. Set to false to disable.",
10)
); );
} }

Wyświetl plik

@ -320,6 +320,10 @@ public abstract class SourceFeature implements WithTags, WithGeometryType {
return id; return id;
} }
/** By default, the feature id is taken as-is from the input data source id. */
public long vectorTileFeatureId(int multiplier) {
return multiplier * id;
}
/** Returns true if this element has any OSM relation info. */ /** Returns true if this element has any OSM relation info. */
public boolean hasRelationInfo() { public boolean hasRelationInfo() {

Wyświetl plik

@ -27,6 +27,7 @@ public interface OsmElement extends WithTags {
Type type(); Type type();
enum Type { enum Type {
OTHER,
NODE, NODE,
WAY, WAY,
RELATION RELATION
@ -46,7 +47,7 @@ public interface OsmElement extends WithTags {
@Override @Override
public Type type() { public Type type() {
return null; return Type.OTHER;
} }
} }

Wyświetl plik

@ -644,6 +644,16 @@ public class OsmReader implements Closeable, MemoryEstimator.HasEstimate {
this.polygon = polygon; this.polygon = polygon;
} }
@Override
public long vectorTileFeatureId(int multiplier) {
return (id() * multiplier) + switch (originalElement.type()) {
case OTHER -> 0;
case NODE -> 1;
case WAY -> 2;
case RELATION -> 3;
};
}
@Override @Override
public Geometry latLonGeometry() throws GeometryException { public Geometry latLonGeometry() throws GeometryException {
return latLonGeom != null ? latLonGeom : (latLonGeom = GeoUtils.worldToLatLonCoords(worldGeometry())); return latLonGeom != null ? latLonGeom : (latLonGeom = GeoUtils.worldToLatLonCoords(worldGeometry()));

Wyświetl plik

@ -942,7 +942,7 @@ class PlanetilerTests {
feature(newPoint(128, 128), Map.of( feature(newPoint(128, 128), Map.of(
"attr", "value", "attr", "value",
"name", "name value" "name", "name value"
)) ), 11)
) )
), results.tiles); ), results.tiles);
} }
@ -1031,7 +1031,7 @@ class PlanetilerTests {
feature(newLineString(128, 128, 192, 192), Map.of( feature(newLineString(128, 128, 192, 192), Map.of(
"attr", "value", "attr", "value",
"name", "name value" "name", "name value"
)) ), 32)
) )
), results.tiles); ), results.tiles);
} }
@ -1157,7 +1157,7 @@ class PlanetilerTests {
"attr", "value", "attr", "value",
"name", "name value", "name", "name value",
"relname", "rel name" "relname", "rel name"
)) ), 173)
) )
), results.tiles); ), results.tiles);
} }
@ -1219,20 +1219,21 @@ class PlanetilerTests {
@Test @Test
void testPreprocessOsmNodesAndWays() throws Exception { void testPreprocessOsmNodesAndWays() throws Exception {
Set<Long> nodes1 = new HashSet<>(); HashMap<Long, Long> nodes1 = new HashMap<>();
Set<Long> nodes2 = new HashSet<>(); Set<Long> nodes2 = new HashSet<>();
var profile = new Profile.NullProfile() { var profile = new Profile.NullProfile() {
@Override @Override
public void preprocessOsmNode(OsmElement.Node node) { public void preprocessOsmNode(OsmElement.Node node) {
if (node.hasTag("a", "b")) { if (node.hasTag("a", "b")) {
nodes1.add(node.id()); nodes1.put(node.id(), node.id());
} }
} }
@Override @Override
public void preprocessOsmWay(OsmElement.Way way) { public void preprocessOsmWay(OsmElement.Way way) {
if (nodes1.contains(way.nodes().get(0))) { Long featureId = nodes1.get(way.nodes().get(0));
nodes2.add(way.nodes().get(0)); if (featureId != null) {
nodes2.add(featureId);
} }
} }

Wyświetl plik

@ -284,7 +284,9 @@ public class TestUtils {
case UNKNOWN -> throw new IllegalArgumentException("cannot decompress \"UNKNOWN\""); case UNKNOWN -> throw new IllegalArgumentException("cannot decompress \"UNKNOWN\"");
}; };
var decoded = VectorTile.decode(bytes).stream() var decoded = VectorTile.decode(bytes).stream()
.map(feature -> feature(decodeSilently(feature.geometry()), feature.layer(), feature.tags())).toList(); .map(
feature -> feature(decodeSilently(feature.geometry()), feature.layer(), feature.tags(), feature.id()))
.toList();
tiles.put(tile.coord(), decoded); tiles.put(tile.coord(), decoded);
} }
return tiles; return tiles;
@ -490,12 +492,21 @@ public class TestUtils {
public record ComparableFeature( public record ComparableFeature(
GeometryComparision geometry, GeometryComparision geometry,
String layer, String layer,
Map<String, Object> attrs Map<String, Object> attrs,
Long id
) { ) {
ComparableFeature(
GeometryComparision geometry,
String layer,
Map<String, Object> attrs
) {
this(geometry, layer, attrs, null);
}
@Override @Override
public boolean equals(Object o) { public boolean equals(Object o) {
return o == this || (o instanceof ComparableFeature other && return o == this || (o instanceof ComparableFeature other &&
(id == null || other.id == null || id.equals(other.id)) &&
geometry.equals(other.geometry) && geometry.equals(other.geometry) &&
attrs.equals(other.attrs) && attrs.equals(other.attrs) &&
(layer == null || other.layer == null || Objects.equals(layer, other.layer))); (layer == null || other.layer == null || Objects.equals(layer, other.layer)));
@ -507,12 +518,25 @@ public class TestUtils {
result = 31 * result + attrs.hashCode(); result = 31 * result + attrs.hashCode();
return result; return result;
} }
ComparableFeature withId(long id) {
return new ComparableFeature(geometry, layer, attrs, id);
}
}
public static ComparableFeature feature(Geometry geom, String layer, Map<String, Object> attrs, long id) {
return new ComparableFeature(new NormGeometry(geom), layer, attrs, id);
} }
public static ComparableFeature feature(Geometry geom, String layer, Map<String, Object> attrs) { public static ComparableFeature feature(Geometry geom, String layer, Map<String, Object> attrs) {
return new ComparableFeature(new NormGeometry(geom), layer, attrs); return new ComparableFeature(new NormGeometry(geom), layer, attrs);
} }
public static ComparableFeature feature(Geometry geom, Map<String, Object> attrs, long id) {
return new ComparableFeature(new NormGeometry(geom), null, attrs, id);
}
public static ComparableFeature feature(Geometry geom, Map<String, Object> attrs) { public static ComparableFeature feature(Geometry geom, Map<String, Object> attrs) {
return new ComparableFeature(new NormGeometry(geom), null, attrs); return new ComparableFeature(new NormGeometry(geom), null, attrs);
} }

Wyświetl plik

@ -363,4 +363,16 @@ class ArgumentsTest {
assertEquals("val_1", args.getArg("key-1")); assertEquals("val_1", args.getArg("key-1"));
assertNull(args.getArg("key-3")); assertNull(args.getArg("key-3"));
} }
@Test
void testFalseForInt() {
var args = Arguments.of(Map.of(
"true", "true",
"false", "false",
"3", "3"
));
assertEquals(1, args.getInteger("true", "", 1));
assertEquals(0, args.getInteger("false", "", 1));
assertEquals(3, args.getInteger("3", "", 1));
}
} }