diff --git a/planetiler-custommap/README.md b/planetiler-custommap/README.md index 719d39ae..c01bc65a 100644 --- a/planetiler-custommap/README.md +++ b/planetiler-custommap/README.md @@ -222,6 +222,8 @@ A feature is a defined set of objects that meet a specified filter criteria. - `exclude_when` - A [Boolean Expression](#boolean-expression) which determines if a feature that matched the include expression should be skipped. If unspecified, no exclusion filter is applied. - `min_zoom` - An [Expression](#expression) that returns the minimum zoom to render this feature at. +- `min_size` - An [Expression](#expression) that returns the minimum length of line features or square root of the + minimum area of polygon features to emit below the maximum zoom-level of the map. - `attributes` - An array of [Feature Attribute](#feature-attribute) objects that specify the attributes to be included on this output feature. @@ -672,6 +674,8 @@ docker run -v "$(pwd)/data":/data ghcr.io/onthegomap/planetiler:latest verify /d - `geometry` - Geometry type of the expected output feature. - `min_zoom` - Min zoom level that the output feature appears in. - `max_zoom` - Max zoom level that the output feature appears in. + - `min_size` - Minimum length of line features or square root of the minimum area of polygon features to emit below + the maximum zoom-level of the map. - `tags` - Attributes expected on the output vector tile feature, or `null` if the attribute should not be set. Use `allow_extra_tags: true` to fail if any other tags appear besides the ones specified here. - `allow_extra_tags` - If `true`, then fail when extra attributes besides tags appear on the output feature. diff --git a/planetiler-custommap/planetiler.schema.json b/planetiler-custommap/planetiler.schema.json index e8b6f2a6..0f636de6 100644 --- a/planetiler-custommap/planetiler.schema.json +++ b/planetiler-custommap/planetiler.schema.json @@ -392,6 +392,10 @@ "items": { "$ref": "#/$defs/attribute" } + }, + "min_size": { + "description": "Minimum length of line features or square root of the minimum area of polygon features to emit below the maximum zoom-level of the map", + "$ref": "#/$defs/expression" } } }, diff --git a/planetiler-custommap/planetilerspec.schema.json b/planetiler-custommap/planetilerspec.schema.json index c642ca18..1cc463c7 100644 --- a/planetiler-custommap/planetilerspec.schema.json +++ b/planetiler-custommap/planetilerspec.schema.json @@ -83,6 +83,10 @@ "description": "Max zoom level that the output feature appears in", "type": "integer" }, + "min_size": { + "description": "Minimum length of line features or square root of the minimum area of polygon features to emit below the maximum zoom-level of the map", + "type": "number" + }, "tags": { "description": "Attributes expected on the output vector tile feature, or null if the attribute should not be set. Use allow_extra_tags: true to fail if any other tags appear besides the ones specified here", "oneOf": [ diff --git a/planetiler-custommap/src/main/java/com/onthegomap/planetiler/custommap/ConfiguredFeature.java b/planetiler-custommap/src/main/java/com/onthegomap/planetiler/custommap/ConfiguredFeature.java index 02023ee3..a43e7f64 100644 --- a/planetiler-custommap/src/main/java/com/onthegomap/planetiler/custommap/ConfiguredFeature.java +++ b/planetiler-custommap/src/main/java/com/onthegomap/planetiler/custommap/ConfiguredFeature.java @@ -84,6 +84,7 @@ public class ConfiguredFeature { } processors.add(makeFeatureProcessor(feature.minZoom(), Integer.class, Feature::setMinZoom)); processors.add(makeFeatureProcessor(feature.maxZoom(), Integer.class, Feature::setMaxZoom)); + processors.add(makeFeatureProcessor(feature.minSize(), Double.class, Feature::setMinPixelSize)); featureProcessors = processors.stream().filter(Objects::nonNull).toList(); } diff --git a/planetiler-custommap/src/main/java/com/onthegomap/planetiler/custommap/configschema/FeatureItem.java b/planetiler-custommap/src/main/java/com/onthegomap/planetiler/custommap/configschema/FeatureItem.java index e695408f..151f9b23 100644 --- a/planetiler-custommap/src/main/java/com/onthegomap/planetiler/custommap/configschema/FeatureItem.java +++ b/planetiler-custommap/src/main/java/com/onthegomap/planetiler/custommap/configschema/FeatureItem.java @@ -9,6 +9,7 @@ public record FeatureItem( @JsonFormat(with = JsonFormat.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY) List source, @JsonProperty("min_zoom") Object minZoom, @JsonProperty("max_zoom") Object maxZoom, + @JsonProperty("min_size") Object minSize, @JsonProperty(required = true) FeatureGeometry geometry, @JsonProperty("include_when") Object includeWhen, @JsonProperty("exclude_when") Object excludeWhen, diff --git a/planetiler-custommap/src/main/java/com/onthegomap/planetiler/custommap/validator/SchemaSpecification.java b/planetiler-custommap/src/main/java/com/onthegomap/planetiler/custommap/validator/SchemaSpecification.java index 5f0230cf..65385adc 100644 --- a/planetiler-custommap/src/main/java/com/onthegomap/planetiler/custommap/validator/SchemaSpecification.java +++ b/planetiler-custommap/src/main/java/com/onthegomap/planetiler/custommap/validator/SchemaSpecification.java @@ -55,6 +55,7 @@ public record SchemaSpecification(List examples) { GeometryType geometry, @JsonProperty("min_zoom") Integer minZoom, @JsonProperty("max_zoom") Integer maxZoom, + @JsonProperty("min_size") Double minSize, @JsonProperty("at_zoom") Integer atZoom, @JsonProperty("allow_extra_tags") Boolean allowExtraTags, @JsonProperty("tags") Map tags diff --git a/planetiler-custommap/src/main/java/com/onthegomap/planetiler/custommap/validator/SchemaValidator.java b/planetiler-custommap/src/main/java/com/onthegomap/planetiler/custommap/validator/SchemaValidator.java index 1964b963..431bda0c 100644 --- a/planetiler-custommap/src/main/java/com/onthegomap/planetiler/custommap/validator/SchemaValidator.java +++ b/planetiler-custommap/src/main/java/com/onthegomap/planetiler/custommap/validator/SchemaValidator.java @@ -208,6 +208,7 @@ public class SchemaValidator { validate(prefix + ".layer", issues, expected.layer(), actual.getLayer()); validate(prefix + ".minzoom", issues, expected.minZoom(), actual.getMinZoom()); validate(prefix + ".maxzoom", issues, expected.maxZoom(), actual.getMaxZoom()); + validate(prefix + ".minsize", issues, expected.minSize(), actual.getMinPixelSizeAtZoom(expected.atZoom())); validate(prefix + ".geometry", issues, expected.geometry(), GeometryType.typeOf(actual.getGeometry())); Set tags = new TreeSet<>(actualTags.keySet()); expected.tags().forEach((tag, value) -> { diff --git a/planetiler-custommap/src/main/resources/samples/shortbread.spec.yml b/planetiler-custommap/src/main/resources/samples/shortbread.spec.yml index 245bf438..4ea4dbd8 100644 --- a/planetiler-custommap/src/main/resources/samples/shortbread.spec.yml +++ b/planetiler-custommap/src/main/resources/samples/shortbread.spec.yml @@ -221,6 +221,7 @@ examples: - layer: streets geometry: line min_zoom: 8 + min_size: 0 tags: bridge: false kind: primary @@ -262,6 +263,7 @@ examples: geometry: line min_zoom: 8 allow_extra_tags: false + min_size: 0 tags: bridge: false kind: primary @@ -291,6 +293,7 @@ examples: geometry: line min_zoom: 8 allow_extra_tags: false + min_size: 0 tags: bridge: false kind: rail @@ -707,6 +710,7 @@ examples: layer: boundaries geometry: line min_zoom: 0 + min_size: 0 tags: maritime: true admin_level: 2 @@ -723,6 +727,7 @@ examples: layer: boundaries geometry: line min_zoom: 7 + min_size: 0 tags: maritime: false admin_level: 4 diff --git a/planetiler-custommap/src/main/resources/samples/shortbread.yml b/planetiler-custommap/src/main/resources/samples/shortbread.yml index 6fdba1bb..83b81b52 100644 --- a/planetiler-custommap/src/main/resources/samples/shortbread.yml +++ b/planetiler-custommap/src/main/resources/samples/shortbread.yml @@ -132,6 +132,7 @@ layers: - source: osm geometry: line # TODO get min admin level from relations + min_size: 0 min_zoom: default_value: 7 overrides: @@ -379,6 +380,7 @@ layers: features: - source: osm geometry: line + min_size: 0 min_zoom: default_value: 13 overrides: 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 725ddb7e..564b97f8 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 @@ -1022,4 +1022,29 @@ class ConfiguredFeatureTest { ))); assertEquals("example.com_file.osm.pbf", loadConfig(config).sources().get(0).defaultFileUrl()); } + + @ParameterizedTest + @CsvSource(""" + 10,10 + ${10+1},11 + ${feature.tags.key}|9 + """) + void setMinSize(String input, double output) { + var config = """ + sources: + osm: + type: osm + url: geofabrik:rhode-island + local_path: data/rhode-island.osm.pbf + layers: + - id: testLayer + features: + - source: osm + min_size: %s + geometry: line + """.formatted(input); + testLinestring(config, Map.of("key", 9), feature -> { + assertEquals(output, feature.getMinPixelSizeAtZoom(11)); + }, 1); + } } diff --git a/planetiler-custommap/src/test/java/com/onthegomap/planetiler/custommap/validator/SchemaValidatorTest.java b/planetiler-custommap/src/test/java/com/onthegomap/planetiler/custommap/validator/SchemaValidatorTest.java index 5c38b6ed..eff36cc9 100644 --- a/planetiler-custommap/src/test/java/com/onthegomap/planetiler/custommap/validator/SchemaValidatorTest.java +++ b/planetiler-custommap/src/test/java/com/onthegomap/planetiler/custommap/validator/SchemaValidatorTest.java @@ -77,6 +77,7 @@ class SchemaValidatorTest { features: - source: osm geometry: polygon + min_size: 10 include_when: natural: water attributes: @@ -121,6 +122,9 @@ class SchemaValidatorTest { "true,water,polygon,natural: water,allow_extra_tags: false", "true,water,polygon,,allow_extra_tags: true", "false,water,polygon,,allow_extra_tags: false", + + "true,water,polygon,,min_size: 10", + "false,water,polygon,,min_size: 9", }) void testValidateWaterPolygon(boolean shouldBeOk, String layer, String geometry, String tags, String allowExtraTags) throws IOException {