kopia lustrzana https://github.com/onthegomap/planetiler
				
				
				
			Encode OSM node/way/relation in vector tile feature id (#826)
							rodzic
							
								
									07afc77fbb
								
							
						
					
					
						commit
						db0eb8afb8
					
				| 
						 | 
					@ -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;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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)
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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() {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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()));
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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));
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Ładowanie…
	
		Reference in New Issue