kopia lustrzana https://github.com/onthegomap/planetiler
				
				
				
			OSM QA Tiles Example Profile (#278)
							rodzic
							
								
									4b7a6018c9
								
							
						
					
					
						commit
						c6ad30cc9a
					
				| 
						 | 
				
			
			@ -15,10 +15,12 @@ import java.nio.file.Path;
 | 
			
		|||
public class BenchmarkOsmRead {
 | 
			
		||||
 | 
			
		||||
  public static void main(String[] args) throws IOException {
 | 
			
		||||
    OsmInputFile file = new OsmInputFile(Path.of("data/sources/northeast.osm.pbf"), true);
 | 
			
		||||
    var profile = new Profile.NullProfile();
 | 
			
		||||
    var stats = Stats.inMemory();
 | 
			
		||||
    var config = PlanetilerConfig.from(Arguments.of());
 | 
			
		||||
    var parsedArgs = Arguments.fromArgsOrConfigFile(args);
 | 
			
		||||
    var config = PlanetilerConfig.from(parsedArgs);
 | 
			
		||||
    var path = parsedArgs.inputFile("osm_path", "path to osm file", Path.of("data/sources/northeast.osm.pbf"));
 | 
			
		||||
    OsmInputFile file = new OsmInputFile(path, config.osmLazyReads());
 | 
			
		||||
 | 
			
		||||
    while (true) {
 | 
			
		||||
      Timer timer = Timer.start();
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -43,7 +43,8 @@ public record PlanetilerConfig(
 | 
			
		|||
  double simplifyToleranceBelowMaxZoom,
 | 
			
		||||
  boolean osmLazyReads,
 | 
			
		||||
  boolean compactDb,
 | 
			
		||||
  boolean skipFilledTiles
 | 
			
		||||
  boolean skipFilledTiles,
 | 
			
		||||
  int tileWarningSizeBytes
 | 
			
		||||
) {
 | 
			
		||||
 | 
			
		||||
  public static final int MIN_MINZOOM = 0;
 | 
			
		||||
| 
						 | 
				
			
			@ -148,7 +149,10 @@ public record PlanetilerConfig(
 | 
			
		|||
        true),
 | 
			
		||||
      arguments.getBoolean("skip_filled_tiles",
 | 
			
		||||
        "Skip writing tiles containing only polygon fills to the output",
 | 
			
		||||
        false)
 | 
			
		||||
        false),
 | 
			
		||||
      (int) (arguments.getDouble("tile_warning_size_mb",
 | 
			
		||||
        "Maximum size in megabytes of a tile to emit a warning about",
 | 
			
		||||
        1d) * 1024 * 1024)
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -756,13 +756,17 @@ public final class Mbtiles implements Closeable {
 | 
			
		|||
 | 
			
		||||
    public Metadata setMetadata(String name, Object value) {
 | 
			
		||||
      if (value != null) {
 | 
			
		||||
        LOGGER.debug("Set mbtiles metadata: {}={}", name, value);
 | 
			
		||||
        String stringValue = value.toString();
 | 
			
		||||
        LOGGER.debug("Set mbtiles metadata: {}={}", name,
 | 
			
		||||
          stringValue.length() > 1_000 ?
 | 
			
		||||
            (stringValue.substring(0, 1_000) + "... " + (stringValue.length() - 1_000) + " more characters") :
 | 
			
		||||
            stringValue);
 | 
			
		||||
        try (
 | 
			
		||||
          PreparedStatement statement = connection.prepareStatement(
 | 
			
		||||
            "INSERT INTO " + METADATA_TABLE + " (" + METADATA_COL_NAME + "," + METADATA_COL_VALUE + ") VALUES(?, ?);")
 | 
			
		||||
        ) {
 | 
			
		||||
          statement.setString(1, name);
 | 
			
		||||
          statement.setString(2, value.toString());
 | 
			
		||||
          statement.setString(2, stringValue);
 | 
			
		||||
          statement.execute();
 | 
			
		||||
        } catch (SQLException throwables) {
 | 
			
		||||
          LOGGER.error("Error setting metadata " + name + "=" + value, throwables);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -283,7 +283,7 @@ public class MbtilesWriter {
 | 
			
		|||
          } else {
 | 
			
		||||
            encoded = en.encode();
 | 
			
		||||
            bytes = gzip(encoded);
 | 
			
		||||
            if (encoded.length > 1_000_000) {
 | 
			
		||||
            if (encoded.length > config.tileWarningSizeBytes()) {
 | 
			
		||||
              LOGGER.warn("{} {}kb uncompressed",
 | 
			
		||||
                tileFeatures.tileCoord(),
 | 
			
		||||
                encoded.length / 1024);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -20,6 +20,8 @@ public interface OsmElement extends WithTags {
 | 
			
		|||
  /** OSM element ID */
 | 
			
		||||
  long id();
 | 
			
		||||
 | 
			
		||||
  Info info();
 | 
			
		||||
 | 
			
		||||
  int cost();
 | 
			
		||||
 | 
			
		||||
  enum Type {
 | 
			
		||||
| 
						 | 
				
			
			@ -31,12 +33,13 @@ public interface OsmElement extends WithTags {
 | 
			
		|||
  /** An un-handled element read from the .osm.pbf file (i.e. file header). */
 | 
			
		||||
  record Other(
 | 
			
		||||
    @Override long id,
 | 
			
		||||
    @Override Map<String, Object> tags
 | 
			
		||||
    @Override Map<String, Object> tags,
 | 
			
		||||
    @Override Info info
 | 
			
		||||
  ) implements OsmElement {
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public int cost() {
 | 
			
		||||
      return 1 + tags.size();
 | 
			
		||||
      return 1 + tags.size() + (info == null ? 0 : Info.COST);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -48,6 +51,7 @@ public interface OsmElement extends WithTags {
 | 
			
		|||
    private final Map<String, Object> tags;
 | 
			
		||||
    private final double lat;
 | 
			
		||||
    private final double lon;
 | 
			
		||||
    private final Info info;
 | 
			
		||||
    // bailed out of a record to make encodedLocation lazy since it is fairly expensive to compute
 | 
			
		||||
    private long encodedLocation = MISSING_LOCATION;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -55,16 +59,27 @@ public interface OsmElement extends WithTags {
 | 
			
		|||
      long id,
 | 
			
		||||
      Map<String, Object> tags,
 | 
			
		||||
      double lat,
 | 
			
		||||
      double lon
 | 
			
		||||
      double lon,
 | 
			
		||||
      Info info
 | 
			
		||||
    ) {
 | 
			
		||||
      this.id = id;
 | 
			
		||||
      this.tags = tags;
 | 
			
		||||
      this.lat = lat;
 | 
			
		||||
      this.lon = lon;
 | 
			
		||||
      this.info = info;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public Node(long id, double lat, double lon) {
 | 
			
		||||
      this(id, new HashMap<>(), lat, lon);
 | 
			
		||||
      this(id, new HashMap<>(), lat, lon, null);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public Node(
 | 
			
		||||
      long id,
 | 
			
		||||
      Map<String, Object> tags,
 | 
			
		||||
      double lat,
 | 
			
		||||
      double lon
 | 
			
		||||
    ) {
 | 
			
		||||
      this(id, tags, lat, lon, null);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
| 
						 | 
				
			
			@ -72,6 +87,11 @@ public interface OsmElement extends WithTags {
 | 
			
		|||
      return id;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public Info info() {
 | 
			
		||||
      return info;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public Map<String, Object> tags() {
 | 
			
		||||
      return tags;
 | 
			
		||||
| 
						 | 
				
			
			@ -94,7 +114,7 @@ public interface OsmElement extends WithTags {
 | 
			
		|||
 | 
			
		||||
    @Override
 | 
			
		||||
    public int cost() {
 | 
			
		||||
      return 1 + tags.size();
 | 
			
		||||
      return 1 + tags.size() + (info == null ? 0 : Info.COST);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
| 
						 | 
				
			
			@ -109,12 +129,13 @@ public interface OsmElement extends WithTags {
 | 
			
		|||
      return this.id == that.id &&
 | 
			
		||||
        Objects.equals(this.tags, that.tags) &&
 | 
			
		||||
        Double.doubleToLongBits(this.lat) == Double.doubleToLongBits(that.lat) &&
 | 
			
		||||
        Double.doubleToLongBits(this.lon) == Double.doubleToLongBits(that.lon);
 | 
			
		||||
        Double.doubleToLongBits(this.lon) == Double.doubleToLongBits(that.lon) &&
 | 
			
		||||
        Objects.equals(this.info, that.info);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public int hashCode() {
 | 
			
		||||
      return Objects.hash(id, tags, lat, lon);
 | 
			
		||||
      return Objects.hash(id, tags, lat, lon, info);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
| 
						 | 
				
			
			@ -123,7 +144,8 @@ public interface OsmElement extends WithTags {
 | 
			
		|||
        "id=" + id + ", " +
 | 
			
		||||
        "tags=" + tags + ", " +
 | 
			
		||||
        "lat=" + lat + ", " +
 | 
			
		||||
        "lon=" + lon + ']';
 | 
			
		||||
        "lon=" + lon + ", " +
 | 
			
		||||
        "info=" + info + ']';
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
  }
 | 
			
		||||
| 
						 | 
				
			
			@ -132,16 +154,21 @@ public interface OsmElement extends WithTags {
 | 
			
		|||
  record Way(
 | 
			
		||||
    @Override long id,
 | 
			
		||||
    @Override Map<String, Object> tags,
 | 
			
		||||
    LongArrayList nodes
 | 
			
		||||
    LongArrayList nodes,
 | 
			
		||||
    @Override Info info
 | 
			
		||||
  ) implements OsmElement {
 | 
			
		||||
 | 
			
		||||
    public Way(long id) {
 | 
			
		||||
      this(id, new HashMap<>(), new LongArrayList(5));
 | 
			
		||||
      this(id, new HashMap<>(), new LongArrayList(5), null);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public Way(long id, Map<String, Object> tags, LongArrayList nodes) {
 | 
			
		||||
      this(id, tags, nodes, null);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public int cost() {
 | 
			
		||||
      return 1 + tags.size() + nodes.size();
 | 
			
		||||
      return 1 + tags.size() + nodes.size() + (info == null ? 0 : Info.COST);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -149,11 +176,16 @@ public interface OsmElement extends WithTags {
 | 
			
		|||
  record Relation(
 | 
			
		||||
    @Override long id,
 | 
			
		||||
    @Override Map<String, Object> tags,
 | 
			
		||||
    List<Member> members
 | 
			
		||||
    List<Member> members,
 | 
			
		||||
    @Override Info info
 | 
			
		||||
  ) implements OsmElement {
 | 
			
		||||
 | 
			
		||||
    public Relation(long id) {
 | 
			
		||||
      this(id, new HashMap<>(), new ArrayList<>());
 | 
			
		||||
      this(id, new HashMap<>(), new ArrayList<>(), null);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public Relation(long id, Map<String, Object> tags, List<Member> members) {
 | 
			
		||||
      this(id, tags, members, null);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public Relation {
 | 
			
		||||
| 
						 | 
				
			
			@ -164,7 +196,7 @@ public interface OsmElement extends WithTags {
 | 
			
		|||
 | 
			
		||||
    @Override
 | 
			
		||||
    public int cost() {
 | 
			
		||||
      return 1 + tags.size() + members.size() * 3;
 | 
			
		||||
      return 1 + tags.size() + members.size() * 3 + (info == null ? 0 : Info.COST);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
| 
						 | 
				
			
			@ -176,4 +208,8 @@ public interface OsmElement extends WithTags {
 | 
			
		|||
      String role
 | 
			
		||||
    ) {}
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  record Info(long changeset, long timestamp, int userId, int version, String user) {
 | 
			
		||||
    private static final int COST = 2;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -621,17 +621,20 @@ public class OsmReader implements Closeable, MemoryEstimator.HasEstimate {
 | 
			
		|||
   * A source feature generated from OSM elements. Inferring the geometry can be expensive, so each subclass is
 | 
			
		||||
   * constructed with the inputs necessary to create the geometry, but the geometry is constructed lazily on read.
 | 
			
		||||
   */
 | 
			
		||||
  private abstract class OsmFeature extends SourceFeature {
 | 
			
		||||
  private abstract class OsmFeature extends SourceFeature implements OsmSourceFeature {
 | 
			
		||||
 | 
			
		||||
    private final OsmElement originalElement;
 | 
			
		||||
    private final boolean polygon;
 | 
			
		||||
    private final boolean line;
 | 
			
		||||
    private final boolean point;
 | 
			
		||||
    private Geometry latLonGeom;
 | 
			
		||||
    private Geometry worldGeom;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    public OsmFeature(OsmElement elem, boolean point, boolean line, boolean polygon,
 | 
			
		||||
      List<RelationMember<OsmRelationInfo>> relationInfo) {
 | 
			
		||||
      super(elem.tags(), name, null, relationInfo, elem.id());
 | 
			
		||||
      this.originalElement = elem;
 | 
			
		||||
      this.point = point;
 | 
			
		||||
      this.line = line;
 | 
			
		||||
      this.polygon = polygon;
 | 
			
		||||
| 
						 | 
				
			
			@ -663,6 +666,11 @@ public class OsmReader implements Closeable, MemoryEstimator.HasEstimate {
 | 
			
		|||
    public boolean canBePolygon() {
 | 
			
		||||
      return polygon;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public OsmElement originalElement() {
 | 
			
		||||
      return originalElement;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /** A {@link Point} created from an OSM node. */
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,5 @@
 | 
			
		|||
package com.onthegomap.planetiler.reader.osm;
 | 
			
		||||
 | 
			
		||||
public interface OsmSourceFeature {
 | 
			
		||||
  OsmElement originalElement();
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -149,7 +149,8 @@ public class PbfDecoder implements Iterable<OsmElement> {
 | 
			
		|||
        node.getId(),
 | 
			
		||||
        buildTags(node.getKeysCount(), node::getKeys, node::getVals),
 | 
			
		||||
        fieldDecoder.decodeLatitude(node.getLat()),
 | 
			
		||||
        fieldDecoder.decodeLongitude(node.getLon())
 | 
			
		||||
        fieldDecoder.decodeLongitude(node.getLon()),
 | 
			
		||||
        parseInfo(node.getInfo())
 | 
			
		||||
      );
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
| 
						 | 
				
			
			@ -198,7 +199,8 @@ public class PbfDecoder implements Iterable<OsmElement> {
 | 
			
		|||
      return new OsmElement.Relation(
 | 
			
		||||
        relation.getId(),
 | 
			
		||||
        buildTags(relation.getKeysCount(), relation::getKeys, relation::getVals),
 | 
			
		||||
        members
 | 
			
		||||
        members,
 | 
			
		||||
        parseInfo(relation.getInfo())
 | 
			
		||||
      );
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
| 
						 | 
				
			
			@ -241,27 +243,40 @@ public class PbfDecoder implements Iterable<OsmElement> {
 | 
			
		|||
      return new OsmElement.Way(
 | 
			
		||||
        way.getId(),
 | 
			
		||||
        buildTags(way.getKeysCount(), way::getKeys, way::getVals),
 | 
			
		||||
        wayNodesList
 | 
			
		||||
        wayNodesList,
 | 
			
		||||
        parseInfo(way.getInfo())
 | 
			
		||||
      );
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private OsmElement.Info parseInfo(Osmformat.Info info) {
 | 
			
		||||
    return info == null ? null : new OsmElement.Info(
 | 
			
		||||
      info.getChangeset(),
 | 
			
		||||
      info.getTimestamp(),
 | 
			
		||||
      info.getUid(),
 | 
			
		||||
      info.getVersion(),
 | 
			
		||||
      fieldDecoder.decodeString(info.getUserSid())
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private class DenseNodeIterator implements Iterator<OsmElement.Node> {
 | 
			
		||||
 | 
			
		||||
    final Osmformat.DenseNodes nodes;
 | 
			
		||||
    long nodeId;
 | 
			
		||||
    long latitude;
 | 
			
		||||
    long longitude;
 | 
			
		||||
    int i;
 | 
			
		||||
    int kvIndex;
 | 
			
		||||
    final Osmformat.DenseInfo denseInfo;
 | 
			
		||||
    long nodeId = 0;
 | 
			
		||||
    long latitude = 0;
 | 
			
		||||
    long longitude = 0;
 | 
			
		||||
    int i = 0;
 | 
			
		||||
    int kvIndex = 0;
 | 
			
		||||
    // info
 | 
			
		||||
    long timestamp = 0;
 | 
			
		||||
    long changeset = 0;
 | 
			
		||||
    int uid = 0;
 | 
			
		||||
    int userSid = 0;
 | 
			
		||||
 | 
			
		||||
    public DenseNodeIterator(Osmformat.DenseNodes nodes) {
 | 
			
		||||
      this.nodes = nodes;
 | 
			
		||||
      nodeId = 0;
 | 
			
		||||
      latitude = 0;
 | 
			
		||||
      longitude = 0;
 | 
			
		||||
      i = 0;
 | 
			
		||||
      kvIndex = 0;
 | 
			
		||||
      this.denseInfo = nodes.getDenseinfo();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -279,6 +294,16 @@ public class PbfDecoder implements Iterable<OsmElement> {
 | 
			
		|||
      nodeId += nodes.getId(i);
 | 
			
		||||
      latitude += nodes.getLat(i);
 | 
			
		||||
      longitude += nodes.getLon(i);
 | 
			
		||||
      int version = 0;
 | 
			
		||||
 | 
			
		||||
      if (denseInfo != null) {
 | 
			
		||||
        version = denseInfo.getVersion(i);
 | 
			
		||||
        timestamp += denseInfo.getTimestamp(i);
 | 
			
		||||
        changeset += denseInfo.getChangeset(i);
 | 
			
		||||
        uid += denseInfo.getUid(i);
 | 
			
		||||
        userSid += denseInfo.getUserSid(i);
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      i++;
 | 
			
		||||
 | 
			
		||||
      // Build the tags. The key and value string indexes are sequential
 | 
			
		||||
| 
						 | 
				
			
			@ -304,7 +329,14 @@ public class PbfDecoder implements Iterable<OsmElement> {
 | 
			
		|||
        nodeId,
 | 
			
		||||
        tags == null ? Collections.emptyMap() : tags,
 | 
			
		||||
        ((double) latitude) / 10000000,
 | 
			
		||||
        ((double) longitude) / 10000000
 | 
			
		||||
        ((double) longitude) / 10000000,
 | 
			
		||||
        denseInfo == null ? null : new OsmElement.Info(
 | 
			
		||||
          changeset,
 | 
			
		||||
          timestamp,
 | 
			
		||||
          uid,
 | 
			
		||||
          version,
 | 
			
		||||
          fieldDecoder.decodeString(userSid)
 | 
			
		||||
        )
 | 
			
		||||
      );
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -24,7 +24,7 @@ class OsmInputFileTest {
 | 
			
		|||
  private final OsmElement.Node expectedNode = new OsmElement.Node(1737114566L, Map.of(
 | 
			
		||||
    "highway", "crossing",
 | 
			
		||||
    "crossing", "zebra"
 | 
			
		||||
  ), 43.7409723, 7.4303278);
 | 
			
		||||
  ), 43.7409723, 7.4303278, new OsmElement.Info(0, 1600807207, 0, 5, ""));
 | 
			
		||||
  private final OsmElement.Way expectedWay = new OsmElement.Way(4097656L, Map.of(
 | 
			
		||||
    "name", "Avenue Princesse Alice",
 | 
			
		||||
    "lanes", "2",
 | 
			
		||||
| 
						 | 
				
			
			@ -35,7 +35,7 @@ class OsmInputFileTest {
 | 
			
		|||
  ), LongArrayList.from(
 | 
			
		||||
    21912089L, 7265761724L, 1079750744L, 2104793864L, 6340961560L, 1110560507L, 21912093L, 6340961559L, 21912095L,
 | 
			
		||||
    7265762803L, 2104793866L, 6340961561L, 5603088200L, 6340961562L, 21912097L, 21912099L
 | 
			
		||||
  ));
 | 
			
		||||
  ), new OsmElement.Info(0, 1583398246, 0, 13, ""));
 | 
			
		||||
  private final OsmElement.Relation expectedRel = new OsmElement.Relation(7360630L, Map.of(
 | 
			
		||||
    "local_ref", "Saint-Roman",
 | 
			
		||||
    "public_transport:version", "2",
 | 
			
		||||
| 
						 | 
				
			
			@ -51,7 +51,7 @@ class OsmInputFileTest {
 | 
			
		|||
    new OsmElement.Relation.Member(OsmElement.Type.NODE, 3465728159L, "stop"),
 | 
			
		||||
    new OsmElement.Relation.Member(OsmElement.Type.NODE, 4939122068L, "platform"),
 | 
			
		||||
    new OsmElement.Relation.Member(OsmElement.Type.NODE, 3805333988L, "stop")
 | 
			
		||||
  ));
 | 
			
		||||
  ), new OsmElement.Info(0, 1586074405, 0, 2, ""));
 | 
			
		||||
  private final Envelope expectedBounds = new Envelope(7.409205, 7.448637, 43.72335, 43.75169);
 | 
			
		||||
 | 
			
		||||
  @Test
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,11 +1,14 @@
 | 
			
		|||
package com.onthegomap.planetiler;
 | 
			
		||||
 | 
			
		||||
import static java.util.Map.entry;
 | 
			
		||||
 | 
			
		||||
import com.onthegomap.planetiler.basemap.BasemapMain;
 | 
			
		||||
import com.onthegomap.planetiler.basemap.util.VerifyMonaco;
 | 
			
		||||
import com.onthegomap.planetiler.benchmarks.BasemapMapping;
 | 
			
		||||
import com.onthegomap.planetiler.benchmarks.LongLongMapBench;
 | 
			
		||||
import com.onthegomap.planetiler.custommap.ConfiguredMapMain;
 | 
			
		||||
import com.onthegomap.planetiler.examples.BikeRouteOverlay;
 | 
			
		||||
import com.onthegomap.planetiler.examples.OsmQaTiles;
 | 
			
		||||
import com.onthegomap.planetiler.examples.ToiletsOverlay;
 | 
			
		||||
import com.onthegomap.planetiler.examples.ToiletsOverlayLowLevelApi;
 | 
			
		||||
import com.onthegomap.planetiler.mbtiles.Verify;
 | 
			
		||||
| 
						 | 
				
			
			@ -20,17 +23,19 @@ import java.util.Map;
 | 
			
		|||
public class Main {
 | 
			
		||||
 | 
			
		||||
  private static final EntryPoint DEFAULT_TASK = BasemapMain::main;
 | 
			
		||||
  private static final Map<String, EntryPoint> ENTRY_POINTS = Map.of(
 | 
			
		||||
    "generate-basemap", BasemapMain::main,
 | 
			
		||||
    "generate-custom", ConfiguredMapMain::main,
 | 
			
		||||
    "basemap", BasemapMain::main,
 | 
			
		||||
    "example-bikeroutes", BikeRouteOverlay::main,
 | 
			
		||||
    "example-toilets", ToiletsOverlay::main,
 | 
			
		||||
    "example-toilets-lowlevel", ToiletsOverlayLowLevelApi::main,
 | 
			
		||||
    "benchmark-mapping", BasemapMapping::main,
 | 
			
		||||
    "benchmark-longlongmap", LongLongMapBench::main,
 | 
			
		||||
    "verify-mbtiles", Verify::main,
 | 
			
		||||
    "verify-monaco", VerifyMonaco::main
 | 
			
		||||
  private static final Map<String, EntryPoint> ENTRY_POINTS = Map.ofEntries(
 | 
			
		||||
    entry("generate-basemap", BasemapMain::main),
 | 
			
		||||
    entry("generate-custom", ConfiguredMapMain::main),
 | 
			
		||||
    entry("basemap", BasemapMain::main),
 | 
			
		||||
    entry("example-bikeroutes", BikeRouteOverlay::main),
 | 
			
		||||
    entry("example-toilets", ToiletsOverlay::main),
 | 
			
		||||
    entry("example-toilets-lowlevel", ToiletsOverlayLowLevelApi::main),
 | 
			
		||||
    entry("example-qa", OsmQaTiles::main),
 | 
			
		||||
    entry("osm-qa", OsmQaTiles::main),
 | 
			
		||||
    entry("benchmark-mapping", BasemapMapping::main),
 | 
			
		||||
    entry("benchmark-longlongmap", LongLongMapBench::main),
 | 
			
		||||
    entry("verify-mbtiles", Verify::main),
 | 
			
		||||
    entry("verify-monaco", VerifyMonaco::main)
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
  public static void main(String[] args) throws Exception {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,107 @@
 | 
			
		|||
package com.onthegomap.planetiler.examples;
 | 
			
		||||
 | 
			
		||||
import com.onthegomap.planetiler.FeatureCollector;
 | 
			
		||||
import com.onthegomap.planetiler.Planetiler;
 | 
			
		||||
import com.onthegomap.planetiler.Profile;
 | 
			
		||||
import com.onthegomap.planetiler.config.Arguments;
 | 
			
		||||
import com.onthegomap.planetiler.reader.SourceFeature;
 | 
			
		||||
import com.onthegomap.planetiler.reader.osm.OsmElement;
 | 
			
		||||
import com.onthegomap.planetiler.reader.osm.OsmSourceFeature;
 | 
			
		||||
import java.nio.file.Path;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Generates tiles with a raw copy of OSM data in a single "osm" layer at one zoom level, similar to
 | 
			
		||||
 * <a href="http://osmlab.github.io/osm-qa-tiles/">OSM QA Tiles</a>.
 | 
			
		||||
 * <p>
 | 
			
		||||
 * Nodes are mapped to points and ways are mapped to polygons or linestrings, and multipolygon relations are mapped to
 | 
			
		||||
 * polygons. Each output feature contains all key/value tags from the input feature, plus these extra attributes:
 | 
			
		||||
 * <ul>
 | 
			
		||||
 * <li>{@code @type}: node, way, or relation</li>
 | 
			
		||||
 * <li>{@code @id}: OSM element ID</li>
 | 
			
		||||
 * <li>{@code @changeset}: Changeset that last modified the element</li>
 | 
			
		||||
 * <li>{@code @timestamp}: Timestamp at which the element was last modified</li>
 | 
			
		||||
 * <li>{@code @version}: Version number of the OSM element</li>
 | 
			
		||||
 * <li>{@code @uid}: User ID that last modified the element</li>
 | 
			
		||||
 * <li>{@code @user}: User name that last modified the element</li>
 | 
			
		||||
 * </ul>
 | 
			
		||||
 * <p>
 | 
			
		||||
 * To run this example:
 | 
			
		||||
 * <ol>
 | 
			
		||||
 * <li>build the examples: {@code mvn clean package}</li>
 | 
			
		||||
 * <li>then run this example:
 | 
			
		||||
 * {@code java -cp target/*-fatjar.jar com.onthegomap.planetiler.examples.OsmQaTiles --area=monaco --download}</li>
 | 
			
		||||
 * <li>then run the demo tileserver: {@code tileserver-gl-light data/output.mbtiles}</li>
 | 
			
		||||
 * <li>and view the output at <a href="http://localhost:8080">localhost:8080</a></li>
 | 
			
		||||
 * </ol>
 | 
			
		||||
 */
 | 
			
		||||
public class OsmQaTiles implements Profile {
 | 
			
		||||
 | 
			
		||||
  public static void main(String[] args) throws Exception {
 | 
			
		||||
    run(Arguments.fromArgsOrConfigFile(args));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  static void run(Arguments inArgs) throws Exception {
 | 
			
		||||
    int zoom = inArgs.getInteger("zoom", "zoom level to generate tiles at", 12);
 | 
			
		||||
    var args = inArgs.orElse(Arguments.of(
 | 
			
		||||
      "minzoom", zoom,
 | 
			
		||||
      "maxzoom", zoom,
 | 
			
		||||
      "tile_warning_size_mb", 100
 | 
			
		||||
    ));
 | 
			
		||||
    String area = args.getString("area", "geofabrik area to download", "monaco");
 | 
			
		||||
    Planetiler.create(args)
 | 
			
		||||
      .setProfile(new OsmQaTiles())
 | 
			
		||||
      .addOsmSource("osm",
 | 
			
		||||
        Path.of("data", "sources", area + ".osm.pbf"),
 | 
			
		||||
        "planet".equalsIgnoreCase(area) ? "aws:latest" : ("geofabrik:" + area)
 | 
			
		||||
      )
 | 
			
		||||
      .overwriteOutput("mbtiles", Path.of("data", "qa.mbtiles"))
 | 
			
		||||
      .run();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @Override
 | 
			
		||||
  public void processFeature(SourceFeature sourceFeature, FeatureCollector features) {
 | 
			
		||||
    if (!sourceFeature.tags().isEmpty() && sourceFeature instanceof OsmSourceFeature osmFeature) {
 | 
			
		||||
      var feature = sourceFeature.canBePolygon() ? features.polygon("osm") :
 | 
			
		||||
        sourceFeature.canBeLine() ? features.line("osm") :
 | 
			
		||||
        sourceFeature.isPoint() ? features.point("osm") :
 | 
			
		||||
        null;
 | 
			
		||||
      if (feature != null) {
 | 
			
		||||
        var element = osmFeature.originalElement();
 | 
			
		||||
        feature
 | 
			
		||||
          .setMinPixelSize(0)
 | 
			
		||||
          .setPixelTolerance(0)
 | 
			
		||||
          .setBufferPixels(0);
 | 
			
		||||
        for (var entry : sourceFeature.tags().entrySet()) {
 | 
			
		||||
          feature.setAttr(entry.getKey(), entry.getValue());
 | 
			
		||||
        }
 | 
			
		||||
        feature
 | 
			
		||||
          .setAttr("@id", sourceFeature.id())
 | 
			
		||||
          .setAttr("@type", element instanceof OsmElement.Node ? "node" :
 | 
			
		||||
            element instanceof OsmElement.Way ? "way" :
 | 
			
		||||
            element instanceof OsmElement.Relation ? "relation" : null
 | 
			
		||||
          );
 | 
			
		||||
        var info = element.info();
 | 
			
		||||
        if (info != null) {
 | 
			
		||||
          feature
 | 
			
		||||
            .setAttr("@version", info.version() == 0 ? null : info.version())
 | 
			
		||||
            .setAttr("@timestamp", info.timestamp() == 0L ? null : info.timestamp())
 | 
			
		||||
            .setAttr("@changeset", info.changeset() == 0L ? null : info.changeset())
 | 
			
		||||
            .setAttr("@uid", info.userId() == 0 ? null : info.userId())
 | 
			
		||||
            .setAttr("@user", info.user() == null || info.user().isBlank() ? null : info.user());
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @Override
 | 
			
		||||
  public String name() {
 | 
			
		||||
    return "osm qa";
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @Override
 | 
			
		||||
  public String attribution() {
 | 
			
		||||
    return """
 | 
			
		||||
      <a href="https://www.openstreetmap.org/copyright" target="_blank">© OpenStreetMap contributors</a>
 | 
			
		||||
      """.trim();
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,138 @@
 | 
			
		|||
package com.onthegomap.planetiler.examples;
 | 
			
		||||
 | 
			
		||||
import static com.onthegomap.planetiler.TestUtils.assertContains;
 | 
			
		||||
import static org.junit.jupiter.api.Assertions.assertEquals;
 | 
			
		||||
 | 
			
		||||
import com.onthegomap.planetiler.TestUtils;
 | 
			
		||||
import com.onthegomap.planetiler.config.Arguments;
 | 
			
		||||
import com.onthegomap.planetiler.geo.GeoUtils;
 | 
			
		||||
import com.onthegomap.planetiler.mbtiles.Mbtiles;
 | 
			
		||||
import com.onthegomap.planetiler.reader.SourceFeature;
 | 
			
		||||
import com.onthegomap.planetiler.reader.osm.OsmElement;
 | 
			
		||||
import com.onthegomap.planetiler.reader.osm.OsmSourceFeature;
 | 
			
		||||
import java.nio.file.Path;
 | 
			
		||||
import java.util.Map;
 | 
			
		||||
import org.junit.jupiter.api.Test;
 | 
			
		||||
import org.junit.jupiter.api.io.TempDir;
 | 
			
		||||
import org.locationtech.jts.geom.Geometry;
 | 
			
		||||
import org.locationtech.jts.geom.LineString;
 | 
			
		||||
import org.locationtech.jts.geom.Point;
 | 
			
		||||
import org.locationtech.jts.geom.Polygon;
 | 
			
		||||
 | 
			
		||||
class OsmQaTilesTest {
 | 
			
		||||
 | 
			
		||||
  private final OsmQaTiles profile = new OsmQaTiles();
 | 
			
		||||
 | 
			
		||||
  @Test
 | 
			
		||||
  void testNode() {
 | 
			
		||||
    Map<String, Object> tags = Map.of("key", "value");
 | 
			
		||||
    class TestNode extends SourceFeature implements OsmSourceFeature {
 | 
			
		||||
      TestNode() {
 | 
			
		||||
        super(tags, "source", "layer", null, 0);
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      @Override
 | 
			
		||||
      public Geometry latLonGeometry() {
 | 
			
		||||
        return null;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      @Override
 | 
			
		||||
      public Geometry worldGeometry() {
 | 
			
		||||
        return TestUtils.newPoint(
 | 
			
		||||
          0.5, 0.5
 | 
			
		||||
        );
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      @Override
 | 
			
		||||
      public boolean isPoint() {
 | 
			
		||||
        return true;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      @Override
 | 
			
		||||
      public boolean canBePolygon() {
 | 
			
		||||
        return false;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      @Override
 | 
			
		||||
      public boolean canBeLine() {
 | 
			
		||||
        return false;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      @Override
 | 
			
		||||
      public OsmElement originalElement() {
 | 
			
		||||
        return new OsmElement.Node(123, tags, 1, 1, new OsmElement.Info(1, 2, 3, 4, "user"));
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    var node = new TestNode();
 | 
			
		||||
    var mapFeatures = TestUtils.processSourceFeature(node, profile);
 | 
			
		||||
 | 
			
		||||
    // verify output attributes
 | 
			
		||||
    assertEquals(1, mapFeatures.size());
 | 
			
		||||
    var feature = mapFeatures.get(0);
 | 
			
		||||
    assertEquals("osm", feature.getLayer());
 | 
			
		||||
    assertEquals(Map.of(
 | 
			
		||||
      "key", "value",
 | 
			
		||||
      "@changeset", 1L,
 | 
			
		||||
      "@timestamp", 2L,
 | 
			
		||||
      "@id", 0L,
 | 
			
		||||
      "@type", "node",
 | 
			
		||||
      "@uid", 3,
 | 
			
		||||
      "@user", "user",
 | 
			
		||||
      "@version", 4
 | 
			
		||||
    ), feature.getAttrsAtZoom(12));
 | 
			
		||||
    assertEquals(0, feature.getBufferPixelsAtZoom(12), 1e-2);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @Test
 | 
			
		||||
  void integrationTest(@TempDir Path tmpDir) throws Exception {
 | 
			
		||||
    Path dbPath = tmpDir.resolve("output.mbtiles");
 | 
			
		||||
    OsmQaTiles.run(Arguments.of(
 | 
			
		||||
      // Override input source locations
 | 
			
		||||
      "osm_path", TestUtils.pathToResource("monaco-latest.osm.pbf"),
 | 
			
		||||
      // Override temp dir location
 | 
			
		||||
      "tmp", tmpDir.toString(),
 | 
			
		||||
      // Override output location
 | 
			
		||||
      "mbtiles", dbPath.toString()
 | 
			
		||||
    ));
 | 
			
		||||
    try (Mbtiles mbtiles = Mbtiles.newReadOnlyDatabase(dbPath)) {
 | 
			
		||||
      Map<String, String> metadata = mbtiles.metadata().getAll();
 | 
			
		||||
      assertEquals("osm qa", metadata.get("name"));
 | 
			
		||||
      assertContains("openstreetmap.org/copyright", metadata.get("attribution"));
 | 
			
		||||
 | 
			
		||||
      TestUtils
 | 
			
		||||
        .assertNumFeatures(mbtiles, "osm", 12, Map.of(
 | 
			
		||||
          "bus", "yes",
 | 
			
		||||
          "name", "Crémaillère",
 | 
			
		||||
          "public_transport", "stop_position",
 | 
			
		||||
          "@type", "node",
 | 
			
		||||
          "@version", 4L,
 | 
			
		||||
          "@id", 1699777833L
 | 
			
		||||
        ), GeoUtils.WORLD_LAT_LON_BOUNDS, 1, Point.class);
 | 
			
		||||
      TestUtils
 | 
			
		||||
        .assertNumFeatures(mbtiles, "osm", 12, Map.of(
 | 
			
		||||
          "boundary", "administrative",
 | 
			
		||||
          "admin_level", "10",
 | 
			
		||||
          "name", "Monte-Carlo",
 | 
			
		||||
          "wikipedia", "fr:Monte-Carlo",
 | 
			
		||||
          "ISO3166-2", "MC-MC",
 | 
			
		||||
          "type", "boundary",
 | 
			
		||||
          "wikidata", "Q45240",
 | 
			
		||||
          "@type", "relation",
 | 
			
		||||
          "@version", 9L,
 | 
			
		||||
          "@id", 5986438L
 | 
			
		||||
        ), GeoUtils.WORLD_LAT_LON_BOUNDS, 1, Polygon.class);
 | 
			
		||||
      TestUtils
 | 
			
		||||
        .assertNumFeatures(mbtiles, "osm", 12, Map.of(
 | 
			
		||||
          "name", "Avenue de la Costa",
 | 
			
		||||
          "maxspeed", "50",
 | 
			
		||||
          "lit", "yes",
 | 
			
		||||
          "surface", "asphalt",
 | 
			
		||||
          "lanes", "2",
 | 
			
		||||
          "@type", "way",
 | 
			
		||||
          "@version", 5L,
 | 
			
		||||
          "@id", 166009791L
 | 
			
		||||
        ), GeoUtils.WORLD_LAT_LON_BOUNDS, 1, LineString.class);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
		Ładowanie…
	
		Reference in New Issue