kopia lustrzana https://github.com/onthegomap/planetiler
example and integration tests
rodzic
6c6b9c976a
commit
9c05837fcf
|
@ -126,7 +126,7 @@
|
|||
<testResources>
|
||||
<testResource>
|
||||
<directory>src/test/resources</directory>
|
||||
<filtering>true</filtering>
|
||||
<filtering>false</filtering>
|
||||
</testResource>
|
||||
</testResources>
|
||||
<pluginManagement>
|
||||
|
|
|
@ -55,7 +55,11 @@ public class FlatMapRunner {
|
|||
}
|
||||
|
||||
public static FlatMapRunner create() {
|
||||
return new FlatMapRunner(Arguments.fromJvmProperties());
|
||||
return createWithArguments(Arguments.fromJvmProperties());
|
||||
}
|
||||
|
||||
public static FlatMapRunner createWithArguments(Arguments arguments) {
|
||||
return new FlatMapRunner(arguments);
|
||||
}
|
||||
|
||||
private LongLongMap getLongLongMap() {
|
||||
|
|
|
@ -24,6 +24,13 @@ public record TileCoord(int encoded, int x, int y, int z) implements Comparable<
|
|||
return new TileCoord(encoded, x, y, z);
|
||||
}
|
||||
|
||||
public static TileCoord aroundLngLat(double lng, double lat, int zoom) {
|
||||
double factor = 1 << zoom;
|
||||
double x = GeoUtils.getWorldX(lng) * factor;
|
||||
double y = GeoUtils.getWorldY(lat) * factor;
|
||||
return TileCoord.ofXYZ((int) Math.floor(x), (int) Math.floor(y), zoom);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
|
@ -98,4 +105,11 @@ public record TileCoord(int encoded, int x, int y, int z) implements Comparable<
|
|||
Coordinate coord = getLatLon();
|
||||
return "https://www.openstreetmap.org/#map=" + z + "/" + format.format(coord.y) + "/" + format.format(coord.x);
|
||||
}
|
||||
|
||||
public Coordinate lngLatToTileCoords(double lng, double lat) {
|
||||
double factor = 1 << z;
|
||||
double x = GeoUtils.getWorldX(lng) * factor;
|
||||
double y = GeoUtils.getWorldY(lat) * factor;
|
||||
return new CoordinateXY((x - Math.floor(x)) * 256, (y - Math.floor(y)) * 256);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -186,6 +186,10 @@ public final class Mbtiles implements Closeable {
|
|||
return getTileStatement;
|
||||
}
|
||||
|
||||
public byte[] getTile(TileCoord coord) {
|
||||
return getTile(coord.x(), coord.y(), coord.z());
|
||||
}
|
||||
|
||||
public byte[] getTile(int x, int y, int z) {
|
||||
try {
|
||||
PreparedStatement stmt = getTileStatement();
|
||||
|
|
|
@ -62,6 +62,7 @@ public class NaturalEarthReader extends Reader {
|
|||
extracted = toOpen;
|
||||
try (var zipFs = FileSystems.newFileSystem(path)) {
|
||||
var zipEntry = FileUtils.walkFileSystem(zipFs)
|
||||
.filter(Files::isRegularFile)
|
||||
.filter(entry -> FileUtils.hasExtension(entry, "sqlite"))
|
||||
.findFirst()
|
||||
.orElseThrow(() -> new IllegalArgumentException("No .sqlite file found inside " + path));
|
||||
|
|
|
@ -20,6 +20,11 @@ public class ReaderFeature extends SourceFeature {
|
|||
this(latLonGeometry, tags, null, null, id);
|
||||
}
|
||||
|
||||
public ReaderFeature(Geometry latLonGeometry, Map<String, Object> tags, long id,
|
||||
List<OsmReader.RelationMember<OsmReader.RelationInfo>> relations) {
|
||||
this(latLonGeometry, tags, null, null, id, relations);
|
||||
}
|
||||
|
||||
public ReaderFeature(Geometry latLonGeometry, Map<String, Object> tags, String source, String sourceLayer,
|
||||
long id) {
|
||||
this(latLonGeometry, tags, source, sourceLayer, id, null);
|
||||
|
|
|
@ -9,7 +9,6 @@ import com.graphhopper.coll.GHIntObjectHashMap;
|
|||
import com.onthegomap.flatmap.geo.GeometryException;
|
||||
import com.onthegomap.flatmap.mbiles.Mbtiles;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
@ -571,7 +570,7 @@ public class FeatureMergeTest {
|
|||
})
|
||||
public void testMergeManyPolygons(String file, int x, int y, int z, int expected)
|
||||
throws IOException, GeometryException {
|
||||
try (var db = Mbtiles.newReadOnlyDatabase(Path.of("src", "test", "resources", file))) {
|
||||
try (var db = Mbtiles.newReadOnlyDatabase(TestUtils.pathToResource(file))) {
|
||||
byte[] tileData = db.getTile(x, y, z);
|
||||
byte[] gunzipped = TestUtils.gunzip(tileData);
|
||||
List<VectorTileEncoder.Feature> features = VectorTileEncoder.decode(gunzipped);
|
||||
|
|
|
@ -43,6 +43,7 @@ import java.util.function.Consumer;
|
|||
import java.util.function.Function;
|
||||
import java.util.stream.DoubleStream;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.io.TempDir;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.CsvSource;
|
||||
import org.locationtech.jts.geom.Coordinate;
|
||||
|
@ -673,7 +674,7 @@ public class FlatMapTest {
|
|||
public void testComplexShorelinePolygons__TAKES_A_MINUTE_OR_TWO(String fileName, int expected)
|
||||
throws Exception {
|
||||
MultiPolygon geometry = (MultiPolygon) new WKBReader()
|
||||
.read(new InputStreamInStream(Files.newInputStream(Path.of("src", "test", "resources", fileName))));
|
||||
.read(new InputStreamInStream(Files.newInputStream(TestUtils.pathToResource(fileName))));
|
||||
assertNotNull(geometry);
|
||||
|
||||
// automatically checks for self-intersections
|
||||
|
@ -1410,4 +1411,37 @@ public class FlatMapTest {
|
|||
|
||||
assertEquals(11, results.tiles.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFlatMapRunner(@TempDir Path tempDir) throws Exception {
|
||||
Path mbtiles = tempDir.resolve("output.mbtiles");
|
||||
FlatMapRunner.createWithArguments(Arguments.of("tmpdir", tempDir))
|
||||
.setProfile(new Profile.NullProfile() {
|
||||
@Override
|
||||
public void processFeature(SourceFeature source, FeatureCollector features) {
|
||||
if (source.canBePolygon() && source.hasTag("building", "yes")) {
|
||||
features.polygon("building").setZoomRange(0, 14).setMinPixelSize(1);
|
||||
}
|
||||
}
|
||||
})
|
||||
.addOsmSource("osm", TestUtils.pathToResource("monaco-latest.osm.pbf"))
|
||||
.addNaturalEarthSource("ne", TestUtils.pathToResource("natural_earth_vector.sqlite"))
|
||||
.addShapefileSource("shapefile", TestUtils.pathToResource("shapefile.zip"))
|
||||
.setOutput("mbtiles", mbtiles)
|
||||
.run();
|
||||
|
||||
try (Mbtiles db = Mbtiles.newReadOnlyDatabase(mbtiles)) {
|
||||
int features = 0;
|
||||
var tileMap = TestUtils.getTileMap(db);
|
||||
for (var tile : tileMap.values()) {
|
||||
for (var feature : tile) {
|
||||
feature.geometry().validate();
|
||||
features++;
|
||||
}
|
||||
}
|
||||
|
||||
assertEquals(11, tileMap.size(), "num tiles");
|
||||
assertEquals(2146, features, "num buildings");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,12 +14,17 @@ import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper;
|
|||
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
|
||||
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement;
|
||||
import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
|
||||
import com.onthegomap.flatmap.config.CommonParams;
|
||||
import com.onthegomap.flatmap.geo.GeoUtils;
|
||||
import com.onthegomap.flatmap.geo.GeometryException;
|
||||
import com.onthegomap.flatmap.geo.TileCoord;
|
||||
import com.onthegomap.flatmap.mbiles.Mbtiles;
|
||||
import com.onthegomap.flatmap.reader.SourceFeature;
|
||||
import com.onthegomap.flatmap.stats.Stats;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.net.URISyntaxException;
|
||||
import java.net.URL;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.sql.ResultSet;
|
||||
|
@ -31,8 +36,10 @@ import java.util.Comparator;
|
|||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.TreeMap;
|
||||
import java.util.TreeSet;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
|
@ -40,6 +47,7 @@ import org.locationtech.jts.algorithm.Orientation;
|
|||
import org.locationtech.jts.geom.Coordinate;
|
||||
import org.locationtech.jts.geom.CoordinateSequence;
|
||||
import org.locationtech.jts.geom.CoordinateXY;
|
||||
import org.locationtech.jts.geom.Envelope;
|
||||
import org.locationtech.jts.geom.Geometry;
|
||||
import org.locationtech.jts.geom.GeometryCollection;
|
||||
import org.locationtech.jts.geom.LineString;
|
||||
|
@ -299,6 +307,15 @@ public class TestUtils {
|
|||
validateGeometryRecursive(g);
|
||||
}
|
||||
|
||||
public static Path pathToResource(String resource) {
|
||||
URL url = Objects.requireNonNull(TestUtils.class.getResource("/" + resource));
|
||||
try {
|
||||
return Path.of(url.toURI());
|
||||
} catch (URISyntaxException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public interface GeometryComparision {
|
||||
|
||||
Geometry geom();
|
||||
|
@ -560,7 +577,122 @@ public class TestUtils {
|
|||
}
|
||||
|
||||
public static OsmXml readOsmXml(String s) throws IOException {
|
||||
Path path = Path.of("src", "test", "resources", s);
|
||||
Path path = pathToResource(s);
|
||||
return xmlMapper.readValue(Files.newInputStream(path), OsmXml.class);
|
||||
}
|
||||
|
||||
public static FeatureCollector newFeatureCollectorFor(SourceFeature feature) {
|
||||
var featureCollectorFactory = new FeatureCollector.Factory(
|
||||
CommonParams.defaults(),
|
||||
Stats.inMemory()
|
||||
);
|
||||
return featureCollectorFactory.get(feature);
|
||||
}
|
||||
|
||||
public static List<FeatureCollector.Feature> processSourceFeature(SourceFeature sourceFeature, Profile profile) {
|
||||
FeatureCollector collector = newFeatureCollectorFor(sourceFeature);
|
||||
profile.processFeature(sourceFeature, collector);
|
||||
List<FeatureCollector.Feature> result = new ArrayList<>();
|
||||
collector.forEach(result::add);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
public static void assertContains(String substring, String string) {
|
||||
if (!string.contains(substring)) {
|
||||
fail("'%s' did not contain '%s'".formatted(string, substring));
|
||||
}
|
||||
}
|
||||
|
||||
public static void assertNumFeatures(Mbtiles db, String layer, int zoom, Map<String, Object> attrs,
|
||||
Envelope envelope,
|
||||
int expected, Class<? extends Geometry> clazz) {
|
||||
try {
|
||||
int num = 0;
|
||||
for (var tileCoord : db.getAllTileCoords()) {
|
||||
Envelope tileEnv = new Envelope();
|
||||
tileEnv.expandToInclude(tileCoord.lngLatToTileCoords(envelope.getMinX(), envelope.getMinY()));
|
||||
tileEnv.expandToInclude(tileCoord.lngLatToTileCoords(envelope.getMaxX(), envelope.getMaxY()));
|
||||
if (tileCoord.z() == zoom) {
|
||||
byte[] data = db.getTile(tileCoord);
|
||||
for (var feature : VectorTileEncoder.decode(gunzip(data))) {
|
||||
if (layer.equals(feature.layer()) && feature.attrs().entrySet().containsAll(attrs.entrySet())) {
|
||||
Geometry geometry = feature.geometry().decode();
|
||||
num += getGeometryCounts(geometry, clazz);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
assertEquals(expected, num, "z%d features in %s".formatted(zoom, layer));
|
||||
} catch (IOException | GeometryException e) {
|
||||
fail(e);
|
||||
}
|
||||
}
|
||||
|
||||
private static int getGeometryCounts(Geometry geom, Class<? extends Geometry> clazz) {
|
||||
int count = 0;
|
||||
if (geom instanceof GeometryCollection geometryCollection) {
|
||||
for (int i = 0; i < geometryCollection.getNumGeometries(); i++) {
|
||||
count += getGeometryCounts(geometryCollection.getGeometryN(i), clazz);
|
||||
}
|
||||
} else if (clazz.isInstance(geom)) {
|
||||
count = 1;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
public static void assertFeatureNear(Mbtiles db, String layer, Map<String, Object> attrs, double lng, double lat,
|
||||
int minzoom, int maxzoom) {
|
||||
try {
|
||||
List<String> failures = new ArrayList<>();
|
||||
outer:
|
||||
for (int zoom = 0; zoom <= 14; zoom++) {
|
||||
boolean shouldFind = zoom >= minzoom && zoom <= maxzoom;
|
||||
var coord = TileCoord.aroundLngLat(lng, lat, zoom);
|
||||
Geometry tilePoint = GeoUtils.point(coord.lngLatToTileCoords(lng, lat));
|
||||
byte[] tile = db.getTile(coord);
|
||||
List<VectorTileEncoder.Feature> features = tile == null ? List.of() : VectorTileEncoder.decode(gunzip(tile));
|
||||
|
||||
Set<String> containedInLayers = new TreeSet<>();
|
||||
Set<String> containedInLayerFeatures = new TreeSet<>();
|
||||
for (var feature : features) {
|
||||
if (feature.geometry().decode().isWithinDistance(tilePoint, 2)) {
|
||||
containedInLayers.add(feature.layer());
|
||||
if (layer.equals(feature.layer())) {
|
||||
Map<String, Object> tags = feature.attrs();
|
||||
containedInLayerFeatures.add(tags.toString());
|
||||
if (tags.entrySet().containsAll(attrs.entrySet())) {
|
||||
// found a match
|
||||
if (!shouldFind) {
|
||||
failures.add("z%d found feature but should not have".formatted(zoom));
|
||||
}
|
||||
continue outer;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// not found
|
||||
if (shouldFind) {
|
||||
if (containedInLayers.isEmpty()) {
|
||||
failures.add("z%d no features were found in any layer".formatted(zoom));
|
||||
} else if (!containedInLayers.contains(layer)) {
|
||||
failures.add("z%d features found in %s but not %s".formatted(
|
||||
zoom, containedInLayers, layer
|
||||
));
|
||||
} else {
|
||||
failures.add("z%d features found in %s but had wrong tags: %s".formatted(
|
||||
zoom, layer, containedInLayerFeatures.stream().collect(Collectors.joining("\n", "\n", "")))
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!failures.isEmpty()) {
|
||||
fail(String.join("\n", failures));
|
||||
}
|
||||
} catch (GeometryException | IOException e) {
|
||||
fail(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
|
|||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import com.onthegomap.flatmap.Profile;
|
||||
import com.onthegomap.flatmap.TestUtils;
|
||||
import com.onthegomap.flatmap.geo.GeoUtils;
|
||||
import com.onthegomap.flatmap.stats.Stats;
|
||||
import com.onthegomap.flatmap.worker.WorkerPipeline;
|
||||
|
@ -24,10 +25,10 @@ public class NaturalEarthReaderTest {
|
|||
@ValueSource(strings = {"natural_earth_vector.sqlite", "natural_earth_vector.sqlite.zip"})
|
||||
@Timeout(30)
|
||||
public void testReadNaturalEarth(String filename, @TempDir Path tempDir) {
|
||||
var path = Path.of("src", "test", "resources", filename);
|
||||
var path = TestUtils.pathToResource(filename);
|
||||
try (var reader = new NaturalEarthReader("test", path, tempDir, new Profile.NullProfile(), Stats.inMemory())) {
|
||||
for (int i = 1; i <= 2; i++) {
|
||||
assertEquals(19, reader.getCount(), "iter " + i);
|
||||
assertEquals(7_679, reader.getCount(), "iter " + i);
|
||||
|
||||
List<Geometry> points = new ArrayList<>();
|
||||
WorkerPipeline.start("test", Stats.inMemory())
|
||||
|
@ -35,10 +36,11 @@ public class NaturalEarthReaderTest {
|
|||
.addBuffer("reader_queue", 100, 1)
|
||||
.sinkToConsumer("counter", 1, elem -> {
|
||||
Object elevation = elem.getTag("elevation");
|
||||
assertTrue(elevation instanceof Double, Objects.toString(elevation));
|
||||
assertEquals("test", elem.getSource());
|
||||
assertEquals("ne_110m_geography_regions_elevation_points", elem.getSourceLayer());
|
||||
points.add(elem.latLonGeometry());
|
||||
if ("ne_110m_geography_regions_elevation_points".equals(elem.getSourceLayer())) {
|
||||
assertTrue(elevation instanceof Double, Objects.toString(elevation));
|
||||
assertEquals("test", elem.getSource());
|
||||
points.add(elem.latLonGeometry());
|
||||
}
|
||||
}).await();
|
||||
assertEquals(19, points.size());
|
||||
var gc = GeoUtils.JTS_FACTORY.createGeometryCollection(points.toArray(new Geometry[0]));
|
||||
|
|
|
@ -5,10 +5,10 @@ import static org.junit.jupiter.api.Assertions.assertNull;
|
|||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import com.onthegomap.flatmap.Profile;
|
||||
import com.onthegomap.flatmap.TestUtils;
|
||||
import com.onthegomap.flatmap.geo.GeoUtils;
|
||||
import com.onthegomap.flatmap.stats.Stats;
|
||||
import com.onthegomap.flatmap.worker.WorkerPipeline;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
|
@ -20,7 +20,7 @@ public class ShapefileReaderTest {
|
|||
|
||||
private ShapefileReader reader = new ShapefileReader(
|
||||
"test",
|
||||
Path.of("src", "test", "resources", "shapefile.zip"),
|
||||
TestUtils.pathToResource("shapefile.zip"),
|
||||
new Profile.NullProfile(),
|
||||
Stats.inMemory()
|
||||
);
|
||||
|
|
|
@ -3,9 +3,9 @@ package com.onthegomap.flatmap.reader.osm;
|
|||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
import com.graphhopper.reader.ReaderElement;
|
||||
import com.onthegomap.flatmap.TestUtils;
|
||||
import com.onthegomap.flatmap.stats.Stats;
|
||||
import com.onthegomap.flatmap.worker.WorkerPipeline;
|
||||
import java.nio.file.Path;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.Timeout;
|
||||
|
@ -13,7 +13,7 @@ import org.locationtech.jts.geom.Envelope;
|
|||
|
||||
public class OsmInputFileTest {
|
||||
|
||||
private OsmInputFile file = new OsmInputFile(Path.of("src", "test", "resources", "monaco-latest.osm.pbf"));
|
||||
private OsmInputFile file = new OsmInputFile(TestUtils.pathToResource("monaco-latest.osm.pbf"));
|
||||
|
||||
@Test
|
||||
public void testGetBounds() {
|
||||
|
|
Plik binarny nie jest wyświetlany.
Plik binarny nie jest wyświetlany.
Plik binarny nie jest wyświetlany.
|
@ -18,6 +18,15 @@
|
|||
<artifactId>flatmap-core</artifactId>
|
||||
<version>${project.parent.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- To use test utilities: -->
|
||||
<dependency>
|
||||
<groupId>com.onthegomap</groupId>
|
||||
<artifactId>flatmap-core</artifactId>
|
||||
<version>${project.parent.version}</version>
|
||||
<type>test-jar</type>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
|
|
|
@ -5,6 +5,7 @@ import com.onthegomap.flatmap.FeatureMerge;
|
|||
import com.onthegomap.flatmap.FlatMapRunner;
|
||||
import com.onthegomap.flatmap.Profile;
|
||||
import com.onthegomap.flatmap.VectorTileEncoder;
|
||||
import com.onthegomap.flatmap.config.Arguments;
|
||||
import com.onthegomap.flatmap.geo.GeometryException;
|
||||
import com.onthegomap.flatmap.reader.SourceFeature;
|
||||
import com.onthegomap.flatmap.reader.osm.OsmElement;
|
||||
|
@ -161,9 +162,13 @@ public class BikeRouteOverlay implements Profile {
|
|||
* Main entrypoint for this example program
|
||||
*/
|
||||
public static void main(String[] args) throws Exception {
|
||||
run(Arguments.fromJvmProperties());
|
||||
}
|
||||
|
||||
static void run(Arguments args) throws Exception {
|
||||
// FlatMapRunner is a convenience wrapper around the lower-level API for the most common use-cases.
|
||||
// See ToiletsOverlayLowLevelApi for an example using the lower-level API
|
||||
FlatMapRunner.create()
|
||||
FlatMapRunner.createWithArguments(args)
|
||||
.setProfile(new BikeRouteOverlay())
|
||||
// override this default with -Dosm="path/to/data.osm.pbf"
|
||||
.addOsmSource("osm", Path.of("data", "sources", "north-america_us_massachusetts.pbf"))
|
||||
|
|
|
@ -3,6 +3,7 @@ package com.onthegomap.flatmap.examples;
|
|||
import com.onthegomap.flatmap.FeatureCollector;
|
||||
import com.onthegomap.flatmap.FlatMapRunner;
|
||||
import com.onthegomap.flatmap.Profile;
|
||||
import com.onthegomap.flatmap.config.Arguments;
|
||||
import com.onthegomap.flatmap.reader.SourceFeature;
|
||||
import com.onthegomap.flatmap.util.ZoomFunction;
|
||||
import java.nio.file.Path;
|
||||
|
@ -89,9 +90,13 @@ public class ToiletsOverlay implements Profile {
|
|||
* Main entrypoint for the example program
|
||||
*/
|
||||
public static void main(String[] args) throws Exception {
|
||||
run(Arguments.fromJvmProperties());
|
||||
}
|
||||
|
||||
static void run(Arguments args) throws Exception {
|
||||
// FlatMapRunner is a convenience wrapper around the lower-level API for the most common use-cases.
|
||||
// See ToiletsOverlayLowLevelApi for an example using this same profile but the lower-level API
|
||||
FlatMapRunner.create()
|
||||
FlatMapRunner.createWithArguments(args)
|
||||
.setProfile(new ToiletsOverlay())
|
||||
// override this default with -Dosm="path/to/data.osm.pbf"
|
||||
.addOsmSource("osm", Path.of("data", "sources", "north-america_us_massachusetts.pbf"))
|
||||
|
|
|
@ -12,6 +12,7 @@ import com.onthegomap.flatmap.reader.osm.OsmInputFile;
|
|||
import com.onthegomap.flatmap.reader.osm.OsmReader;
|
||||
import com.onthegomap.flatmap.stats.Stats;
|
||||
import com.onthegomap.flatmap.util.FileUtils;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import org.slf4j.Logger;
|
||||
|
@ -35,15 +36,19 @@ public class ToiletsOverlayLowLevelApi {
|
|||
private static final Logger LOGGER = LoggerFactory.getLogger(ToiletsOverlayLowLevelApi.class);
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
run(
|
||||
Path.of("data", "sources", "north-america_us_massachusetts.pbf"),
|
||||
Path.of("data", "tmp"),
|
||||
Path.of("data", "toilets.mbtiles")
|
||||
);
|
||||
}
|
||||
|
||||
static void run(Path input, Path tmpDir, Path output) throws IOException {
|
||||
// Collect runtime statistics in memory. Alternatively you can push them to
|
||||
// prometheus using a push gateway (see https://github.com/prometheus/pushgateway)
|
||||
Stats stats = Stats.inMemory();
|
||||
Profile profile = new ToiletsOverlay();
|
||||
|
||||
Path input = Path.of("data", "sources", "north-america_us_massachusetts.pbf");
|
||||
Path tmpDir = Path.of("data", "tmp");
|
||||
Path output = Path.of("data", "toilets.mbtiles");
|
||||
|
||||
// use default settings, but allow overrides from -Dkey=value jvm arguments
|
||||
CommonParams config = CommonParams.from(Arguments.fromJvmProperties());
|
||||
|
||||
|
|
|
@ -0,0 +1,120 @@
|
|||
package com.onthegomap.flatmap.examples;
|
||||
|
||||
import static com.onthegomap.flatmap.TestUtils.assertContains;
|
||||
import static com.onthegomap.flatmap.TestUtils.newLineString;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
import com.onthegomap.flatmap.FeatureCollector;
|
||||
import com.onthegomap.flatmap.TestUtils;
|
||||
import com.onthegomap.flatmap.VectorTileEncoder;
|
||||
import com.onthegomap.flatmap.config.Arguments;
|
||||
import com.onthegomap.flatmap.geo.GeoUtils;
|
||||
import com.onthegomap.flatmap.geo.GeometryException;
|
||||
import com.onthegomap.flatmap.mbiles.Mbtiles;
|
||||
import com.onthegomap.flatmap.reader.ReaderFeature;
|
||||
import com.onthegomap.flatmap.reader.osm.OsmElement;
|
||||
import com.onthegomap.flatmap.reader.osm.OsmReader;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.io.TempDir;
|
||||
import org.locationtech.jts.geom.LineString;
|
||||
|
||||
public class BikeRouteOverlayTest {
|
||||
|
||||
private final BikeRouteOverlay profile = new BikeRouteOverlay();
|
||||
|
||||
@Test
|
||||
public void testSourceFeatureProcessing() {
|
||||
// step 1) preprocess an example OSM relation
|
||||
var relationResult = profile.preprocessOsmRelation(new OsmElement.Relation(1, Map.of(
|
||||
"type", "route",
|
||||
"route", "bicycle",
|
||||
"name", "rail trail",
|
||||
"network", "lcn",
|
||||
"ref", "1"
|
||||
), List.of(
|
||||
new OsmElement.Relation.Member(OsmElement.Type.WAY, 2, "role")
|
||||
)));
|
||||
|
||||
// step 2) process a way contained in that relation
|
||||
var way = new ReaderFeature(
|
||||
TestUtils.newLineString(
|
||||
10, 20, // point 1: 10 east 20 north
|
||||
30, 40 // point 2: 30 east 40 north
|
||||
),
|
||||
Map.of(), // way tags don't matter
|
||||
2,
|
||||
relationResult.stream().map(info -> new OsmReader.RelationMember<>("role", info)).toList()
|
||||
);
|
||||
List<FeatureCollector.Feature> mapFeatures = TestUtils.processSourceFeature(way, profile);
|
||||
|
||||
// verify output geometry
|
||||
assertEquals(1, mapFeatures.size());
|
||||
var feature = mapFeatures.get(0);
|
||||
assertEquals("bicycle-route-local", feature.getLayer());
|
||||
assertEquals(Map.of(
|
||||
"name", "rail trail",
|
||||
"ref", "1"
|
||||
), feature.getAttrsAtZoom(14));
|
||||
// output geometry is in world coordinates where 0,0 is top left and 1,1 is bottom right
|
||||
assertEquals(0.085, feature.getGeometry().getLength(), 1e-2);
|
||||
assertEquals(0, feature.getMinZoom());
|
||||
assertEquals(14, feature.getMaxZoom());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTilePostProcessingMergesConnectedLines() throws GeometryException {
|
||||
String layer = "bicycle-route-local";
|
||||
Map<String, Object> attrs = Map.of(
|
||||
"name", "rail trail",
|
||||
"ref", "1"
|
||||
);
|
||||
// segment 1: (0, 0) to (10, 0)
|
||||
var line1 = new VectorTileEncoder.Feature(layer, 1, // id
|
||||
VectorTileEncoder.encodeGeometry(newLineString(0, 0, 10, 0)),
|
||||
attrs
|
||||
);
|
||||
// segment 2: (10, 0) to (20, 0)
|
||||
var line2 = new VectorTileEncoder.Feature(layer, 2, // id
|
||||
VectorTileEncoder.encodeGeometry(newLineString(10, 0, 20, 0)),
|
||||
attrs
|
||||
);
|
||||
// merged: (0, 0) to (20, 0)
|
||||
var connected = new VectorTileEncoder.Feature(layer, 1, // id
|
||||
VectorTileEncoder.encodeGeometry(newLineString(0, 0, 20, 0)),
|
||||
attrs
|
||||
);
|
||||
|
||||
// ensure that 2 touching linestrings with same tags are merged
|
||||
assertEquals(
|
||||
List.of(connected),
|
||||
profile.postProcessLayerFeatures(layer, 14, List.of(line1, line2))
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void integrationTest(@TempDir Path tmpDir) throws Exception {
|
||||
Path dbPath = tmpDir.resolve("output.mbtiles");
|
||||
BikeRouteOverlay.run(Arguments.of(
|
||||
// Override input source locations
|
||||
"osm", 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("Bike Paths Overlay", metadata.get("name"));
|
||||
assertContains("openstreetmap.org/copyright", metadata.get("attribution"));
|
||||
|
||||
TestUtils
|
||||
.assertNumFeatures(mbtiles, "bicycle-route-international", 14, Map.of(
|
||||
"name", "EuroVelo 8 - Mediterranean Route - part Monaco",
|
||||
"ref", "EV8"
|
||||
), GeoUtils.WORLD_LAT_LON_BOUNDS, 25, LineString.class);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
package com.onthegomap.flatmap.examples;
|
||||
|
||||
import static com.onthegomap.flatmap.TestUtils.assertContains;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
import com.onthegomap.flatmap.TestUtils;
|
||||
import com.onthegomap.flatmap.geo.GeoUtils;
|
||||
import com.onthegomap.flatmap.mbiles.Mbtiles;
|
||||
import java.io.IOException;
|
||||
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.Point;
|
||||
|
||||
public class ToiletsOverlayLowLevelApiTest {
|
||||
|
||||
@Test
|
||||
public void integrationTest(@TempDir Path tmpDir) throws IOException {
|
||||
Path dbPath = tmpDir.resolve("output.mbtiles");
|
||||
ToiletsOverlayLowLevelApi.run(
|
||||
// Override input source locations
|
||||
TestUtils.pathToResource("monaco-latest.osm.pbf"),
|
||||
// Override temp dir location
|
||||
tmpDir,
|
||||
// Override output location
|
||||
dbPath
|
||||
);
|
||||
try (Mbtiles mbtiles = Mbtiles.newReadOnlyDatabase(dbPath)) {
|
||||
Map<String, String> metadata = mbtiles.metadata().getAll();
|
||||
assertEquals("Toilets Overlay", metadata.get("name"));
|
||||
assertContains("openstreetmap.org/copyright", metadata.get("attribution"));
|
||||
|
||||
TestUtils.assertNumFeatures(mbtiles, "toilets", 14, Map.of(), GeoUtils.WORLD_LAT_LON_BOUNDS,
|
||||
34, Point.class);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,73 @@
|
|||
package com.onthegomap.flatmap.examples;
|
||||
|
||||
import static com.onthegomap.flatmap.TestUtils.assertContains;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
import com.onthegomap.flatmap.FeatureCollector;
|
||||
import com.onthegomap.flatmap.TestUtils;
|
||||
import com.onthegomap.flatmap.config.Arguments;
|
||||
import com.onthegomap.flatmap.geo.GeoUtils;
|
||||
import com.onthegomap.flatmap.mbiles.Mbtiles;
|
||||
import com.onthegomap.flatmap.reader.ReaderFeature;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.io.TempDir;
|
||||
import org.locationtech.jts.geom.Point;
|
||||
|
||||
public class ToiletsProfileTest {
|
||||
|
||||
private final ToiletsOverlay profile = new ToiletsOverlay();
|
||||
|
||||
@Test
|
||||
public void testSourceFeatureProcessing() {
|
||||
var node = new ReaderFeature(
|
||||
TestUtils.newPoint(1, 2),
|
||||
Map.of("amenity", "toilets"),
|
||||
1 // node ID
|
||||
);
|
||||
List<FeatureCollector.Feature> mapFeatures = TestUtils.processSourceFeature(node, profile);
|
||||
|
||||
// verify output feature
|
||||
assertEquals(1, mapFeatures.size());
|
||||
var feature = mapFeatures.get(0);
|
||||
assertEquals("toilets", feature.getLayer());
|
||||
// no attributes
|
||||
assertEquals(Map.of(), feature.getAttrsAtZoom(14));
|
||||
assertEquals(0, feature.getMinZoom());
|
||||
assertEquals(14, feature.getMaxZoom());
|
||||
assertEquals(1, feature.getZorder());
|
||||
|
||||
// at z12 - use label grid to limit output
|
||||
assertEquals(4, feature.getLabelGridLimitAtZoom(12));
|
||||
assertEquals(32, feature.getLabelGridPixelSizeAtZoom(12));
|
||||
assertEquals(32, feature.getBufferPixelsAtZoom(12));
|
||||
|
||||
// at z13 - no label grid (0 disables filtering)
|
||||
assertEquals(0, feature.getLabelGridLimitAtZoom(13));
|
||||
assertEquals(0, feature.getLabelGridPixelSizeAtZoom(13));
|
||||
assertEquals(4, feature.getBufferPixelsAtZoom(13));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void integrationTest(@TempDir Path tmpDir) throws Exception {
|
||||
Path dbPath = tmpDir.resolve("output.mbtiles");
|
||||
ToiletsOverlay.run(Arguments.of(
|
||||
// Override input source locations
|
||||
"osm", 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("Toilets Overlay", metadata.get("name"));
|
||||
assertContains("openstreetmap.org/copyright", metadata.get("attribution"));
|
||||
|
||||
TestUtils.assertNumFeatures(mbtiles, "toilets", 14, Map.of(), GeoUtils.WORLD_LAT_LON_BOUNDS,
|
||||
36, Point.class);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -20,9 +20,11 @@ public class OpenMapTilesMain {
|
|||
private static final Path sourcesDir = Path.of("data", "sources");
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
run(Arguments.fromJvmProperties());
|
||||
}
|
||||
|
||||
FlatMapRunner runner = FlatMapRunner.create();
|
||||
|
||||
static void run(Arguments arguments) throws Exception {
|
||||
FlatMapRunner runner = FlatMapRunner.createWithArguments(arguments);
|
||||
runner
|
||||
.setProfile(createProfileWithWikidataTranslations(runner))
|
||||
.addShapefileSource("EPSG:3857", OpenMapTilesProfile.LAKE_CENTERLINE_SOURCE,
|
||||
|
|
|
@ -103,7 +103,7 @@ public class WaterName implements OpenMapTilesSchema.WaterName,
|
|||
public void processLakeCenterline(SourceFeature feature, FeatureCollector features) {
|
||||
long osmId = Math.abs(feature.getLong("OSM_ID"));
|
||||
if (osmId == 0L) {
|
||||
LOGGER.warn("Bad lake centerline: " + feature);
|
||||
LOGGER.warn("Bad lake centerline. Tags: " + feature.tags());
|
||||
} else {
|
||||
try {
|
||||
// multiple threads call this concurrently
|
||||
|
|
|
@ -11,7 +11,7 @@ import com.onthegomap.flatmap.stats.Stats;
|
|||
import java.util.List;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
public class OpenMaptilesProfileTest {
|
||||
public class OpenMapTilesProfileTest {
|
||||
|
||||
private final Wikidata.WikidataTranslations wikidataTranslations = new Wikidata.WikidataTranslations();
|
||||
private final Translations translations = Translations.defaultProvider(List.of("en", "es", "de"))
|
|
@ -0,0 +1,211 @@
|
|||
package com.onthegomap.flatmap.openmaptiles;
|
||||
|
||||
import static com.onthegomap.flatmap.TestUtils.assertContains;
|
||||
import static com.onthegomap.flatmap.TestUtils.assertFeatureNear;
|
||||
import static com.onthegomap.flatmap.TestUtils.gunzip;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
import com.onthegomap.flatmap.TestUtils;
|
||||
import com.onthegomap.flatmap.VectorTileEncoder;
|
||||
import com.onthegomap.flatmap.config.Arguments;
|
||||
import com.onthegomap.flatmap.mbiles.Mbtiles;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import org.junit.jupiter.api.AfterAll;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.io.TempDir;
|
||||
import org.locationtech.jts.geom.Envelope;
|
||||
import org.locationtech.jts.geom.Geometry;
|
||||
import org.locationtech.jts.geom.LineString;
|
||||
import org.locationtech.jts.geom.Point;
|
||||
import org.locationtech.jts.geom.Polygon;
|
||||
|
||||
/**
|
||||
* End-to-end tests for OpenMapTiles map generation.
|
||||
*/
|
||||
public class OpenMapTilesTest {
|
||||
|
||||
@TempDir
|
||||
static Path tmpDir;
|
||||
private static Mbtiles mbtiles;
|
||||
|
||||
private static final Envelope monacoBounds = new Envelope(7.40921, 7.44864, 43.72335, 43.75169);
|
||||
|
||||
@BeforeAll
|
||||
public static void runFlatmap() throws Exception {
|
||||
Path dbPath = tmpDir.resolve("output.mbtiles");
|
||||
OpenMapTilesMain.run(Arguments.of(
|
||||
// Override input source locations
|
||||
"osm", TestUtils.pathToResource("monaco-latest.osm.pbf"),
|
||||
"natural_earth", TestUtils.pathToResource("natural_earth_vector.sqlite"),
|
||||
"lake_centerlines", TestUtils.pathToResource("natural_earth_vector.sqlite"),
|
||||
"water_polygons", TestUtils.pathToResource("water-polygons-split-3857.zip"),
|
||||
// no centerlines in monaco - so fake it out with an empty source
|
||||
"lake_centerlines", TestUtils.pathToResource("water-polygons-split-3857.zip"),
|
||||
|
||||
// Override temp dir location
|
||||
"tmp", tmpDir.toString(),
|
||||
|
||||
// Override output location
|
||||
"mbtiles", dbPath.toString()
|
||||
));
|
||||
mbtiles = Mbtiles.newReadOnlyDatabase(dbPath);
|
||||
}
|
||||
|
||||
@AfterAll
|
||||
public static void close() throws IOException {
|
||||
mbtiles.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMetadata() {
|
||||
Map<String, String> metadata = mbtiles.metadata().getAll();
|
||||
assertEquals("OpenMapTiles", metadata.get("name"));
|
||||
assertEquals("0", metadata.get("minzoom"));
|
||||
assertEquals("14", metadata.get("maxzoom"));
|
||||
assertEquals("baselayer", metadata.get("type"));
|
||||
assertEquals("pbf", metadata.get("format"));
|
||||
assertEquals("7.40921,43.72335,7.44864,43.75169", metadata.get("bounds"));
|
||||
assertEquals("7.42892,43.73752,14", metadata.get("center"));
|
||||
assertContains("openmaptiles.org", metadata.get("description"));
|
||||
assertContains("openmaptiles.org", metadata.get("attribution"));
|
||||
assertContains("www.openstreetmap.org/copyright", metadata.get("attribution"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void ensureValidGeometries() throws Exception {
|
||||
Set<Mbtiles.TileEntry> parsedTiles = TestUtils.getAllTiles(mbtiles);
|
||||
for (var tileEntry : parsedTiles) {
|
||||
var decoded = VectorTileEncoder.decode(gunzip(tileEntry.bytes()));
|
||||
for (VectorTileEncoder.Feature feature : decoded) {
|
||||
TestUtils.validateGeometry(feature.geometry().decode());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testContainsOceanPolyons() {
|
||||
assertFeatureNear(mbtiles, "water", Map.of(
|
||||
"class", "ocean"
|
||||
), 7.4484, 43.70783, 0, 14);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testContainsCountryName() {
|
||||
assertFeatureNear(mbtiles, "place", Map.of(
|
||||
"class", "country",
|
||||
"iso_a2", "MC",
|
||||
"name", "Monaco"
|
||||
), 7.42769, 43.73235, 2, 14);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testContainsSuburb() {
|
||||
assertFeatureNear(mbtiles, "place", Map.of(
|
||||
"name", "Les Moneghetti",
|
||||
"class", "suburb"
|
||||
), 7.41746, 43.73638, 11, 14);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testContainsBuildings() {
|
||||
assertFeatureNear(mbtiles, "building", Map.of(), 7.41919, 43.73401, 13, 14);
|
||||
assertNumFeatures("building", Map.of(), 14, 1316, Polygon.class);
|
||||
assertNumFeatures("building", Map.of(), 13, 196, Polygon.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testContainsHousenumber() {
|
||||
assertFeatureNear(mbtiles, "housenumber", Map.of(
|
||||
"housenumber", "27"
|
||||
), 7.42117, 43.73652, 14, 14);
|
||||
assertNumFeatures("housenumber", Map.of(), 14, 274, Point.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBoundary() {
|
||||
assertFeatureNear(mbtiles, "boundary", Map.of(
|
||||
"admin_level", 2L,
|
||||
"maritime", 1L,
|
||||
"disputed", 0L
|
||||
), 7.41884, 43.72396, 4, 14);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAeroway() {
|
||||
assertNumFeatures("aeroway", Map.of(
|
||||
"class", "heliport"
|
||||
), 14, 1, Polygon.class);
|
||||
assertNumFeatures("aeroway", Map.of(
|
||||
"class", "helipad"
|
||||
), 14, 11, Polygon.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLandcover() {
|
||||
assertNumFeatures("landcover", Map.of(
|
||||
"class", "grass",
|
||||
"subclass", "park"
|
||||
), 14, 20, Polygon.class);
|
||||
assertNumFeatures("landcover", Map.of(
|
||||
"class", "grass",
|
||||
"subclass", "garden"
|
||||
), 14, 33, Polygon.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPoi() {
|
||||
assertNumFeatures("poi", Map.of(
|
||||
"class", "restaurant",
|
||||
"subclass", "restaurant"
|
||||
), 14, 217, Point.class);
|
||||
assertNumFeatures("poi", Map.of(
|
||||
"class", "art_gallery",
|
||||
"subclass", "artwork"
|
||||
), 14, 132, Point.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLanduse() {
|
||||
assertNumFeatures("landuse", Map.of(
|
||||
"class", "residential"
|
||||
), 14, 8, Polygon.class);
|
||||
assertNumFeatures("landuse", Map.of(
|
||||
"class", "hospital"
|
||||
), 14, 4, Polygon.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTransportation() {
|
||||
assertNumFeatures("transportation", Map.of(
|
||||
"class", "path",
|
||||
"subclass", "footway"
|
||||
), 14, 909, LineString.class);
|
||||
assertNumFeatures("transportation", Map.of(
|
||||
"class", "primary"
|
||||
), 14, 170, LineString.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTransportationName() {
|
||||
assertNumFeatures("transportation_name", Map.of(
|
||||
"name", "Boulevard du Larvotto",
|
||||
"class", "primary"
|
||||
), 14, 22, LineString.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWaterway() {
|
||||
assertNumFeatures("waterway", Map.of(
|
||||
"class", "stream"
|
||||
), 14, 6, LineString.class);
|
||||
}
|
||||
|
||||
private static void assertNumFeatures(String layer, Map<String, Object> attrs, int zoom,
|
||||
int expected, Class<? extends Geometry> clazz) {
|
||||
TestUtils.assertNumFeatures(mbtiles, layer, zoom, attrs, monacoBounds, expected, clazz);
|
||||
}
|
||||
}
|
Ładowanie…
Reference in New Issue