diff --git a/planetiler-core/src/main/java/com/onthegomap/planetiler/ForwardingProfile.java b/planetiler-core/src/main/java/com/onthegomap/planetiler/ForwardingProfile.java index 83230c554a..1a7aa64afd 100644 --- a/planetiler-core/src/main/java/com/onthegomap/planetiler/ForwardingProfile.java +++ b/planetiler-core/src/main/java/com/onthegomap/planetiler/ForwardingProfile.java @@ -81,13 +81,38 @@ protected ForwardingProfile(Handler... handlers) { } private boolean caresAboutLayer(String layer) { - return (onlyLayers.isEmpty() || onlyLayers.contains(layer)) && !excludeLayers.contains(layer); + var dependencies = dependsOnLayer(); + return ((onlyLayers.isEmpty() || onlyLayers.contains(layer)) && !excludeLayers.contains(layer)) || + this.handlers.stream() + .filter(HandlerForLayer.class::isInstance) + .map(HandlerForLayer.class::cast) + .filter(l -> !l.name().equals(layer)) + .anyMatch( + existing -> dependencies.containsKey(existing.name()) && dependencies.get(existing.name()).contains(layer)); } public boolean caresAboutLayer(Object obj) { return !(obj instanceof HandlerForLayer l) || caresAboutLayer(l.name()); } + /** + * Indicate to {@link #caresAboutLayer(String)} what layers (dependents) rely on other layers (dependencies) so that + * methods like {@link #registerFeatureHandler(FeatureProcessor)}, {@link #registerHandler(Handler)} or + * {@link #registerSourceHandler(String, FeatureProcessor)} do not block registration of those dependencies. + *

+ * Dependencies are described as a map with dependent layer names as keys and lists containing dependency layer names + * as map values. Example: Layer named {@code transportation_name} depends on layer named {@code transportation}: + *

+ * {@snippet : + * Map.of("transportation_name", List.of("transportation")); + * } + * + * @return a map describing dependencies + */ + public Map> dependsOnLayer() { + return Map.of(); + } + /** * Call {@code processor} for every element in {@code source}. * diff --git a/planetiler-core/src/test/java/com/onthegomap/planetiler/ForwardingProfileTests.java b/planetiler-core/src/test/java/com/onthegomap/planetiler/ForwardingProfileTests.java index 76305f614f..dc96b7f28d 100644 --- a/planetiler-core/src/test/java/com/onthegomap/planetiler/ForwardingProfileTests.java +++ b/planetiler-core/src/test/java/com/onthegomap/planetiler/ForwardingProfileTests.java @@ -24,6 +24,7 @@ import java.util.TreeSet; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; import org.junit.jupiter.params.provider.ValueSource; class ForwardingProfileTests { @@ -455,4 +456,51 @@ public void processFeature(SourceFeature sourceFeature, FeatureCollector feature "_layer", "water" )), a); } + + @ParameterizedTest + @CsvSource({ + "'--only-layers=water', water", + "'--exclude-layers=land,transportation,transportation_name', water", + // transportation excluded but transportation_name NOT => transportation will be processed + "'--exclude-layers=land,transportation', water transportation_name transportation", + "'--exclude-layers=land,transportation_name', water transportation", + "'--exclude-layers=land --only-layers=water,land', water", + // transportation excluded but transportation_name NOT => transportation will be processed + "'--exclude-layers=transportation --only-layers=water,transportation,transportation_name', water transportation_name transportation", + "'--exclude-layers=transportation_name --only-layers=water,transportation,transportation_name', water transportation", + "'--exclude-layers=transportation,transportation_name --only-layers=water,transportation,transportation_name', water", + // transportation excluded but transportation_name NOT => transportation will be processed + "'--exclude-layers=transportation --only-layers=water,transportation_name', water transportation_name transportation", + "'--exclude-layers=transportation_name --only-layers=water,transportation', water transportation", + }) + void testLayerWithDepsCliArgFilter(String args, String expectedLayers) { + profile = new ForwardingProfile(PlanetilerConfig.from(Arguments.fromArgs(args.split(" ")))) { + @Override + public Map> dependsOnLayer() { + return Map.of("transportation_name", List.of("transportation")); + } + }; + record Processor(String name) implements ForwardingProfile.HandlerForLayer, ForwardingProfile.FeatureProcessor { + + @Override + public void processFeature(SourceFeature sourceFeature, FeatureCollector features) { + features.point(name); + } + } + + SourceFeature a = SimpleFeature.create(GeoUtils.EMPTY_POINT, Map.of("key", "value"), "source", "source layer", 1); + profile.registerHandler(new Processor("water")); + profile.registerHandler(new Processor("transportation")); + profile.registerHandler(new Processor("transportation_name")); + profile.registerHandler(new Processor("land")); + // profiles like OpenMapTiles will try to add "transportation" once again to cover for dependency + profile.registerHandler(new Processor("transportation")); + + List> expected = new ArrayList<>(); + for (var expectedLayer : expectedLayers.split(" ")) { + expected.add(Map.of("_layer", expectedLayer)); + } + + testFeatures(expected, a); + } }