diff --git a/planetiler-custommap/README.md b/planetiler-custommap/README.md index d89d9d21..f611d592 100644 --- a/planetiler-custommap/README.md +++ b/planetiler-custommap/README.md @@ -65,6 +65,10 @@ A description that tells planetiler how to read geospatial objects with tags fro For [geofabrik](https://download.geofabrik.de/) named areas, use `geofabrik:` prefixes, for example `geofabrik:rhode-island`. Can be a string or [expression](#expression) that can reference [argument values](#arguments). +- `projection` - Planetiler will try to determine the projection automatically for shapefile/geopackage sources, but if + that is not correct you can override the projection by specifying a coordinate reference system authority code + like `EPSG:3857` or `EPSG:4326` here. Can be a string or [expression](#expression) that can + reference [argument values](#arguments). For example: diff --git a/planetiler-custommap/planetiler.schema.json b/planetiler-custommap/planetiler.schema.json index 437d691c..04421344 100644 --- a/planetiler-custommap/planetiler.schema.json +++ b/planetiler-custommap/planetiler.schema.json @@ -51,6 +51,20 @@ "local_path": { "description": "Local path to the file to use, inferred from `url` if missing", "$ref": "#/$defs/expression" + }, + "projection": { + "description": "Override the coordinate reference system authority code for a shapefile or geopackage source if it can not be determined automatically", + "anyOf": [ + { + "enum": [ + "EPSG:3857", + "EPSG:4326" + ] + }, + { + "type": "#/$defs/expression" + } + ] } }, "anyOf": [ diff --git a/planetiler-custommap/src/main/java/com/onthegomap/planetiler/custommap/ConfiguredMapMain.java b/planetiler-custommap/src/main/java/com/onthegomap/planetiler/custommap/ConfiguredMapMain.java index ff73a1f5..c4a11437 100644 --- a/planetiler-custommap/src/main/java/com/onthegomap/planetiler/custommap/ConfiguredMapMain.java +++ b/planetiler-custommap/src/main/java/com/onthegomap/planetiler/custommap/ConfiguredMapMain.java @@ -62,6 +62,7 @@ public class ConfiguredMapMain { DataSourceType sourceType = source.type(); Path localPath = source.localPath(); + String projection = source.projection(); if (localPath == null) { if (source.url() == null) { throw new ParseException("Must provide either a url or path for " + source.id()); @@ -71,8 +72,8 @@ public class ConfiguredMapMain { switch (sourceType) { case OSM -> planetiler.addOsmSource(source.id(), localPath, source.url()); - case SHAPEFILE -> planetiler.addShapefileSource(source.id(), localPath, source.url()); - case GEOPACKAGE -> planetiler.addGeoPackageSource(source.id(), localPath, source.url()); + case SHAPEFILE -> planetiler.addShapefileSource(projection, source.id(), localPath, source.url()); + case GEOPACKAGE -> planetiler.addGeoPackageSource(projection, source.id(), localPath, source.url()); default -> throw new IllegalArgumentException("Unhandled source type for " + source.id() + ": " + sourceType); } } diff --git a/planetiler-custommap/src/main/java/com/onthegomap/planetiler/custommap/ConfiguredProfile.java b/planetiler-custommap/src/main/java/com/onthegomap/planetiler/custommap/ConfiguredProfile.java index 02461761..aa00ae9b 100644 --- a/planetiler-custommap/src/main/java/com/onthegomap/planetiler/custommap/ConfiguredProfile.java +++ b/planetiler-custommap/src/main/java/com/onthegomap/planetiler/custommap/ConfiguredProfile.java @@ -117,13 +117,18 @@ public class ConfiguredProfile implements Profile { public List sources() { List sources = new ArrayList<>(); schema.sources().forEach((key, value) -> { - var url = ConfigExpressionParser.tryStaticEvaluate(rootContext, value.url(), String.class).get(); - var path = ConfigExpressionParser.tryStaticEvaluate(rootContext, value.localPath(), String.class).get(); - sources.add(new Source(key, value.type(), url, path == null ? null : Path.of(path))); + var url = evaluate(value.url(), String.class); + var path = evaluate(value.localPath(), String.class); + var projection = evaluate(value.projection(), String.class); + sources.add(new Source(key, value.type(), url, path == null ? null : Path.of(path), projection)); }); return sources; } + private T evaluate(Object expression, Class returnType) { + return ConfigExpressionParser.tryStaticEvaluate(rootContext, expression, returnType).get(); + } + public FeatureLayer findFeatureLayer(String layerId) { return layersById.get(layerId); } diff --git a/planetiler-custommap/src/main/java/com/onthegomap/planetiler/custommap/Source.java b/planetiler-custommap/src/main/java/com/onthegomap/planetiler/custommap/Source.java index 102e1209..31019340 100644 --- a/planetiler-custommap/src/main/java/com/onthegomap/planetiler/custommap/Source.java +++ b/planetiler-custommap/src/main/java/com/onthegomap/planetiler/custommap/Source.java @@ -8,7 +8,8 @@ public record Source( String id, DataSourceType type, String url, - Path localPath + Path localPath, + String projection ) { public String defaultFileUrl() { diff --git a/planetiler-custommap/src/main/java/com/onthegomap/planetiler/custommap/configschema/DataSource.java b/planetiler-custommap/src/main/java/com/onthegomap/planetiler/custommap/configschema/DataSource.java index 6296a64d..4c21e70c 100644 --- a/planetiler-custommap/src/main/java/com/onthegomap/planetiler/custommap/configschema/DataSource.java +++ b/planetiler-custommap/src/main/java/com/onthegomap/planetiler/custommap/configschema/DataSource.java @@ -5,5 +5,6 @@ import com.fasterxml.jackson.annotation.JsonProperty; public record DataSource( DataSourceType type, Object url, - @JsonProperty("local_path") Object localPath + @JsonProperty("local_path") Object localPath, + Object projection ) {} diff --git a/planetiler-custommap/src/main/resources/samples/owg_simple.yml b/planetiler-custommap/src/main/resources/samples/owg_simple.yml index 929d6de7..df4594af 100644 --- a/planetiler-custommap/src/main/resources/samples/owg_simple.yml +++ b/planetiler-custommap/src/main/resources/samples/owg_simple.yml @@ -6,6 +6,7 @@ sources: water_polygons: type: shapefile url: https://osmdata.openstreetmap.de/download/water-polygons-split-3857.zip + projection: EPSG:3857 osm: type: osm url: geofabrik:monaco diff --git a/planetiler-custommap/src/test/java/com/onthegomap/planetiler/custommap/ConfiguredFeatureTest.java b/planetiler-custommap/src/test/java/com/onthegomap/planetiler/custommap/ConfiguredFeatureTest.java index cbda42c3..d4585bc1 100644 --- a/planetiler-custommap/src/test/java/com/onthegomap/planetiler/custommap/ConfiguredFeatureTest.java +++ b/planetiler-custommap/src/test/java/com/onthegomap/planetiler/custommap/ConfiguredFeatureTest.java @@ -1117,6 +1117,7 @@ class ConfiguredFeatureTest { "osm", DataSourceType.OSM, "geofabrik:boston", + null, null )), loadConfig(config).sources()); @@ -1125,6 +1126,7 @@ class ConfiguredFeatureTest { "osm", DataSourceType.OSM, "geofabrik:rhode-island", + null, null )), loadConfig(config).sources()); @@ -1137,6 +1139,35 @@ class ConfiguredFeatureTest { assertEquals("example.com_file.osm.pbf", loadConfig(config).sources().get(0).defaultFileUrl()); } + @ParameterizedTest + @CsvSource({ + "EPSG:3875, EPSG:3875", + "${'EPSG:' + '3875'}, EPSG:3875", + }) + void testSetProjection(String in, String out) { + var config = """ + sources: + osm: + type: osm + url: geofabrik:rhode-island + projection: %s + layers: + - id: testLayer + features: + - source: osm + geometry: point + """.formatted(in); + + this.planetilerConfig = PlanetilerConfig.from(Arguments.of(Map.of())); + assertEquals(List.of(new Source( + "osm", + DataSourceType.OSM, + "geofabrik:rhode-island", + null, + out + )), loadConfig(config).sources()); + } + @ParameterizedTest @CsvSource(""" 10,10 diff --git a/planetiler-custommap/src/test/java/com/onthegomap/planetiler/custommap/ConfiguredMapTest.java b/planetiler-custommap/src/test/java/com/onthegomap/planetiler/custommap/ConfiguredMapTest.java index 8b2bffa1..a8b7493d 100644 --- a/planetiler-custommap/src/test/java/com/onthegomap/planetiler/custommap/ConfiguredMapTest.java +++ b/planetiler-custommap/src/test/java/com/onthegomap/planetiler/custommap/ConfiguredMapTest.java @@ -81,11 +81,11 @@ class ConfiguredMapTest { } } - // @Test --TODO FIX after adding water layer + @Test void testContainsOceanPolyons() { assertMinFeatures("water", Map.of( "natural", "water" - ), 0, 1, Polygon.class); + ), 6, 1, Polygon.class); } @Test