Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions src/main/java/org/openmaptiles/layers/Transportation.java
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,8 @@ public class Transportation implements
.put(6, 100)
.put(5, 500)
.put(4, 1_000);
private static final ZoomFunction.MeterToPixelThresholds TRUNK_UPGRADE_LENGTH = ZoomFunction.meterThresholds()
.put(5, 1_000);
// ORDER BY network_type, network, LENGTH(ref), ref)
private static final Comparator<RouteRelation> RELATION_ORDERING = Comparator
.<RouteRelation>comparingInt(
Expand Down Expand Up @@ -285,6 +287,22 @@ private static boolean isTrunkForZ5(String highway, List<RouteRelation> routeRel
.anyMatch(Z5_TRUNK_BY_NETWORK::contains);
}

/**
* Checks if a trunk segment is small enough to be processed at z5 so it can merge with surrounding motorways.
*
* @param element the highway linestring element to check
* @return true if the trunk segment length is less than the threshold, false otherwise
*/
private boolean isTrunkZ5MergeableLength(Tables.OsmHighwayLinestring element) {
try {
return element.source().length() < TRUNK_UPGRADE_LENGTH.apply(5).doubleValue();
} catch (GeometryException e) {
e.log(stats, "omt_transportation_trunk_length",
"Unable to get feature length for trunk upgrade: " + element.source().id());
return false;
}
}

private static boolean isMotorwayWithNetworkForZ4(List<RouteRelation> routeRelations) {
// All roads in network included in osm_national_network except gb-trunk and us-highway
return routeRelations.stream()
Expand Down Expand Up @@ -570,6 +588,12 @@ int getMinzoom(Tables.OsmHighwayLinestring element, String highwayClass) {
(z13Paths || !nullOrEmpty(element.name()) || routeRank <= 2 || !nullOrEmpty(element.sacScale())) ? 13 : 14;
case FieldValues.CLASS_TRUNK -> {
boolean z5trunk = isTrunkForZ5(highway, routeRelations);

//Allow small trunk segments to be processed at z5 so they can merge with surrounding motorways
if (isTrunkZ5MergeableLength(element)) {
z5trunk = true;
}

// and if it is good for Z5, it may be good also for Z4 (see CLASS_MOTORWAY bellow):
String clazz = FieldValues.CLASS_TRUNK;
if (z5trunk && isMotorwayWithNetworkForZ4(routeRelations)) {
Expand Down Expand Up @@ -702,6 +726,19 @@ public List<VectorTile.Feature> postProcess(int zoom, List<VectorTile.Feature> i
}
}

//Upgrade small trunk segments at z5 so they can merge with surrounding motorways
if (zoom == 5) {
for (var item : items) {
var highway = item.tags().get(Fields.CLASS);
if (highway instanceof String highwayStr && highwayStr.equals(FieldValues.CLASS_TRUNK)) {
item.tags().put(Fields.CLASS, FieldValues.CLASS_MOTORWAY);
}
if (highway instanceof String highwayStr && highwayStr.equals(FieldValues.CLASS_TRUNK_CONSTRUCTION)) {
item.tags().put(Fields.CLASS, FieldValues.CLASS_MOTORWAY_CONSTRUCTION);
}
}
}

var merged = FeatureMerge.mergeLineStrings(items, minLength, tolerance, BUFFER_SIZE);

for (var item : merged) {
Expand Down
68 changes: 61 additions & 7 deletions src/test/java/org/openmaptiles/layers/TransportationTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@
import static com.onthegomap.planetiler.TestUtils.newPoint;
import static com.onthegomap.planetiler.TestUtils.rectangle;
import static java.util.Map.entry;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;

import com.onthegomap.planetiler.FeatureCollector;
import com.onthegomap.planetiler.VectorTile;
import com.onthegomap.planetiler.config.Arguments;
import com.onthegomap.planetiler.config.PlanetilerConfig;
import com.onthegomap.planetiler.geo.GeometryException;
Expand All @@ -16,6 +18,7 @@
import com.onthegomap.planetiler.reader.osm.OsmRelationInfo;
import com.onthegomap.planetiler.stats.Stats;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Stream;
Expand Down Expand Up @@ -396,7 +399,7 @@ void testDuplicateRoute() {
"_layer", "transportation",
"class", "trunk",
"network", "us-state",
"_minzoom", 6
"_minzoom", 5
), Map.of(
"_layer", "transportation_name",
"class", "trunk",
Expand Down Expand Up @@ -1217,7 +1220,7 @@ void testTransCanadaProvincialCaOnPrimaryRefOther() {
"_layer", "transportation",
"class", "trunk",
"network", "ca-provincial",
"_minzoom", 6
"_minzoom", 5
), Map.of(
"_layer", "transportation_name",
"class", "trunk",
Expand Down Expand Up @@ -1271,7 +1274,7 @@ void testTransCanadaProvincialCaMbPthRefOther() {
"_layer", "transportation",
"class", "trunk",
"network", "ca-provincial",
"_minzoom", 6
"_minzoom", 5
), Map.of(
"_layer", "transportation_name",
"class", "trunk",
Expand Down Expand Up @@ -1325,7 +1328,7 @@ void testTransCanadaProvincialCaAbPrimaryRefOther() {
"_layer", "transportation",
"class", "trunk",
"network", "ca-provincial",
"_minzoom", 6
"_minzoom", 5
), Map.of(
"_layer", "transportation_name",
"class", "trunk",
Expand Down Expand Up @@ -1379,7 +1382,7 @@ void testTransCanadaProvincialCaBcRefOther() {
"_layer", "transportation",
"class", "trunk",
"network", "ca-provincial",
"_minzoom", 6
"_minzoom", 5
), Map.of(
"_layer", "transportation_name",
"class", "trunk",
Expand All @@ -1404,7 +1407,7 @@ void testTransCanadaProvincialCaOther() {
assertFeatures(13, List.of(Map.of(
"_layer", "transportation",
"class", "trunk",
"_minzoom", 6
"_minzoom", 5
)), features);
boolean caProvPresent = StreamSupport.stream(features.spliterator(), false)
.flatMap(f -> f.getAttrsAtZoom(13).entrySet().stream())
Expand Down Expand Up @@ -2192,7 +2195,7 @@ void testARoad() {
assertFeatures(13, List.of(Map.of(
"_layer", "transportation",
"class", "trunk",
"_minzoom", 6
"_minzoom", 5
), Map.of(
"_layer", "transportation_name",
"class", "trunk",
Expand Down Expand Up @@ -2227,4 +2230,55 @@ void testERoad() {
"route_1_ref", "E 77"
)), features);
}

@Test
void testShortTrunkMerge() throws GeometryException {
var layer = Transportation.LAYER_NAME;

var motorwayZ6 = new VectorTile.Feature(
layer,
1,
VectorTile.encodeGeometry(newLineString(0, 0, 10, 10)),
new HashMap<>(Map.of("class", "motorway")),
0
);
var trunkZ6 = new VectorTile.Feature(
layer,
2,
VectorTile.encodeGeometry(newLineString(10, 10, 10, 11)),
new HashMap<>(Map.of("class", "trunk")),
0
);
var motorwayZ5 = new VectorTile.Feature(
layer,
1,
VectorTile.encodeGeometry(newLineString(0, 0, 10, 10)),
new HashMap<>(Map.of("class", "motorway")),
0
);
var trunkZ5 = new VectorTile.Feature(
layer,
2,
VectorTile.encodeGeometry(newLineString(10, 10, 10, 11)),
new HashMap<>(Map.of("class", "trunk")),
0
);

List<VectorTile.Feature> inputZ6 = List.<VectorTile.Feature>of(motorwayZ6, trunkZ6);
List<VectorTile.Feature> inputZ5 = List.<VectorTile.Feature>of(motorwayZ5, trunkZ5);

List<VectorTile.Feature> resultZ6 = profile.postProcessLayerFeatures(layer, 6, inputZ6);
List<VectorTile.Feature> resultZ5 = profile.postProcessLayerFeatures(layer, 5, inputZ5);

assertEquals(2, resultZ6.size(), "Should be separate features at zoom 6");
assertEquals(1, resultZ5.size(), "Should merge into a single feature at zoom 5");

VectorTile.Feature mergedFeatureZ5 = resultZ5.get(0);
assertEquals("motorway", mergedFeatureZ5.tags().get("class"), "Merged feature should be motorway class at zoom 5");

List<String> classesZ6 = resultZ6.stream()
.map(f -> (String) f.tags().get("class"))
.toList();
assertEquals(List.of("motorway", "trunk"), classesZ6, "At zoom 6, should have motorway and trunk classes");
}
}
Loading