diff --git a/planetiler-basemap/src/main/java/com/onthegomap/planetiler/basemap/layers/TransportationName.java b/planetiler-basemap/src/main/java/com/onthegomap/planetiler/basemap/layers/TransportationName.java index c616cdd6..1e63a43f 100644 --- a/planetiler-basemap/src/main/java/com/onthegomap/planetiler/basemap/layers/TransportationName.java +++ b/planetiler-basemap/src/main/java/com/onthegomap/planetiler/basemap/layers/TransportationName.java @@ -121,6 +121,7 @@ public class TransportationName implements private final boolean sizeForShield; private final boolean limitMerge; private final PlanetilerConfig config; + private final boolean minorRefs; private Transportation transportation; private final LongByteMap motorwayJunctionHighwayClasses = Hppc.newLongByteHashMap(); @@ -141,6 +142,11 @@ public class TransportationName implements "transportation_name layer: limit merge so we don't combine different relations to help merge long highways", false ); + this.minorRefs = config.arguments().getBoolean( + "transportation_name_minor_refs", + "transportation_name layer: include name and refs from minor road networks if not present on a way", + false + ); } public void needsTransportationLayer(Transportation transportation) { @@ -208,9 +214,22 @@ public class TransportationName implements public void process(Tables.OsmHighwayLinestring element, FeatureCollector features) { String ref = element.ref(); List relations = transportation.getRouteRelations(element); - Transportation.RouteRelation relation = relations.isEmpty() ? null : relations.get(0); - if (relation != null && nullIfEmpty(relation.ref()) != null) { - ref = relation.ref(); + Transportation.RouteRelation firstRelationWithNetwork = relations.stream() + .filter(rel -> rel.networkType() != null) + .findFirst() + .orElse(null); + + if (firstRelationWithNetwork != null && !nullOrEmpty(firstRelationWithNetwork.ref())) { + ref = firstRelationWithNetwork.ref(); + } + + // if transportation_name_minor_refs flag set and we don't have a ref yet, then pull it from any network + if (nullOrEmpty(ref) && minorRefs && !relations.isEmpty()) { + ref = relations.stream() + .map(r -> r.ref()) + .filter(r -> !nullOrEmpty(r)) + .findFirst() + .orElse(null); } String name = nullIfEmpty(element.name()); @@ -240,8 +259,8 @@ public class TransportationName implements .setAttr(Fields.REF, ref) .setAttr(Fields.REF_LENGTH, ref != null ? ref.length() : null) .setAttr(Fields.NETWORK, - (relation != null && relation.networkType() != null) ? relation.networkType().name : - !nullOrEmpty(ref) ? "road" : null) + firstRelationWithNetwork != null ? firstRelationWithNetwork.networkType().name : !nullOrEmpty(ref) ? "road" : + null) .setAttr(Fields.CLASS, highwayClass) .setAttr(Fields.SUBCLASS, highwaySubclass(highwayClass, null, highway)) .setMinPixelSize(0) @@ -267,7 +286,7 @@ public class TransportationName implements if (limitMerge) { feature .setAttr(LINK_TEMP_KEY, isLink ? 1 : 0) - .setAttr(RELATION_ID_TEMP_KEY, relation == null ? null : relation.id()); + .setAttr(RELATION_ID_TEMP_KEY, firstRelationWithNetwork == null ? null : firstRelationWithNetwork.id()); } if (isFootwayOrSteps(highway)) { diff --git a/planetiler-basemap/src/test/java/com/onthegomap/planetiler/basemap/layers/TransportationTest.java b/planetiler-basemap/src/test/java/com/onthegomap/planetiler/basemap/layers/TransportationTest.java index 0cf325c4..ccec0813 100644 --- a/planetiler-basemap/src/test/java/com/onthegomap/planetiler/basemap/layers/TransportationTest.java +++ b/planetiler-basemap/src/test/java/com/onthegomap/planetiler/basemap/layers/TransportationTest.java @@ -341,15 +341,25 @@ public class TransportationTest extends AbstractLayerTest { @Test public void testRouteWithoutNetworkType() { - var rel = new OsmElement.Relation(1); - rel.setTag("type", "route"); - rel.setTag("route", "road"); - rel.setTag("network", "US:NJ:NJTP"); - rel.setTag("ref", "NJTP"); - rel.setTag("name", "New Jersey Turnpike (mainline)"); + var rel1 = new OsmElement.Relation(1); + rel1.setTag("type", "route"); + rel1.setTag("route", "road"); + rel1.setTag("network", "US:NJ:NJTP"); + rel1.setTag("ref", "NJTP"); + rel1.setTag("name", "New Jersey Turnpike (mainline)"); + + var rel2 = new OsmElement.Relation(1); + rel2.setTag("type", "route"); + rel2.setTag("route", "road"); + rel2.setTag("network", "US:I"); + rel2.setTag("ref", "95"); + rel2.setTag("name", "I 95 (NJ)"); FeatureCollector rendered = process(lineFeatureWithRelation( - profile.preprocessOsmRelation(rel), + Stream.concat( + profile.preprocessOsmRelation(rel1).stream(), + profile.preprocessOsmRelation(rel2).stream() + ).toList(), Map.of( "highway", "motorway", "name", "New Jersey Turnpike", @@ -364,13 +374,94 @@ public class TransportationTest extends AbstractLayerTest { "_layer", "transportation_name", "class", "motorway", "name", "New Jersey Turnpike", - "ref", "NJTP", - "ref_length", 4, - "route_1", "US:NJ:NJTP=NJTP", + "ref", "95", + "ref_length", 2, + "route_1", "US:I=95", + "route_2", "US:NJ:NJTP=NJTP", "_minzoom", 6 )), rendered); } + @Test + public void testMinorRouteRef() { + var rel1 = new OsmElement.Relation(1); + rel1.setTag("type", "route"); + rel1.setTag("route", "road"); + rel1.setTag("network", "rwn"); + rel1.setTag("ref", "GFW"); + rel1.setTag("name", "Georg-Fahrbach-Weg"); + + assertFeatures(13, List.of(mapOf( + "_layer", "transportation", + "class", "tertiary" + )), process(lineFeatureWithRelation( + profile.preprocessOsmRelation(rel1), + Map.of( + "highway", "tertiary" + )))); + + var profileWithMinorRefs = new BasemapProfile(translations, PlanetilerConfig.from(Arguments.of(Map.of( + "transportation_name_minor_refs", "true" + ))), Stats.inMemory()); + + SourceFeature feature = lineFeatureWithRelation( + profileWithMinorRefs.preprocessOsmRelation(rel1), + Map.of( + "highway", "tertiary" + )); + var collector = featureCollectorFactory.get(feature); + profileWithMinorRefs.processFeature(feature, collector); + assertFeatures(13, List.of(mapOf( + "_layer", "transportation", + "class", "tertiary" + ), mapOf( + "_layer", "transportation_name", + "class", "tertiary", + "ref", "GFW", + "network", "road" + )), collector); + } + + @Test + public void testPolishHighwayIssue165() { + var rel1 = new OsmElement.Relation(1); + rel1.setTag("type", "route"); + rel1.setTag("route", "road"); + rel1.setTag("network", "e-road"); + rel1.setTag("ref", "E 77"); + rel1.setTag("name", "European route E 77"); + + var rel2 = new OsmElement.Relation(2); + rel2.setTag("type", "route"); + rel2.setTag("route", "road"); + rel2.setTag("network", "e-road"); + rel2.setTag("ref", "E 28"); + rel2.setTag("name", "European route E 28"); + + FeatureCollector rendered = process(lineFeatureWithRelation( + Stream.concat( + profile.preprocessOsmRelation(rel1).stream(), + profile.preprocessOsmRelation(rel2).stream() + ).toList(), + Map.of( + "highway", "trunk", + "ref", "S7" + ))); + + assertFeatures(13, List.of(mapOf( + "_layer", "transportation", + "class", "trunk" + ), Map.of( + "_layer", "transportation_name", + "class", "trunk", + "name", "", + "ref", "S7", + "ref_length", 2, + "route_1", "e-road=E 28", + "route_2", "e-road=E 77" + )), rendered); + } + @Test public void testMotorwayJunction() { var otherNode1 = new OsmElement.Node(1, 1, 1);