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