From 0bb61f730620c4d8bc4703103b4cd5d10481aca1 Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Wed, 5 Jul 2023 13:37:21 +0200 Subject: [PATCH 001/106] version bumped from 3.14.0 to 3.15.0-SNAPSHOT --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 5f392ffb..57cd3327 100644 --- a/pom.xml +++ b/pom.xml @@ -22,7 +22,7 @@ org.openmaptiles planetiler-openmaptiles - 3.14.0 + 3.15.0-SNAPSHOT OpenMapTiles Vector Tile Schema implementation for Planetiler tool From d32501e85263303bc805cc3aba47d3209e08b4af Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Wed, 5 Jul 2023 13:39:04 +0200 Subject: [PATCH 002/106] regenerate-openmaptiles.sh 07f243c5d9efa558fa539d7a31b2ae50507aaa9d (to match content of OMT PR 1457) --- .../generated/OpenMapTilesSchema.java | 83 +++++++++++-------- .../org/openmaptiles/generated/Tables.java | 34 ++++---- 2 files changed, 68 insertions(+), 49 deletions(-) diff --git a/src/main/java/org/openmaptiles/generated/OpenMapTilesSchema.java b/src/main/java/org/openmaptiles/generated/OpenMapTilesSchema.java index 8ae534a5..3dd59c39 100644 --- a/src/main/java/org/openmaptiles/generated/OpenMapTilesSchema.java +++ b/src/main/java/org/openmaptiles/generated/OpenMapTilesSchema.java @@ -48,9 +48,9 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE import org.openmaptiles.Layer; /** - * All vector tile layer definitions, attributes, and allowed values generated from the - * OpenMapTiles vector tile schema - * v3.14. + * All vector tile layer definitions, attributes, and allowed values generated from the OpenMapTiles + * vector tile schema 07f243c5d9efa558fa539d7a31b2ae50507aaa9d. */ @SuppressWarnings("unused") public class OpenMapTilesSchema { @@ -95,8 +95,8 @@ public static List createInstances(Translations translations, PlanetilerC * polygons to improve rendering performance. This however can lead to less rendering options in clients since these * boundaries show up. So you might not be able to use border styling for ocean water features. * - * Generated from - * water.yaml + * Generated from water.yaml */ public interface Water extends Layer { double BUFFER_SIZE = 4.0; @@ -121,7 +121,7 @@ final class Fields { * water=river tag are classified as * river. Wet and dry docks tagged * waterway=dock are classified as - * a dock. Swimming pools tagged + * a dock. Various minor waterbodies are classified as a pond. Swimming pools tagged * leisure=swimming_pool * are classified as a swimming_pool All other water bodies are classified as lake. *

@@ -129,6 +129,7 @@ final class Fields { *

    *
  • dock *
  • river + *
  • pond *
  • lake *
  • ocean *
  • swimming_pool @@ -163,10 +164,11 @@ final class Fields { final class FieldValues { public static final String CLASS_DOCK = "dock"; public static final String CLASS_RIVER = "river"; + public static final String CLASS_POND = "pond"; public static final String CLASS_LAKE = "lake"; public static final String CLASS_OCEAN = "ocean"; public static final String CLASS_SWIMMING_POOL = "swimming_pool"; - public static final Set CLASS_VALUES = Set.of("dock", "river", "lake", "ocean", "swimming_pool"); + public static final Set CLASS_VALUES = Set.of("dock", "river", "pond", "lake", "ocean", "swimming_pool"); public static final String BRUNNEL_BRIDGE = "bridge"; public static final String BRUNNEL_TUNNEL = "tunnel"; public static final Set BRUNNEL_VALUES = Set.of("bridge", "tunnel"); @@ -175,8 +177,9 @@ final class FieldValues { final class FieldMappings { public static final MultiExpression Class = MultiExpression.of(List.of(MultiExpression.entry("dock", matchAny("waterway", "dock")), - MultiExpression.entry("river", matchAny("water", "river")), MultiExpression.entry("lake", FALSE), - MultiExpression.entry("ocean", FALSE), + MultiExpression.entry("river", matchAny("water", "river")), + MultiExpression.entry("pond", matchAny("water", "pond", "basin", "wastewater")), + MultiExpression.entry("lake", FALSE), MultiExpression.entry("ocean", FALSE), MultiExpression.entry("swimming_pool", matchAny("leisure", "swimming_pool")))); } } @@ -187,8 +190,8 @@ final class FieldMappings { * there is also canal generated, starting z13 there is no generalization according to class * field applied. Waterways do not have a subclass field. * - * Generated from - * waterway.yaml + * Generated from waterway.yaml */ public interface Waterway extends Layer { double BUFFER_SIZE = 4.0; @@ -273,7 +276,7 @@ final class FieldMappings { * layer is to style wood (class=wood) and grass (class=grass) areas. * * Generated from landcover.yaml + * "https://github.com/openmaptiles/openmaptiles/blob/07f243c5d9efa558fa539d7a31b2ae50507aaa9d/layers/landcover/landcover.yaml">landcover.yaml */ public interface Landcover extends Layer { double BUFFER_SIZE = 4.0; @@ -429,8 +432,8 @@ final class FieldMappings { * Landuse is used to describe use of land by humans. At lower zoom levels this is from Natural Earth data for * residential (urban) areas and at higher zoom levels mostly OSM landuse tags. * - * Generated from - * landuse.yaml + * Generated from landuse.yaml */ public interface Landuse extends Layer { double BUFFER_SIZE = 4.0; @@ -526,7 +529,7 @@ final class FieldMappings { * Natural peaks * * Generated from mountain_peak.yaml + * "https://github.com/openmaptiles/openmaptiles/blob/07f243c5d9efa558fa539d7a31b2ae50507aaa9d/layers/mountain_peak/mountain_peak.yaml">mountain_peak.yaml */ public interface MountainPeak extends Layer { double BUFFER_SIZE = 64.0; @@ -600,8 +603,8 @@ final class FieldMappings { * "http://wiki.openstreetmap.org/wiki/Tag:boundary%3Dprotected_area">boundary=protected_area, or * leisure=nature_reserve. * - * Generated from - * park.yaml + * Generated from park.yaml */ public interface Park extends Layer { double BUFFER_SIZE = 4.0; @@ -660,8 +663,8 @@ final class FieldMappings { * admin_level * but for most styles it makes sense to just style admin_level=2 and admin_level=4. * - * Generated from - * boundary.yaml + * Generated from boundary.yaml */ public interface Boundary extends Layer { double BUFFER_SIZE = 4.0; @@ -761,8 +764,8 @@ final class FieldMappings { * buildings are contained in the building layer but all other airport related polygons can be found * in the aeroway layer. * - * Generated from - * aeroway.yaml + * Generated from aeroway.yaml */ public interface Aeroway extends Layer { double BUFFER_SIZE = 4.0; @@ -822,7 +825,7 @@ final class FieldMappings { * features like plazas. * * Generated from transportation.yaml + * "https://github.com/openmaptiles/openmaptiles/blob/07f243c5d9efa558fa539d7a31b2ae50507aaa9d/layers/transportation/transportation.yaml">transportation.yaml */ public interface Transportation extends Layer { double BUFFER_SIZE = 4.0; @@ -1165,8 +1168,8 @@ final class FieldMappings { * (building= ). Only buildings with tag * location:underground are excluded. * - * Generated from - * building.yaml + * Generated from building.yaml */ public interface Building extends Layer { double BUFFER_SIZE = 4.0; @@ -1208,7 +1211,7 @@ final class FieldMappings { * from OSM water bodies. Only the most important lakes contain labels. * * Generated from water_name.yaml + * "https://github.com/openmaptiles/openmaptiles/blob/07f243c5d9efa558fa539d7a31b2ae50507aaa9d/layers/water_name/water_name.yaml">water_name.yaml */ public interface WaterName extends Layer { double BUFFER_SIZE = 256.0; @@ -1231,11 +1234,14 @@ final class Fields { public static final String NAME_DE = "name_de"; /** - * Distinguish between lake, ocean and sea. + * Distinguish between lake, ocean, bay, strait, and + * sea. *

    * allowed values: *

      *
    • "lake" + *
    • "bay" + *
    • "strait" *
    • "sea" *
    • "ocean" *
    @@ -1257,9 +1263,11 @@ final class Fields { /** Attribute values for map elements in the water_name layer. */ final class FieldValues { public static final String CLASS_LAKE = "lake"; + public static final String CLASS_BAY = "bay"; + public static final String CLASS_STRAIT = "strait"; public static final String CLASS_SEA = "sea"; public static final String CLASS_OCEAN = "ocean"; - public static final Set CLASS_VALUES = Set.of("lake", "sea", "ocean"); + public static final Set CLASS_VALUES = Set.of("lake", "bay", "strait", "sea", "ocean"); } /** Complex mappings to generate attribute values from OSM element tags in the water_name layer. */ final class FieldMappings { @@ -1273,7 +1281,7 @@ final class FieldMappings { * while for other roads you should use name. * * Generated from transportation_name.yaml + * "https://github.com/openmaptiles/openmaptiles/blob/07f243c5d9efa558fa539d7a31b2ae50507aaa9d/layers/transportation_name/transportation_name.yaml">transportation_name.yaml */ public interface TransportationName extends Layer { double BUFFER_SIZE = 8.0; @@ -1316,6 +1324,8 @@ final class Fields { *
  • "us-highway" *
  • "us-state" *
  • "ca-transcanada" + *
  • "ca-provincial-arterial" + *
  • "ca-provincial" *
  • "gb-motorway" *
  • "gb-trunk" *
  • "road (default)" @@ -1427,11 +1437,13 @@ final class FieldValues { public static final String NETWORK_US_HIGHWAY = "us-highway"; public static final String NETWORK_US_STATE = "us-state"; public static final String NETWORK_CA_TRANSCANADA = "ca-transcanada"; + public static final String NETWORK_CA_PROVINCIAL_ARTERIAL = "ca-provincial-arterial"; + public static final String NETWORK_CA_PROVINCIAL = "ca-provincial"; public static final String NETWORK_GB_MOTORWAY = "gb-motorway"; public static final String NETWORK_GB_TRUNK = "gb-trunk"; public static final String NETWORK_ROAD = "road"; - public static final Set NETWORK_VALUES = - Set.of("us-interstate", "us-highway", "us-state", "ca-transcanada", "gb-motorway", "gb-trunk", "road"); + public static final Set NETWORK_VALUES = Set.of("us-interstate", "us-highway", "us-state", + "ca-transcanada", "ca-provincial-arterial", "ca-provincial", "gb-motorway", "gb-trunk", "road"); public static final String CLASS_MOTORWAY = "motorway"; public static final String CLASS_TRUNK = "trunk"; public static final String CLASS_PRIMARY = "primary"; @@ -1489,8 +1501,8 @@ final class FieldMappings { * of the more important layers to create a beautiful map. We suggest you use different font styles and sizes to * create a text hierarchy. * - * Generated from - * place.yaml + * Generated from place.yaml */ public interface Place extends Layer { double BUFFER_SIZE = 256.0; @@ -1598,7 +1610,7 @@ final class FieldMappings { * housenumber. * * Generated from housenumber.yaml + * "https://github.com/openmaptiles/openmaptiles/blob/07f243c5d9efa558fa539d7a31b2ae50507aaa9d/layers/housenumber/housenumber.yaml">housenumber.yaml */ public interface Housenumber extends Layer { double BUFFER_SIZE = 8.0; @@ -1627,7 +1639,8 @@ final class FieldMappings { * Points of interests containing a of a variety * of OpenStreetMap tags. Mostly contains amenities, sport, shop and tourist POIs. * - * Generated from poi.yaml + * Generated from poi.yaml */ public interface Poi extends Layer { double BUFFER_SIZE = 64.0; @@ -1846,7 +1859,7 @@ final class FieldMappings { * Aerodrome labels * * Generated from aerodrome_label.yaml + * "https://github.com/openmaptiles/openmaptiles/blob/07f243c5d9efa558fa539d7a31b2ae50507aaa9d/layers/aerodrome_label/aerodrome_label.yaml">aerodrome_label.yaml */ public interface AerodromeLabel extends Layer { double BUFFER_SIZE = 64.0; diff --git a/src/main/java/org/openmaptiles/generated/Tables.java b/src/main/java/org/openmaptiles/generated/Tables.java index a29e15e7..a090d736 100644 --- a/src/main/java/org/openmaptiles/generated/Tables.java +++ b/src/main/java/org/openmaptiles/generated/Tables.java @@ -50,8 +50,9 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE /** * OSM element parsers generated from the imposm3 table definitions - * in the OpenMapTiles vector tile - * schema. + * in the OpenMapTiles + * vector tile schema. * * These filter and parse the raw OSM key/value attribute pairs on tags into records with fields that match the columns * in the tables that imposm3 would generate. Layer implementations can "subscribe" to elements from each "table" but @@ -94,21 +95,23 @@ public record RowHandlerAndClass ( ) {} /** An OSM element that would appear in the {@code osm_water_polygon} table generated by imposm3. */ public record OsmWaterPolygon(@Override String name, @Override String nameEn, @Override String nameDe, - @Override String natural, @Override String landuse, @Override String waterway, @Override String leisure, - @Override String water, @Override boolean isIntermittent, @Override boolean isTunnel, @Override boolean isBridge, - @Override SourceFeature source) implements Row, WithName, WithNameEn, WithNameDe, WithNatural, WithLanduse, - WithWaterway, WithLeisure, WithWater, WithIsIntermittent, WithIsTunnel, WithIsBridge, WithSource { + @Override String place, @Override String natural, @Override String landuse, @Override String waterway, + @Override String leisure, @Override String water, @Override boolean isIntermittent, @Override boolean isTunnel, + @Override boolean isBridge, @Override SourceFeature source) + implements Row, WithName, WithNameEn, WithNameDe, WithPlace, WithNatural, WithLanduse, WithWaterway, WithLeisure, + WithWater, WithIsIntermittent, WithIsTunnel, WithIsBridge, WithSource { public OsmWaterPolygon(SourceFeature source, String mappingKey) { this(source.getString("name"), source.getString("name:en"), source.getString("name:de"), - source.getString("natural"), source.getString("landuse"), source.getString("waterway"), - source.getString("leisure"), source.getString("water"), source.getBoolean("intermittent"), - source.getBoolean("tunnel"), source.getBoolean("bridge"), source); + source.getString("place"), source.getString("natural"), source.getString("landuse"), + source.getString("waterway"), source.getString("leisure"), source.getString("water"), + source.getBoolean("intermittent"), source.getBoolean("tunnel"), source.getBoolean("bridge"), source); } /** Imposm3 "mapping" to filter OSM elements that should appear in this "table". */ public static final Expression MAPPING = and( or(matchAny("landuse", "reservoir", "basin", "salt_pond"), matchAny("leisure", "swimming_pool"), - matchAny("natural", "water", "bay", "spring"), matchAny("waterway", "dock"), matchAny("water", "river")), + matchAny("natural", "water", "bay", "spring"), matchAny("waterway", "dock"), + matchAny("water", "river", "pond", "basin", "wastewater")), not(matchAny("covered", "yes")), matchType("polygon")); /** @@ -521,16 +524,19 @@ public interface Handler { } /** An OSM element that would appear in the {@code osm_marine_point} table generated by imposm3. */ public record OsmMarinePoint(@Override String name, @Override String nameEn, @Override String nameDe, - @Override String place, @Override long rank, @Override boolean isIntermittent, @Override SourceFeature source) - implements Row, WithName, WithNameEn, WithNameDe, WithPlace, WithRank, WithIsIntermittent, WithSource { + @Override String place, @Override String natural, @Override long rank, @Override boolean isIntermittent, + @Override SourceFeature source) + implements Row, WithName, WithNameEn, WithNameDe, WithPlace, WithNatural, WithRank, WithIsIntermittent, WithSource { public OsmMarinePoint(SourceFeature source, String mappingKey) { this(source.getString("name"), source.getString("name:en"), source.getString("name:de"), - source.getString("place"), source.getLong("rank"), source.getBoolean("intermittent"), source); + source.getString("place"), source.getString("natural"), source.getLong("rank"), + source.getBoolean("intermittent"), source); } /** Imposm3 "mapping" to filter OSM elements that should appear in this "table". */ public static final Expression MAPPING = - and(matchAny("place", "ocean", "sea"), matchField("name"), matchType("point")); + and(or(matchAny("place", "ocean", "sea"), matchAny("natural", "bay", "strait")), matchField("name"), + matchType("point")); /** * Interface for layer implementations to extend to subscribe to OSM elements filtered and parsed as From 862e50aef1743cc3fe557cc00f4e1f31a4c68ae0 Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Thu, 6 Jul 2023 18:23:07 +0200 Subject: [PATCH 003/106] SQL -> Java re-implementation of OMT PR 1457 --- .../org/openmaptiles/layers/WaterName.java | 50 ++++++++++++++++--- .../openmaptiles/layers/WaterNameTest.java | 40 ++++++++++++++- 2 files changed, 80 insertions(+), 10 deletions(-) diff --git a/src/main/java/org/openmaptiles/layers/WaterName.java b/src/main/java/org/openmaptiles/layers/WaterName.java index 2bc19fc9..8159ed28 100644 --- a/src/main/java/org/openmaptiles/layers/WaterName.java +++ b/src/main/java/org/openmaptiles/layers/WaterName.java @@ -35,6 +35,7 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE */ package org.openmaptiles.layers; +import static org.openmaptiles.util.Utils.coalesce; import static org.openmaptiles.util.Utils.nullIfEmpty; import com.carrotsearch.hppc.LongObjectMap; @@ -48,6 +49,7 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE import com.onthegomap.planetiler.util.Parse; import com.onthegomap.planetiler.util.Translations; import java.util.Map; +import java.util.Set; import java.util.concurrent.ConcurrentSkipListMap; import org.locationtech.jts.geom.Geometry; import org.openmaptiles.OpenMapTilesProfile; @@ -80,9 +82,8 @@ public class WaterName implements */ private static final Logger LOGGER = LoggerFactory.getLogger(WaterName.class); - private static final double WORLD_AREA_FOR_70K_SQUARE_METERS = - Math.pow(GeoUtils.metersToPixelAtEquator(0, Math.sqrt(70_000)) / 256d, 2); private static final double LOG2 = Math.log(2); + private static final Set SEA_OR_OCEAN_PLACE = Set.of("sea", "ocean"); private final Translations translations; // need to synchronize updates from multiple threads private final LongObjectMap lakeCenterlines = Hppc.newLongObjectHashMap(); @@ -141,7 +142,10 @@ public void processNaturalEarth(String table, SourceFeature feature, FeatureColl @Override public void process(Tables.OsmMarinePoint element, FeatureCollector features) { if (!element.name().isBlank()) { - String place = element.place(); + String clazz = coalesce( + nullIfEmpty(element.natural()), + nullIfEmpty(element.place()) + ); var source = element.source(); // use name from OSM, but get min zoom from natural earth based on fuzzy name match... Integer rank = Parse.parseIntOrNull(source.getTag("rank")); @@ -159,11 +163,20 @@ public void process(Tables.OsmMarinePoint element, FeatureCollector features) { rank = next.getValue(); } } - int minZoom = "ocean".equals(place) ? 0 : rank != null ? rank : 8; + int minZoom; + if ("ocean".equals(element.place())) { + minZoom = 0; + } else if (rank != null) { + minZoom = rank; + } else if ("bay".equals(element.natural())) { + minZoom = 13; + } else { + minZoom = 8; + } features.point(LAYER_NAME) .setBufferPixels(BUFFER_SIZE) .putAttrs(OmtLanguageUtils.getNames(source.tags(), translations)) - .setAttr(Fields.CLASS, place) + .setAttr(Fields.CLASS, clazz) .setAttr(Fields.INTERMITTENT, element.isIntermittent() ? 1 : 0) .setMinZoom(minZoom); } @@ -176,6 +189,7 @@ public void process(Tables.OsmWaterPolygon element, FeatureCollector features) { Geometry centerlineGeometry = lakeCenterlines.get(element.source().id()); FeatureCollector.Feature feature; int minzoom = 9; + String place = element.place(); if (centerlineGeometry != null) { // prefer lake centerline if it exists feature = features.geometry(LAYER_NAME, centerlineGeometry) @@ -185,11 +199,22 @@ public void process(Tables.OsmWaterPolygon element, FeatureCollector features) { feature = features.pointOnSurface(LAYER_NAME); Geometry geometry = element.source().worldGeometry(); double area = geometry.getArea(); - minzoom = (int) Math.floor(20 - Math.log(area / WORLD_AREA_FOR_70K_SQUARE_METERS) / LOG2); - minzoom = Math.min(14, Math.max(9, minzoom)); + if (place != null && SEA_OR_OCEAN_PLACE.contains(place)) { + minzoom = 0; + } else { + minzoom = areaToMinZoom(area); + } + } + String clazz; + if ("bay".equals(element.natural())) { + clazz = FieldValues.CLASS_BAY; + } else if ("sea".equals(place)) { + clazz = FieldValues.CLASS_SEA; + } else { + clazz = FieldValues.CLASS_LAKE; } feature - .setAttr(Fields.CLASS, FieldValues.CLASS_LAKE) + .setAttr(Fields.CLASS, clazz) .setBufferPixels(BUFFER_SIZE) .putAttrs(OmtLanguageUtils.getNames(element.source().tags(), translations)) .setAttr(Fields.INTERMITTENT, element.isIntermittent() ? 1 : 0) @@ -199,4 +224,13 @@ public void process(Tables.OsmWaterPolygon element, FeatureCollector features) { } } } + + public static int areaToMinZoom(double areaWorld) { + double oneSideWorld = Math.sqrt(areaWorld); + // 1/4 of area => 1/2 of side => 256 / 2 = 128 + int zoom = (int) Math.round( + Math.log(128d / oneSideWorld) / LOG2) - + 8; + return Math.min(14, Math.max(3, zoom)); + } } diff --git a/src/test/java/org/openmaptiles/layers/WaterNameTest.java b/src/test/java/org/openmaptiles/layers/WaterNameTest.java index 54e0b00d..abd06c64 100644 --- a/src/test/java/org/openmaptiles/layers/WaterNameTest.java +++ b/src/test/java/org/openmaptiles/layers/WaterNameTest.java @@ -27,7 +27,7 @@ void testWaterNamePoint() { "_layer", "water_name", "_type", "point", - "_minzoom", 9, + "_minzoom", 3, "_maxzoom", 14 )), process(polygonFeatureWithArea(1, Map.of( "name", "waterway", @@ -36,7 +36,8 @@ void testWaterNamePoint() { "water", "pond", "intermittent", "1" )))); - double z11area = Math.pow((GeoUtils.metersToPixelAtEquator(0, Math.sqrt(70_000)) / 256d), 2) * Math.pow(2, 20 - 11); + // 1/4 th of tile area is the threshold, 1/4 = 0.25 => area->side:0.25->0.5 => slightly bigger -> 0.51 + double z11area = Math.pow(0.51d / Math.pow(2, 11), 2); assertFeatures(10, List.of(Map.of( "_layer", "water" ), Map.of( @@ -51,6 +52,41 @@ void testWaterNamePoint() { )))); } + // https://zelonewolf.github.io/openstreetmap-americana/#map=13/41.43989/-71.5716 + @Test + void testWordenPondNamePoint() { + assertFeatures(10, List.of(Map.of( + "_layer", "water" + ), Map.of( + "_layer", "water_name", + "_type", "point", + "_minzoom", 13, + "_maxzoom", 14 + )), process(polygonFeatureWithArea(4.930387948170328E-9, Map.of( + "name", "waterway", + "natural", "water", + "water", "pond" + )))); + } + + // FIXME later + // https://zelonewolf.github.io/openstreetmap-americana/#map=12/41.43989/-71.5716 + //@Test + void testPointJudithPondNamePoint() { + assertFeatures(10, List.of(Map.of( + "_layer", "water" + ), Map.of( + "_layer", "water_name", + "_type", "point", + "_minzoom", 12, + "_maxzoom", 14 + )), process(polygonFeatureWithArea(7.024280929916055E-9, Map.of( + "name", "waterway", + "natural", "water", + "water", "pond" + )))); + } + @Test void testWaterNameLakeline() { assertFeatures(11, List.of(), process(SimpleFeature.create( From 8998ddddc605154e4add876a84589d299ef5f718 Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Wed, 5 Jul 2023 13:37:21 +0200 Subject: [PATCH 004/106] version bumped from 3.14.0 to 3.15.0-SNAPSHOT --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 5f392ffb..57cd3327 100644 --- a/pom.xml +++ b/pom.xml @@ -22,7 +22,7 @@ org.openmaptiles planetiler-openmaptiles - 3.14.0 + 3.15.0-SNAPSHOT OpenMapTiles Vector Tile Schema implementation for Planetiler tool From 14c204d9dc9435a4b457fab6674e0db90582cad2 Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Fri, 7 Jul 2023 16:25:06 +0200 Subject: [PATCH 005/106] WaterName.areaToMinZoom(): improved handling of rounding and precission + added unit tests --- .../org/openmaptiles/layers/WaterName.java | 12 ++-- .../openmaptiles/layers/WaterNameTest.java | 71 ++++++++++++++----- 2 files changed, 61 insertions(+), 22 deletions(-) diff --git a/src/main/java/org/openmaptiles/layers/WaterName.java b/src/main/java/org/openmaptiles/layers/WaterName.java index 8159ed28..870076be 100644 --- a/src/main/java/org/openmaptiles/layers/WaterName.java +++ b/src/main/java/org/openmaptiles/layers/WaterName.java @@ -228,9 +228,13 @@ public void process(Tables.OsmWaterPolygon element, FeatureCollector features) { public static int areaToMinZoom(double areaWorld) { double oneSideWorld = Math.sqrt(areaWorld); // 1/4 of area => 1/2 of side => 256 / 2 = 128 - int zoom = (int) Math.round( - Math.log(128d / oneSideWorld) / LOG2) - - 8; - return Math.min(14, Math.max(3, zoom)); + double zoom = (Math.log(128d / oneSideWorld) / LOG2) - 8; + + // Say Z13.01 means bellow threshold, Z13.00 is exactly threshold, Z12.99 is over threshold, + // hence Z13.01 and Z13.00 will be rounded to Z14 and Z12.99 to Z13 (e.g. `floor() + 1`). + // And to accommodate for some precision errors (observed for Z9-Z11) we do also `- 0.1e-11`. + int result = (int) Math.floor(zoom - 0.1e-11) + 1; + + return Math.min(14, Math.max(3, result)); } } diff --git a/src/test/java/org/openmaptiles/layers/WaterNameTest.java b/src/test/java/org/openmaptiles/layers/WaterNameTest.java index abd06c64..ccc72050 100644 --- a/src/test/java/org/openmaptiles/layers/WaterNameTest.java +++ b/src/test/java/org/openmaptiles/layers/WaterNameTest.java @@ -5,7 +5,10 @@ import com.onthegomap.planetiler.TestUtils; import com.onthegomap.planetiler.geo.GeoUtils; +import com.onthegomap.planetiler.geo.GeometryException; import com.onthegomap.planetiler.reader.SimpleFeature; +import com.onthegomap.planetiler.reader.SourceFeature; +import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -69,24 +72,6 @@ void testWordenPondNamePoint() { )))); } - // FIXME later - // https://zelonewolf.github.io/openstreetmap-americana/#map=12/41.43989/-71.5716 - //@Test - void testPointJudithPondNamePoint() { - assertFeatures(10, List.of(Map.of( - "_layer", "water" - ), Map.of( - "_layer", "water_name", - "_type", "point", - "_minzoom", 12, - "_maxzoom", 14 - )), process(polygonFeatureWithArea(7.024280929916055E-9, Map.of( - "name", "waterway", - "natural", "water", - "water", "pond" - )))); - } - @Test void testWaterNameLakeline() { assertFeatures(11, List.of(), process(SimpleFeature.create( @@ -237,4 +222,54 @@ void testMarinePoint() { "place", "sea" )))); } + + private record TestEntry( + SourceFeature feature, + int expectedZoom + ) {} + + private void createAreaForMinZoomTest(List testEntries, double side, int expectedZoom, String name) { + double area = Math.pow(side, 2); + var feature = polygonFeatureWithArea(area, Map.of( + "name", name, + "natural", "water" + )); + testEntries.add(new TestEntry( + feature, + Math.min(14, Math.max(3, expectedZoom)) + )); + } + + @Test + void testAreaToMinZoom() throws GeometryException { + // threshold is 1/4 of tile area, hence ... + // ... side if 1/2 tile side: from pixels to world coord, for say Z14 ... + //final double HALF_OF_TILE_SIDE = 128d / Math.pow(2d, 14d + 8d); + // ... and then for some lower zoom: + //double testAreaSide = HALF_OF_TILE_SIDE * Math.pow(2, 14 - zoom); + // all this then simplified to `testAreaSide` calculation bellow + + final List testEntries = new ArrayList<>(); + for (int zoom = 14; zoom >= 0; zoom--) { + double testAreaSide = Math.pow(2, - zoom - 1); + + // slightly bellow the threshold + createAreaForMinZoomTest(testEntries, testAreaSide * 0.999, zoom + 1, "waterway-"); + // precisely at the threshold + createAreaForMinZoomTest(testEntries, testAreaSide, zoom, "waterway="); + // slightly over the threshold + createAreaForMinZoomTest(testEntries, testAreaSide * 1.001, zoom, "waterway+"); + } + + for (var entry : testEntries) { + assertFeatures(10, List.of(Map.of( + "_layer", "water" + ), Map.of( + "_layer", "water_name", + "_type", "point", + "_minzoom", entry.expectedZoom, + "_maxzoom", 14 + )), process(entry.feature)); + } + } } From fcfe11595307ed915e668e81c3ce1e9a9fd7e8ba Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Fri, 7 Jul 2023 16:26:11 +0200 Subject: [PATCH 006/106] mvn spotless:apply --- .../openmaptiles/layers/WaterNameTest.java | 40 +++++++++---------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/src/test/java/org/openmaptiles/layers/WaterNameTest.java b/src/test/java/org/openmaptiles/layers/WaterNameTest.java index ccc72050..77ec32e3 100644 --- a/src/test/java/org/openmaptiles/layers/WaterNameTest.java +++ b/src/test/java/org/openmaptiles/layers/WaterNameTest.java @@ -59,16 +59,16 @@ void testWaterNamePoint() { @Test void testWordenPondNamePoint() { assertFeatures(10, List.of(Map.of( - "_layer", "water" + "_layer", "water" ), Map.of( - "_layer", "water_name", - "_type", "point", - "_minzoom", 13, - "_maxzoom", 14 + "_layer", "water_name", + "_type", "point", + "_minzoom", 13, + "_maxzoom", 14 )), process(polygonFeatureWithArea(4.930387948170328E-9, Map.of( - "name", "waterway", - "natural", "water", - "water", "pond" + "name", "waterway", + "natural", "water", + "water", "pond" )))); } @@ -224,19 +224,19 @@ void testMarinePoint() { } private record TestEntry( - SourceFeature feature, - int expectedZoom + SourceFeature feature, + int expectedZoom ) {} private void createAreaForMinZoomTest(List testEntries, double side, int expectedZoom, String name) { double area = Math.pow(side, 2); var feature = polygonFeatureWithArea(area, Map.of( - "name", name, - "natural", "water" + "name", name, + "natural", "water" )); testEntries.add(new TestEntry( - feature, - Math.min(14, Math.max(3, expectedZoom)) + feature, + Math.min(14, Math.max(3, expectedZoom)) )); } @@ -251,7 +251,7 @@ void testAreaToMinZoom() throws GeometryException { final List testEntries = new ArrayList<>(); for (int zoom = 14; zoom >= 0; zoom--) { - double testAreaSide = Math.pow(2, - zoom - 1); + double testAreaSide = Math.pow(2, -zoom - 1); // slightly bellow the threshold createAreaForMinZoomTest(testEntries, testAreaSide * 0.999, zoom + 1, "waterway-"); @@ -263,12 +263,12 @@ void testAreaToMinZoom() throws GeometryException { for (var entry : testEntries) { assertFeatures(10, List.of(Map.of( - "_layer", "water" + "_layer", "water" ), Map.of( - "_layer", "water_name", - "_type", "point", - "_minzoom", entry.expectedZoom, - "_maxzoom", 14 + "_layer", "water_name", + "_type", "point", + "_minzoom", entry.expectedZoom, + "_maxzoom", 14 )), process(entry.feature)); } } From 91b18423236990aa20c8687767bb60ccf62ae5a1 Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Sun, 9 Jul 2023 20:28:06 +0200 Subject: [PATCH 007/106] water label min. zoom calculation simplified --- src/main/java/org/openmaptiles/layers/WaterName.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/openmaptiles/layers/WaterName.java b/src/main/java/org/openmaptiles/layers/WaterName.java index 870076be..d031bdbe 100644 --- a/src/main/java/org/openmaptiles/layers/WaterName.java +++ b/src/main/java/org/openmaptiles/layers/WaterName.java @@ -197,8 +197,7 @@ public void process(Tables.OsmWaterPolygon element, FeatureCollector features) { } else { // otherwise just use a label point inside the lake feature = features.pointOnSurface(LAYER_NAME); - Geometry geometry = element.source().worldGeometry(); - double area = geometry.getArea(); + double area = element.source().area(); if (place != null && SEA_OR_OCEAN_PLACE.contains(place)) { minzoom = 0; } else { @@ -228,7 +227,7 @@ public void process(Tables.OsmWaterPolygon element, FeatureCollector features) { public static int areaToMinZoom(double areaWorld) { double oneSideWorld = Math.sqrt(areaWorld); // 1/4 of area => 1/2 of side => 256 / 2 = 128 - double zoom = (Math.log(128d / oneSideWorld) / LOG2) - 8; + double zoom = - ( Math.log(oneSideWorld) / LOG2) - 1; // Say Z13.01 means bellow threshold, Z13.00 is exactly threshold, Z12.99 is over threshold, // hence Z13.01 and Z13.00 will be rounded to Z14 and Z12.99 to Z13 (e.g. `floor() + 1`). From 05b91a7dcb363467068d40b0c48adf901c5b2f2f Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Sun, 9 Jul 2023 20:34:27 +0200 Subject: [PATCH 008/106] comment adjusted to be hopefully more useful --- src/main/java/org/openmaptiles/layers/WaterName.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/openmaptiles/layers/WaterName.java b/src/main/java/org/openmaptiles/layers/WaterName.java index d031bdbe..b1341b76 100644 --- a/src/main/java/org/openmaptiles/layers/WaterName.java +++ b/src/main/java/org/openmaptiles/layers/WaterName.java @@ -226,7 +226,7 @@ public void process(Tables.OsmWaterPolygon element, FeatureCollector features) { public static int areaToMinZoom(double areaWorld) { double oneSideWorld = Math.sqrt(areaWorld); - // 1/4 of area => 1/2 of side => 256 / 2 = 128 + // full(-er) formula (along with comments) is in WaterNameTest.testAreaToMinZoom(), here is simplified reverse of that double zoom = - ( Math.log(oneSideWorld) / LOG2) - 1; // Say Z13.01 means bellow threshold, Z13.00 is exactly threshold, Z12.99 is over threshold, From 096b1a450f280c629b31a30b1130baf017829147 Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Mon, 10 Jul 2023 15:50:49 +0200 Subject: [PATCH 009/106] mvn spotless:apply --- src/main/java/org/openmaptiles/layers/WaterName.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/openmaptiles/layers/WaterName.java b/src/main/java/org/openmaptiles/layers/WaterName.java index b1341b76..04c4f96b 100644 --- a/src/main/java/org/openmaptiles/layers/WaterName.java +++ b/src/main/java/org/openmaptiles/layers/WaterName.java @@ -227,7 +227,7 @@ public void process(Tables.OsmWaterPolygon element, FeatureCollector features) { public static int areaToMinZoom(double areaWorld) { double oneSideWorld = Math.sqrt(areaWorld); // full(-er) formula (along with comments) is in WaterNameTest.testAreaToMinZoom(), here is simplified reverse of that - double zoom = - ( Math.log(oneSideWorld) / LOG2) - 1; + double zoom = -(Math.log(oneSideWorld) / LOG2) - 1; // Say Z13.01 means bellow threshold, Z13.00 is exactly threshold, Z12.99 is over threshold, // hence Z13.01 and Z13.00 will be rounded to Z14 and Z12.99 to Z13 (e.g. `floor() + 1`). From fda198f475ae52eb63083f5adb9bfea0323ed227 Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Mon, 10 Jul 2023 15:52:22 +0200 Subject: [PATCH 010/106] minzoom for CA_TRANSCANADA and US_INTERSTATE trunk now 4 (to match OMT PR 1440) --- .../openmaptiles/layers/Transportation.java | 13 ++++++++++++ .../layers/TransportationTest.java | 20 +++++++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/src/main/java/org/openmaptiles/layers/Transportation.java b/src/main/java/org/openmaptiles/layers/Transportation.java index 585b0384..95d3e4ff 100644 --- a/src/main/java/org/openmaptiles/layers/Transportation.java +++ b/src/main/java/org/openmaptiles/layers/Transportation.java @@ -62,6 +62,7 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE import java.util.Comparator; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Set; import java.util.concurrent.atomic.AtomicBoolean; import java.util.regex.Matcher; @@ -137,6 +138,9 @@ public class Transportation implements private static final Set ACCESS_NO_VALUES = Set.of( "private", "no" ); + private static final Set TRUNK_AS_MOTORWAY_BY_NETWORK = Set.of( + RouteNetwork.CA_TRANSCANADA.toString(), RouteNetwork.US_INTERSTATE.toString() + ); private static final ZoomFunction.MeterToPixelThresholds MIN_LENGTH = ZoomFunction.meterThresholds() .put(7, 50) .put(6, 100) @@ -413,6 +417,15 @@ int getMinzoom(Tables.OsmHighwayLinestring element, String highwayClass) { case FieldValues.CLASS_SERVICE -> isDrivewayOrParkingAisle(service(element.service())) ? 14 : 13; case FieldValues.CLASS_TRACK, FieldValues.CLASS_PATH -> routeRank == 1 ? 12 : (z13Paths || !nullOrEmpty(element.name()) || routeRank <= 2 || !nullOrEmpty(element.sacScale())) ? 13 : 14; + case FieldValues.CLASS_TRUNK -> { + // trunks in some networks to have same min. zoom as highway = "motorway" + String clazz = routeRelations.stream() + .map(RouteRelation::networkType) + .filter(Objects::nonNull) + .map(Enum::toString) + .anyMatch(TRUNK_AS_MOTORWAY_BY_NETWORK::contains) ? FieldValues.CLASS_MOTORWAY : FieldValues.CLASS_TRUNK; + yield MINZOOMS.getOrDefault(clazz, Integer.MAX_VALUE); + } default -> MINZOOMS.getOrDefault(baseClass, Integer.MAX_VALUE); }; } diff --git a/src/test/java/org/openmaptiles/layers/TransportationTest.java b/src/test/java/org/openmaptiles/layers/TransportationTest.java index 5f1e6636..5a2ac50b 100644 --- a/src/test/java/org/openmaptiles/layers/TransportationTest.java +++ b/src/test/java/org/openmaptiles/layers/TransportationTest.java @@ -909,6 +909,26 @@ void testTransCanadaHighway() { )), features); } + @Test + void testTransCanadaTrunk() { + var rel = new OsmElement.Relation(1); + rel.setTag("type", "route"); + rel.setTag("route", "road"); + rel.setTag("network", "CA:transcanada:namedRoute"); + + FeatureCollector features = process(lineFeatureWithRelation( + profile.preprocessOsmRelation(rel), + Map.of( + "highway", "trunk" + ))); + + assertFeatures(13, List.of(Map.of( + "_layer", "transportation", + "class", "trunk", + "_minzoom", 4 + )), features); + } + @Test void testGreatBritainHighway() { process(SimpleFeature.create( From 74f655b8bad093f52a90126541766cc859051a10 Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Mon, 10 Jul 2023 19:00:25 +0200 Subject: [PATCH 011/106] minzoom for some other Canada trunks now 4 (to match OMT PR 1446) --- .../openmaptiles/layers/Transportation.java | 33 +- .../layers/TransportationTest.java | 292 ++++++++++++++++++ 2 files changed, 324 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/openmaptiles/layers/Transportation.java b/src/main/java/org/openmaptiles/layers/Transportation.java index 95d3e4ff..d220c961 100644 --- a/src/main/java/org/openmaptiles/layers/Transportation.java +++ b/src/main/java/org/openmaptiles/layers/Transportation.java @@ -139,7 +139,15 @@ public class Transportation implements "private", "no" ); private static final Set TRUNK_AS_MOTORWAY_BY_NETWORK = Set.of( - RouteNetwork.CA_TRANSCANADA.toString(), RouteNetwork.US_INTERSTATE.toString() + RouteNetwork.CA_TRANSCANADA.toString(), + RouteNetwork.CA_PROVINCIAL_ARTERIAL.toString(), + RouteNetwork.US_INTERSTATE.toString() + ); + private static final Set CA_AB_PRIMARY_AS_ARTERIAL_BY_REF = Set.of( + "2", "3", "4" + ); + private static final Set CA_BC_AS_ARTERIAL_BY_REF = Set.of( + "3", "5", "99" ); private static final ZoomFunction.MeterToPixelThresholds MIN_LENGTH = ZoomFunction.meterThresholds() .put(7, 50) @@ -272,6 +280,27 @@ public List preprocessOsmRelation(OsmElement.Relation relation) networkType = RouteNetwork.US_STATE; } else if (network != null && network.startsWith("CA:transcanada")) { networkType = RouteNetwork.CA_TRANSCANADA; + } else if (network != null && network.equals("CA:QC:A")) { + networkType = RouteNetwork.CA_PROVINCIAL_ARTERIAL; // CA:QC:A + } else if (network != null && network.equals("CA:ON:primary")) { + if (ref != null && ref.length() == 3 && ref.startsWith("4")) { + networkType = RouteNetwork.CA_PROVINCIAL_ARTERIAL; // CA:ON:primary|420 + } else if ("QEW".equals(ref)) { + networkType = RouteNetwork.CA_PROVINCIAL_ARTERIAL; // CA:ON:primary|QEW + } else { + networkType = RouteNetwork.CA_PROVINCIAL; // CA:ON:primary|85 + } + } else if (network != null && network.equals("CA:MB:PTH") && "75".equals(ref)) { + networkType = RouteNetwork.CA_PROVINCIAL_ARTERIAL; // CA:MB:PTH|75 + } else if (network != null && network.equals("CA:AB:primary") && ref != null && + CA_AB_PRIMARY_AS_ARTERIAL_BY_REF.contains(ref)) { + networkType = RouteNetwork.CA_PROVINCIAL_ARTERIAL; // CA:AB:primary|4 + } else if (network != null && network.equals("CA:BC") && ref != null && CA_BC_AS_ARTERIAL_BY_REF.contains(ref)) { + networkType = RouteNetwork.CA_PROVINCIAL_ARTERIAL; // CA:BC|5 + } else if (network != null && ((network.length() == 5 && network.startsWith("CA:")) || + (network.length() >= 6 && network.startsWith("CA:") && network.charAt(5) == ':'))) { + // in SQL: LIKE 'CA:__' OR network LIKE 'CA:__:%'; but wanted to avoid regexp hence more ugly + networkType = RouteNetwork.CA_PROVINCIAL; // network|ref: CA:AB:primary|11 CA:ON:private_toll|407 } int rank = switch (coalesce(network, "")) { @@ -563,6 +592,8 @@ enum RouteNetwork { US_HIGHWAY("us-highway"), US_STATE("us-state"), CA_TRANSCANADA("ca-transcanada"), + CA_PROVINCIAL_ARTERIAL("ca-provincial-arterial"), + CA_PROVINCIAL("ca-provincial"), GB_MOTORWAY("gb-motorway"), GB_TRUNK("gb-trunk"); diff --git a/src/test/java/org/openmaptiles/layers/TransportationTest.java b/src/test/java/org/openmaptiles/layers/TransportationTest.java index 5a2ac50b..ff954c82 100644 --- a/src/test/java/org/openmaptiles/layers/TransportationTest.java +++ b/src/test/java/org/openmaptiles/layers/TransportationTest.java @@ -3,6 +3,7 @@ import static com.onthegomap.planetiler.TestUtils.newLineString; import static com.onthegomap.planetiler.TestUtils.newPoint; import static com.onthegomap.planetiler.TestUtils.rectangle; +import static org.junit.jupiter.api.Assertions.assertFalse; import com.onthegomap.planetiler.FeatureCollector; import com.onthegomap.planetiler.config.Arguments; @@ -15,6 +16,7 @@ import java.util.List; import java.util.Map; import java.util.stream.Stream; +import java.util.stream.StreamSupport; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; @@ -929,6 +931,296 @@ void testTransCanadaTrunk() { )), features); } + @Test + void testTransCanadaProvincialCaQcA() { + var rel = new OsmElement.Relation(1); + rel.setTag("type", "route"); + rel.setTag("route", "road"); + rel.setTag("network", "CA:QC:A"); + + FeatureCollector features = process(lineFeatureWithRelation( + profile.preprocessOsmRelation(rel), + Map.of( + "highway", "trunk" + ))); + + assertFeatures(13, List.of(Map.of( + "_layer", "transportation", + "class", "trunk", + "network", "ca-provincial-arterial", + "_minzoom", 4 + )), features); + } + + @Test + void testTransCanadaProvincialCaOnPrimaryRef4xx() { + var rel = new OsmElement.Relation(1); + rel.setTag("type", "route"); + rel.setTag("route", "road"); + rel.setTag("network", "CA:ON:primary"); + rel.setTag("ref", "420"); + + FeatureCollector features = process(lineFeatureWithRelation( + profile.preprocessOsmRelation(rel), + Map.of( + "highway", "trunk" + ))); + + assertFeatures(13, List.of(Map.of( + "_layer", "transportation", + "class", "trunk", + "network", "ca-provincial-arterial", + "_minzoom", 4 + ), Map.of( + "_layer", "transportation_name", + "class", "trunk", + "ref", "420", + "network", "ca-provincial-arterial" + )), features); + } + + @Test + void testTransCanadaProvincialCaOnPrimaryRefQew() { + var rel = new OsmElement.Relation(1); + rel.setTag("type", "route"); + rel.setTag("route", "road"); + rel.setTag("network", "CA:ON:primary"); + rel.setTag("ref", "QEW"); + + FeatureCollector features = process(lineFeatureWithRelation( + profile.preprocessOsmRelation(rel), + Map.of( + "highway", "trunk" + ))); + + assertFeatures(13, List.of(Map.of( + "_layer", "transportation", + "class", "trunk", + "network", "ca-provincial-arterial", + "_minzoom", 4 + ), Map.of( + "_layer", "transportation_name", + "class", "trunk", + "ref", "QEW", + "network", "ca-provincial-arterial" + )), features); + } + + @Test + void testTransCanadaProvincialCaOnPrimaryRefOther() { + var rel = new OsmElement.Relation(1); + rel.setTag("type", "route"); + rel.setTag("route", "road"); + rel.setTag("network", "CA:ON:primary"); + rel.setTag("ref", "85"); + + FeatureCollector features = process(lineFeatureWithRelation( + profile.preprocessOsmRelation(rel), + Map.of( + "highway", "trunk" + ))); + + assertFeatures(13, List.of(Map.of( + "_layer", "transportation", + "class", "trunk", + "network", "ca-provincial", + "_minzoom", 5 + ), Map.of( + "_layer", "transportation_name", + "class", "trunk", + "ref", "85", + "network", "ca-provincial" + )), features); + } + + @Test + void testTransCanadaProvincialCaMbPthRef75() { + var rel = new OsmElement.Relation(1); + rel.setTag("type", "route"); + rel.setTag("route", "road"); + rel.setTag("network", "CA:MB:PTH"); + rel.setTag("ref", "75"); + + FeatureCollector features = process(lineFeatureWithRelation( + profile.preprocessOsmRelation(rel), + Map.of( + "highway", "trunk" + ))); + + assertFeatures(13, List.of(Map.of( + "_layer", "transportation", + "class", "trunk", + "network", "ca-provincial-arterial", + "_minzoom", 4 + ), Map.of( + "_layer", "transportation_name", + "class", "trunk", + "ref", "75", + "network", "ca-provincial-arterial" + )), features); + } + + @Test + void testTransCanadaProvincialCaMbPthRefOther() { + var rel = new OsmElement.Relation(1); + rel.setTag("type", "route"); + rel.setTag("route", "road"); + rel.setTag("network", "CA:MB:PTH"); + rel.setTag("ref", "77"); + + FeatureCollector features = process(lineFeatureWithRelation( + profile.preprocessOsmRelation(rel), + Map.of( + "highway", "trunk" + ))); + + assertFeatures(13, List.of(Map.of( + "_layer", "transportation", + "class", "trunk", + "network", "ca-provincial", + "_minzoom", 5 + ), Map.of( + "_layer", "transportation_name", + "class", "trunk", + "ref", "77", + "network", "ca-provincial" + )), features); + } + + @Test + void testTransCanadaProvincialCaAbPrimaryRef3() { + var rel = new OsmElement.Relation(1); + rel.setTag("type", "route"); + rel.setTag("route", "road"); + rel.setTag("network", "CA:AB:primary"); + rel.setTag("ref", "3"); + + FeatureCollector features = process(lineFeatureWithRelation( + profile.preprocessOsmRelation(rel), + Map.of( + "highway", "trunk" + ))); + + assertFeatures(13, List.of(Map.of( + "_layer", "transportation", + "class", "trunk", + "network", "ca-provincial-arterial", + "_minzoom", 4 + ), Map.of( + "_layer", "transportation_name", + "class", "trunk", + "ref", "3", + "network", "ca-provincial-arterial" + )), features); + } + + @Test + void testTransCanadaProvincialCaAbPrimaryRefOther() { + var rel = new OsmElement.Relation(1); + rel.setTag("type", "route"); + rel.setTag("route", "road"); + rel.setTag("network", "CA:AB:primary"); + rel.setTag("ref", "10"); + + FeatureCollector features = process(lineFeatureWithRelation( + profile.preprocessOsmRelation(rel), + Map.of( + "highway", "trunk" + ))); + + assertFeatures(13, List.of(Map.of( + "_layer", "transportation", + "class", "trunk", + "network", "ca-provincial", + "_minzoom", 5 + ), Map.of( + "_layer", "transportation_name", + "class", "trunk", + "ref", "10", + "network", "ca-provincial" + )), features); + } + + @Test + void testTransCanadaProvincialCaBcRef3() { + var rel = new OsmElement.Relation(1); + rel.setTag("type", "route"); + rel.setTag("route", "road"); + rel.setTag("network", "CA:BC"); + rel.setTag("ref", "3"); + + FeatureCollector features = process(lineFeatureWithRelation( + profile.preprocessOsmRelation(rel), + Map.of( + "highway", "trunk" + ))); + + assertFeatures(13, List.of(Map.of( + "_layer", "transportation", + "class", "trunk", + "network", "ca-provincial-arterial", + "_minzoom", 4 + ), Map.of( + "_layer", "transportation_name", + "class", "trunk", + "ref", "3", + "network", "ca-provincial-arterial" + )), features); + } + + @Test + void testTransCanadaProvincialCaBcRefOther() { + var rel = new OsmElement.Relation(1); + rel.setTag("type", "route"); + rel.setTag("route", "road"); + rel.setTag("network", "CA:BC"); + rel.setTag("ref", "10"); + + FeatureCollector features = process(lineFeatureWithRelation( + profile.preprocessOsmRelation(rel), + Map.of( + "highway", "trunk" + ))); + + assertFeatures(13, List.of(Map.of( + "_layer", "transportation", + "class", "trunk", + "network", "ca-provincial", + "_minzoom", 5 + ), Map.of( + "_layer", "transportation_name", + "class", "trunk", + "ref", "10", + "network", "ca-provincial" + )), features); + } + + @Test + void testTransCanadaProvincialCaOther() { + var rel = new OsmElement.Relation(1); + rel.setTag("type", "route"); + rel.setTag("route", "road"); + rel.setTag("network", "CA:yellowhead"); + + FeatureCollector features = process(lineFeatureWithRelation( + profile.preprocessOsmRelation(rel), + Map.of( + "highway", "trunk" + ))); + + assertFeatures(13, List.of(Map.of( + "_layer", "transportation", + "class", "trunk", + "_minzoom", 5 + )), features); + boolean caProvPresent = StreamSupport.stream(features.spliterator(), false) + .flatMap(f -> f.getAttrsAtZoom(13).entrySet().stream()) + .filter(e -> "network".equals(e.getKey())) + .map(Map.Entry::getValue) + .anyMatch(v -> "ca-provincial".equals(v) || "ca-provincial-arterial".equals(v)); + assertFalse(caProvPresent, "ca-provincial present"); + } + @Test void testGreatBritainHighway() { process(SimpleFeature.create( From cf92b5e26c5b1abaeeacf80c580f8acf0f25ef94 Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Mon, 10 Jul 2023 19:04:52 +0200 Subject: [PATCH 012/106] equals() simplified + clean-up of comments --- .../openmaptiles/layers/Transportation.java | 27 +++++++++---------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/src/main/java/org/openmaptiles/layers/Transportation.java b/src/main/java/org/openmaptiles/layers/Transportation.java index d220c961..6ea5c7c8 100644 --- a/src/main/java/org/openmaptiles/layers/Transportation.java +++ b/src/main/java/org/openmaptiles/layers/Transportation.java @@ -280,27 +280,26 @@ public List preprocessOsmRelation(OsmElement.Relation relation) networkType = RouteNetwork.US_STATE; } else if (network != null && network.startsWith("CA:transcanada")) { networkType = RouteNetwork.CA_TRANSCANADA; - } else if (network != null && network.equals("CA:QC:A")) { - networkType = RouteNetwork.CA_PROVINCIAL_ARTERIAL; // CA:QC:A - } else if (network != null && network.equals("CA:ON:primary")) { + } else if ("CA:QC:A".equals(network)) { + networkType = RouteNetwork.CA_PROVINCIAL_ARTERIAL; + } else if ("CA:ON:primary".equals(network)) { if (ref != null && ref.length() == 3 && ref.startsWith("4")) { - networkType = RouteNetwork.CA_PROVINCIAL_ARTERIAL; // CA:ON:primary|420 + networkType = RouteNetwork.CA_PROVINCIAL_ARTERIAL; } else if ("QEW".equals(ref)) { - networkType = RouteNetwork.CA_PROVINCIAL_ARTERIAL; // CA:ON:primary|QEW + networkType = RouteNetwork.CA_PROVINCIAL_ARTERIAL; } else { - networkType = RouteNetwork.CA_PROVINCIAL; // CA:ON:primary|85 + networkType = RouteNetwork.CA_PROVINCIAL; } - } else if (network != null && network.equals("CA:MB:PTH") && "75".equals(ref)) { - networkType = RouteNetwork.CA_PROVINCIAL_ARTERIAL; // CA:MB:PTH|75 - } else if (network != null && network.equals("CA:AB:primary") && ref != null && - CA_AB_PRIMARY_AS_ARTERIAL_BY_REF.contains(ref)) { - networkType = RouteNetwork.CA_PROVINCIAL_ARTERIAL; // CA:AB:primary|4 - } else if (network != null && network.equals("CA:BC") && ref != null && CA_BC_AS_ARTERIAL_BY_REF.contains(ref)) { - networkType = RouteNetwork.CA_PROVINCIAL_ARTERIAL; // CA:BC|5 + } else if ("CA:MB:PTH".equals(network) && "75".equals(ref)) { + networkType = RouteNetwork.CA_PROVINCIAL_ARTERIAL; + } else if ("CA:AB:primary".equals(network) && ref != null && CA_AB_PRIMARY_AS_ARTERIAL_BY_REF.contains(ref)) { + networkType = RouteNetwork.CA_PROVINCIAL_ARTERIAL; + } else if ("CA:BC".equals(network) && ref != null && CA_BC_AS_ARTERIAL_BY_REF.contains(ref)) { + networkType = RouteNetwork.CA_PROVINCIAL_ARTERIAL; } else if (network != null && ((network.length() == 5 && network.startsWith("CA:")) || (network.length() >= 6 && network.startsWith("CA:") && network.charAt(5) == ':'))) { // in SQL: LIKE 'CA:__' OR network LIKE 'CA:__:%'; but wanted to avoid regexp hence more ugly - networkType = RouteNetwork.CA_PROVINCIAL; // network|ref: CA:AB:primary|11 CA:ON:private_toll|407 + networkType = RouteNetwork.CA_PROVINCIAL; } int rank = switch (coalesce(network, "")) { From 32c59d1b87590b0407e717a7086b6767839fa1d3 Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Fri, 14 Jul 2023 09:47:04 +0200 Subject: [PATCH 013/106] regenerate-openmaptiles.sh 5f7b2c11b3224759a21133381ca7d959a1f3cf51 (to match content of OMT PR 1465) --- .../generated/OpenMapTilesSchema.java | 40 ++++++++++--------- .../org/openmaptiles/generated/Tables.java | 2 +- 2 files changed, 22 insertions(+), 20 deletions(-) diff --git a/src/main/java/org/openmaptiles/generated/OpenMapTilesSchema.java b/src/main/java/org/openmaptiles/generated/OpenMapTilesSchema.java index 3dd59c39..163f803f 100644 --- a/src/main/java/org/openmaptiles/generated/OpenMapTilesSchema.java +++ b/src/main/java/org/openmaptiles/generated/OpenMapTilesSchema.java @@ -49,8 +49,8 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE /** * All vector tile layer definitions, attributes, and allowed values generated from the OpenMapTiles - * vector tile schema 07f243c5d9efa558fa539d7a31b2ae50507aaa9d. + * "https://github.com/openmaptiles/openmaptiles/blob/5f7b2c11b3224759a21133381ca7d959a1f3cf51/openmaptiles.yaml">OpenMapTiles + * vector tile schema 5f7b2c11b3224759a21133381ca7d959a1f3cf51. */ @SuppressWarnings("unused") public class OpenMapTilesSchema { @@ -96,7 +96,7 @@ public static List createInstances(Translations translations, PlanetilerC * boundaries show up. So you might not be able to use border styling for ocean water features. * * Generated from water.yaml + * "https://github.com/openmaptiles/openmaptiles/blob/5f7b2c11b3224759a21133381ca7d959a1f3cf51/layers/water/water.yaml">water.yaml */ public interface Water extends Layer { double BUFFER_SIZE = 4.0; @@ -191,7 +191,7 @@ final class FieldMappings { * field applied. Waterways do not have a subclass field. * * Generated from waterway.yaml + * "https://github.com/openmaptiles/openmaptiles/blob/5f7b2c11b3224759a21133381ca7d959a1f3cf51/layers/waterway/waterway.yaml">waterway.yaml */ public interface Waterway extends Layer { double BUFFER_SIZE = 4.0; @@ -276,7 +276,7 @@ final class FieldMappings { * layer is to style wood (class=wood) and grass (class=grass) areas. * * Generated from landcover.yaml + * "https://github.com/openmaptiles/openmaptiles/blob/5f7b2c11b3224759a21133381ca7d959a1f3cf51/layers/landcover/landcover.yaml">landcover.yaml */ public interface Landcover extends Layer { double BUFFER_SIZE = 4.0; @@ -433,7 +433,7 @@ final class FieldMappings { * residential (urban) areas and at higher zoom levels mostly OSM landuse tags. * * Generated from landuse.yaml + * "https://github.com/openmaptiles/openmaptiles/blob/5f7b2c11b3224759a21133381ca7d959a1f3cf51/layers/landuse/landuse.yaml">landuse.yaml */ public interface Landuse extends Layer { double BUFFER_SIZE = 4.0; @@ -529,7 +529,7 @@ final class FieldMappings { * Natural peaks * * Generated from mountain_peak.yaml + * "https://github.com/openmaptiles/openmaptiles/blob/5f7b2c11b3224759a21133381ca7d959a1f3cf51/layers/mountain_peak/mountain_peak.yaml">mountain_peak.yaml */ public interface MountainPeak extends Layer { double BUFFER_SIZE = 64.0; @@ -604,7 +604,7 @@ final class FieldMappings { * leisure=nature_reserve. * * Generated from park.yaml + * "https://github.com/openmaptiles/openmaptiles/blob/5f7b2c11b3224759a21133381ca7d959a1f3cf51/layers/park/park.yaml">park.yaml */ public interface Park extends Layer { double BUFFER_SIZE = 4.0; @@ -664,7 +664,7 @@ final class FieldMappings { * but for most styles it makes sense to just style admin_level=2 and admin_level=4. * * Generated from boundary.yaml + * "https://github.com/openmaptiles/openmaptiles/blob/5f7b2c11b3224759a21133381ca7d959a1f3cf51/layers/boundary/boundary.yaml">boundary.yaml */ public interface Boundary extends Layer { double BUFFER_SIZE = 4.0; @@ -765,7 +765,7 @@ final class FieldMappings { * in the aeroway layer. * * Generated from aeroway.yaml + * "https://github.com/openmaptiles/openmaptiles/blob/5f7b2c11b3224759a21133381ca7d959a1f3cf51/layers/aeroway/aeroway.yaml">aeroway.yaml */ public interface Aeroway extends Layer { double BUFFER_SIZE = 4.0; @@ -825,7 +825,7 @@ final class FieldMappings { * features like plazas. * * Generated from transportation.yaml + * "https://github.com/openmaptiles/openmaptiles/blob/5f7b2c11b3224759a21133381ca7d959a1f3cf51/layers/transportation/transportation.yaml">transportation.yaml */ public interface Transportation extends Layer { double BUFFER_SIZE = 4.0; @@ -1169,7 +1169,7 @@ final class FieldMappings { * location:underground are excluded. * * Generated from building.yaml + * "https://github.com/openmaptiles/openmaptiles/blob/5f7b2c11b3224759a21133381ca7d959a1f3cf51/layers/building/building.yaml">building.yaml */ public interface Building extends Layer { double BUFFER_SIZE = 4.0; @@ -1211,7 +1211,7 @@ final class FieldMappings { * from OSM water bodies. Only the most important lakes contain labels. * * Generated from water_name.yaml + * "https://github.com/openmaptiles/openmaptiles/blob/5f7b2c11b3224759a21133381ca7d959a1f3cf51/layers/water_name/water_name.yaml">water_name.yaml */ public interface WaterName extends Layer { double BUFFER_SIZE = 256.0; @@ -1281,7 +1281,7 @@ final class FieldMappings { * while for other roads you should use name. * * Generated from transportation_name.yaml + * "https://github.com/openmaptiles/openmaptiles/blob/5f7b2c11b3224759a21133381ca7d959a1f3cf51/layers/transportation_name/transportation_name.yaml">transportation_name.yaml */ public interface TransportationName extends Layer { double BUFFER_SIZE = 8.0; @@ -1328,6 +1328,7 @@ final class Fields { *
  • "ca-provincial" *
  • "gb-motorway" *
  • "gb-trunk" + *
  • "gb-primary" *
  • "road (default)" *
*/ @@ -1441,9 +1442,10 @@ final class FieldValues { public static final String NETWORK_CA_PROVINCIAL = "ca-provincial"; public static final String NETWORK_GB_MOTORWAY = "gb-motorway"; public static final String NETWORK_GB_TRUNK = "gb-trunk"; + public static final String NETWORK_GB_PRIMARY = "gb-primary"; public static final String NETWORK_ROAD = "road"; public static final Set NETWORK_VALUES = Set.of("us-interstate", "us-highway", "us-state", - "ca-transcanada", "ca-provincial-arterial", "ca-provincial", "gb-motorway", "gb-trunk", "road"); + "ca-transcanada", "ca-provincial-arterial", "ca-provincial", "gb-motorway", "gb-trunk", "gb-primary", "road"); public static final String CLASS_MOTORWAY = "motorway"; public static final String CLASS_TRUNK = "trunk"; public static final String CLASS_PRIMARY = "primary"; @@ -1502,7 +1504,7 @@ final class FieldMappings { * create a text hierarchy. * * Generated from place.yaml + * "https://github.com/openmaptiles/openmaptiles/blob/5f7b2c11b3224759a21133381ca7d959a1f3cf51/layers/place/place.yaml">place.yaml */ public interface Place extends Layer { double BUFFER_SIZE = 256.0; @@ -1610,7 +1612,7 @@ final class FieldMappings { * housenumber. * * Generated from housenumber.yaml + * "https://github.com/openmaptiles/openmaptiles/blob/5f7b2c11b3224759a21133381ca7d959a1f3cf51/layers/housenumber/housenumber.yaml">housenumber.yaml */ public interface Housenumber extends Layer { double BUFFER_SIZE = 8.0; @@ -1640,7 +1642,7 @@ final class FieldMappings { * of OpenStreetMap tags. Mostly contains amenities, sport, shop and tourist POIs. * * Generated from poi.yaml + * "https://github.com/openmaptiles/openmaptiles/blob/5f7b2c11b3224759a21133381ca7d959a1f3cf51/layers/poi/poi.yaml">poi.yaml */ public interface Poi extends Layer { double BUFFER_SIZE = 64.0; @@ -1859,7 +1861,7 @@ final class FieldMappings { * Aerodrome labels * * Generated from aerodrome_label.yaml + * "https://github.com/openmaptiles/openmaptiles/blob/5f7b2c11b3224759a21133381ca7d959a1f3cf51/layers/aerodrome_label/aerodrome_label.yaml">aerodrome_label.yaml */ public interface AerodromeLabel extends Layer { double BUFFER_SIZE = 64.0; diff --git a/src/main/java/org/openmaptiles/generated/Tables.java b/src/main/java/org/openmaptiles/generated/Tables.java index a090d736..c49bf3c7 100644 --- a/src/main/java/org/openmaptiles/generated/Tables.java +++ b/src/main/java/org/openmaptiles/generated/Tables.java @@ -51,7 +51,7 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE /** * OSM element parsers generated from the imposm3 table definitions * in the OpenMapTiles + * "https://github.com/openmaptiles/openmaptiles/blob/5f7b2c11b3224759a21133381ca7d959a1f3cf51/openmaptiles.yaml">OpenMapTiles * vector tile schema. * * These filter and parse the raw OSM key/value attribute pairs on tags into records with fields that match the columns From 4e3f0fdbd4fb22734837e12b168473d5c668a14b Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Fri, 14 Jul 2023 14:14:31 +0200 Subject: [PATCH 014/106] GB road relations processing adjusted to match OMT PR 1465, e.g. handle also primary and secondary roads --- .../openmaptiles/layers/Transportation.java | 33 ++++- .../layers/TransportationTest.java | 136 ++++++++++++++++++ 2 files changed, 163 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/openmaptiles/layers/Transportation.java b/src/main/java/org/openmaptiles/layers/Transportation.java index 6ea5c7c8..e0c23298 100644 --- a/src/main/java/org/openmaptiles/layers/Transportation.java +++ b/src/main/java/org/openmaptiles/layers/Transportation.java @@ -104,7 +104,7 @@ public class Transportation implements */ private static final Logger LOGGER = LoggerFactory.getLogger(Transportation.class); - private static final Pattern GREAT_BRITAIN_REF_NETWORK_PATTERN = Pattern.compile("^[AM][0-9AM()]+"); + private static final Pattern GREAT_BRITAIN_REF_NETWORK_PATTERN = Pattern.compile("^[ABM][0-9ABM()]+"); private static final MultiExpression.Index classMapping = FieldMappings.Class.index(); private static final Set RAILWAY_RAIL_VALUES = Set.of( FieldValues.SUBCLASS_RAIL, @@ -339,10 +339,30 @@ List getRouteRelations(Tables.OsmHighwayLinestring element) { try { Geometry wayGeometry = element.source().worldGeometry(); if (greatBritain.intersects(wayGeometry)) { - Transportation.RouteNetwork networkType = - "motorway".equals(element.highway()) ? Transportation.RouteNetwork.GB_MOTORWAY : - Transportation.RouteNetwork.GB_TRUNK; - String network = "motorway".equals(element.highway()) ? "omt-gb-motorway" : "omt-gb-trunk"; + Transportation.RouteNetwork networkType; + String network; + switch (element.highway()) { + case "motorway" -> { + networkType = Transportation.RouteNetwork.GB_MOTORWAY; + network = "omt-gb-motorway"; + } + case "trunk" -> { + networkType = RouteNetwork.GB_TRUNK; + network = "omt-gb-trunk"; + } + case "primary" -> { + networkType = RouteNetwork.GB_PRIMARY; + network = "omt-gb-primary"; + } + case "secondary" -> { + networkType = null; + network = "omt-gb-primary"; + } + default -> { + networkType = null; + network = null; + } + } result.add(new RouteRelation(refMatcher.group(), network, networkType, (byte) -1, 0)); } @@ -594,7 +614,8 @@ enum RouteNetwork { CA_PROVINCIAL_ARTERIAL("ca-provincial-arterial"), CA_PROVINCIAL("ca-provincial"), GB_MOTORWAY("gb-motorway"), - GB_TRUNK("gb-trunk"); + GB_TRUNK("gb-trunk"), + GB_PRIMARY("gb-primary"); final String name; diff --git a/src/test/java/org/openmaptiles/layers/TransportationTest.java b/src/test/java/org/openmaptiles/layers/TransportationTest.java index ff954c82..ccd11bd9 100644 --- a/src/test/java/org/openmaptiles/layers/TransportationTest.java +++ b/src/test/java/org/openmaptiles/layers/TransportationTest.java @@ -1284,6 +1284,142 @@ void testGreatBritainHighway() { ))); } + @Test + void testGreatBritainTrunk() { + process(SimpleFeature.create( + rectangle(0, 0.1), + Map.of("iso_a2", "GB"), + OpenMapTilesProfile.NATURAL_EARTH_SOURCE, + "ne_10m_admin_0_countries", + 0 + )); + + // in GB + assertFeatures(13, List.of(Map.of( + "_layer", "transportation", + "class", "trunk", + "_minzoom", 5 + ), Map.of( + "_layer", "transportation_name", + "class", "trunk", + "ref", "A272", + "ref_length", 4, + "network", "gb-trunk", + "_minzoom", 8 + )), process(SimpleFeature.create( + newLineString(0, 0, 1, 1), + Map.of( + "highway", "trunk", + "ref", "A272" + ), + OpenMapTilesProfile.OSM_SOURCE, + null, + 0 + ))); + } + + @Test + void testGreatBritainPrimary() { + process(SimpleFeature.create( + rectangle(0, 0.1), + Map.of("iso_a2", "GB"), + OpenMapTilesProfile.NATURAL_EARTH_SOURCE, + "ne_10m_admin_0_countries", + 0 + )); + + // in GB + assertFeatures(13, List.of(Map.of( + "_layer", "transportation", + "class", "primary", + "_minzoom", 7 + ), Map.of( + "_layer", "transportation_name", + "class", "primary", + "ref", "A598", + "ref_length", 4, + "network", "gb-primary", + "_minzoom", 12 + )), process(SimpleFeature.create( + newLineString(0, 0, 1, 1), + Map.of( + "highway", "primary", + "ref", "A598" + ), + OpenMapTilesProfile.OSM_SOURCE, + null, + 0 + ))); + } + + @Test + void testGreatBritainSecondary() { + process(SimpleFeature.create( + rectangle(0, 0.1), + Map.of("iso_a2", "GB"), + OpenMapTilesProfile.NATURAL_EARTH_SOURCE, + "ne_10m_admin_0_countries", + 0 + )); + + // in GB + assertFeatures(13, List.of(Map.of( + "_layer", "transportation", + "class", "secondary", + "_minzoom", 9 + ), Map.of( + "_layer", "transportation_name", + "class", "secondary", + "ref", "B4558", + "ref_length", 5, + "network", "road", + "_minzoom", 12 + )), process(SimpleFeature.create( + newLineString(0, 0, 1, 1), + Map.of( + "highway", "secondary", + "ref", "B4558" + ), + OpenMapTilesProfile.OSM_SOURCE, + null, + 0 + ))); + } + + @Test + void testGreatBritainTertiary() { + process(SimpleFeature.create( + rectangle(0, 0.1), + Map.of("iso_a2", "GB"), + OpenMapTilesProfile.NATURAL_EARTH_SOURCE, + "ne_10m_admin_0_countries", + 0 + )); + + // in GB + assertFeatures(13, List.of(Map.of( + "_layer", "transportation", + "class", "tertiary", + "_minzoom", 11 + ), Map.of( + "_layer", "transportation_name", + "class", "tertiary", + "ref", "B4086", + "ref_length", 5, + "network", "road", + "_minzoom", 12 + )), process(SimpleFeature.create( + newLineString(0, 0, 1, 1), + Map.of( + "highway", "tertiary", + "ref", "B4086" + ), + OpenMapTilesProfile.OSM_SOURCE, + null, + 0 + ))); + } + @Test void testMergesDisconnectedRoadNameFeatures() throws GeometryException { testMergesLinestrings(Map.of("class", "motorway"), TransportationName.LAYER_NAME, 10, 14); From da044caf34d68ea4feffe57bc5dc048bb6c67ac9 Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Fri, 14 Jul 2023 17:35:33 +0200 Subject: [PATCH 015/106] regenerate-openmaptiles.sh edb42f2db3c2b0ec37045367720eed84d7bbd71f (to match content of OMT PR 1466) --- .../generated/OpenMapTilesSchema.java | 68 +++++++++++-------- .../org/openmaptiles/generated/Tables.java | 9 ++- 2 files changed, 45 insertions(+), 32 deletions(-) diff --git a/src/main/java/org/openmaptiles/generated/OpenMapTilesSchema.java b/src/main/java/org/openmaptiles/generated/OpenMapTilesSchema.java index 163f803f..ee3d08e1 100644 --- a/src/main/java/org/openmaptiles/generated/OpenMapTilesSchema.java +++ b/src/main/java/org/openmaptiles/generated/OpenMapTilesSchema.java @@ -49,8 +49,8 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE /** * All vector tile layer definitions, attributes, and allowed values generated from the OpenMapTiles - * vector tile schema 5f7b2c11b3224759a21133381ca7d959a1f3cf51. + * "https://github.com/openmaptiles/openmaptiles/blob/edb42f2db3c2b0ec37045367720eed84d7bbd71f/openmaptiles.yaml">OpenMapTiles + * vector tile schema edb42f2db3c2b0ec37045367720eed84d7bbd71f. */ @SuppressWarnings("unused") public class OpenMapTilesSchema { @@ -96,7 +96,7 @@ public static List createInstances(Translations translations, PlanetilerC * boundaries show up. So you might not be able to use border styling for ocean water features. * * Generated from water.yaml + * "https://github.com/openmaptiles/openmaptiles/blob/edb42f2db3c2b0ec37045367720eed84d7bbd71f/layers/water/water.yaml">water.yaml */ public interface Water extends Layer { double BUFFER_SIZE = 4.0; @@ -117,9 +117,13 @@ final class Fields { /** * All water polygons from OpenStreetMapData have the class - * ocean. Water bodies with the - * water=river tag are classified as - * river. Wet and dry docks tagged + * ocean. The water-covered areas of flowing water bodies with the + * water=river, + * water=canal, + * water=stream, + * water=ditch, or + * water=drain tags are classified + * as river. Wet and dry docks tagged * waterway=dock are classified as * a dock. Various minor waterbodies are classified as a pond. Swimming pools tagged * leisure=swimming_pool @@ -177,7 +181,7 @@ final class FieldValues { final class FieldMappings { public static final MultiExpression Class = MultiExpression.of(List.of(MultiExpression.entry("dock", matchAny("waterway", "dock")), - MultiExpression.entry("river", matchAny("water", "river")), + MultiExpression.entry("river", matchAny("water", "river", "stream", "canal", "ditch", "drain")), MultiExpression.entry("pond", matchAny("water", "pond", "basin", "wastewater")), MultiExpression.entry("lake", FALSE), MultiExpression.entry("ocean", FALSE), MultiExpression.entry("swimming_pool", matchAny("leisure", "swimming_pool")))); @@ -191,7 +195,7 @@ final class FieldMappings { * field applied. Waterways do not have a subclass field. * * Generated from waterway.yaml + * "https://github.com/openmaptiles/openmaptiles/blob/edb42f2db3c2b0ec37045367720eed84d7bbd71f/layers/waterway/waterway.yaml">waterway.yaml */ public interface Waterway extends Layer { double BUFFER_SIZE = 4.0; @@ -276,7 +280,7 @@ final class FieldMappings { * layer is to style wood (class=wood) and grass (class=grass) areas. * * Generated from landcover.yaml + * "https://github.com/openmaptiles/openmaptiles/blob/edb42f2db3c2b0ec37045367720eed84d7bbd71f/layers/landcover/landcover.yaml">landcover.yaml */ public interface Landcover extends Layer { double BUFFER_SIZE = 4.0; @@ -433,7 +437,7 @@ final class FieldMappings { * residential (urban) areas and at higher zoom levels mostly OSM landuse tags. * * Generated from landuse.yaml + * "https://github.com/openmaptiles/openmaptiles/blob/edb42f2db3c2b0ec37045367720eed84d7bbd71f/layers/landuse/landuse.yaml">landuse.yaml */ public interface Landuse extends Layer { double BUFFER_SIZE = 4.0; @@ -529,7 +533,7 @@ final class FieldMappings { * Natural peaks * * Generated from mountain_peak.yaml + * "https://github.com/openmaptiles/openmaptiles/blob/edb42f2db3c2b0ec37045367720eed84d7bbd71f/layers/mountain_peak/mountain_peak.yaml">mountain_peak.yaml */ public interface MountainPeak extends Layer { double BUFFER_SIZE = 64.0; @@ -604,7 +608,7 @@ final class FieldMappings { * leisure=nature_reserve. * * Generated from park.yaml + * "https://github.com/openmaptiles/openmaptiles/blob/edb42f2db3c2b0ec37045367720eed84d7bbd71f/layers/park/park.yaml">park.yaml */ public interface Park extends Layer { double BUFFER_SIZE = 4.0; @@ -664,7 +668,7 @@ final class FieldMappings { * but for most styles it makes sense to just style admin_level=2 and admin_level=4. * * Generated from boundary.yaml + * "https://github.com/openmaptiles/openmaptiles/blob/edb42f2db3c2b0ec37045367720eed84d7bbd71f/layers/boundary/boundary.yaml">boundary.yaml */ public interface Boundary extends Layer { double BUFFER_SIZE = 4.0; @@ -765,7 +769,7 @@ final class FieldMappings { * in the aeroway layer. * * Generated from aeroway.yaml + * "https://github.com/openmaptiles/openmaptiles/blob/edb42f2db3c2b0ec37045367720eed84d7bbd71f/layers/aeroway/aeroway.yaml">aeroway.yaml */ public interface Aeroway extends Layer { double BUFFER_SIZE = 4.0; @@ -825,7 +829,7 @@ final class FieldMappings { * features like plazas. * * Generated from transportation.yaml + * "https://github.com/openmaptiles/openmaptiles/blob/edb42f2db3c2b0ec37045367720eed84d7bbd71f/layers/transportation/transportation.yaml">transportation.yaml */ public interface Transportation extends Layer { double BUFFER_SIZE = 4.0; @@ -910,8 +914,9 @@ final class Fields { * The network type derived mainly from * network tag of the road. See more * info about us- , - * ca-transcanada, or - * gb- . + * ca-transcanada, + * gb- , + * or ie- . */ public static final String NETWORK = "network"; @@ -1169,7 +1174,7 @@ final class FieldMappings { * location:underground are excluded. * * Generated from building.yaml + * "https://github.com/openmaptiles/openmaptiles/blob/edb42f2db3c2b0ec37045367720eed84d7bbd71f/layers/building/building.yaml">building.yaml */ public interface Building extends Layer { double BUFFER_SIZE = 4.0; @@ -1211,7 +1216,7 @@ final class FieldMappings { * from OSM water bodies. Only the most important lakes contain labels. * * Generated from water_name.yaml + * "https://github.com/openmaptiles/openmaptiles/blob/edb42f2db3c2b0ec37045367720eed84d7bbd71f/layers/water_name/water_name.yaml">water_name.yaml */ public interface WaterName extends Layer { double BUFFER_SIZE = 256.0; @@ -1281,7 +1286,7 @@ final class FieldMappings { * while for other roads you should use name. * * Generated from transportation_name.yaml + * "https://github.com/openmaptiles/openmaptiles/blob/edb42f2db3c2b0ec37045367720eed84d7bbd71f/layers/transportation_name/transportation_name.yaml">transportation_name.yaml */ public interface TransportationName extends Layer { double BUFFER_SIZE = 8.0; @@ -1329,6 +1334,9 @@ final class Fields { *
  • "gb-motorway" *
  • "gb-trunk" *
  • "gb-primary" + *
  • "ie-motorway" + *
  • "ie-national" + *
  • "ie-regional" *
  • "road (default)" * */ @@ -1443,9 +1451,13 @@ final class FieldValues { public static final String NETWORK_GB_MOTORWAY = "gb-motorway"; public static final String NETWORK_GB_TRUNK = "gb-trunk"; public static final String NETWORK_GB_PRIMARY = "gb-primary"; + public static final String NETWORK_IE_MOTORWAY = "ie-motorway"; + public static final String NETWORK_IE_NATIONAL = "ie-national"; + public static final String NETWORK_IE_REGIONAL = "ie-regional"; public static final String NETWORK_ROAD = "road"; - public static final Set NETWORK_VALUES = Set.of("us-interstate", "us-highway", "us-state", - "ca-transcanada", "ca-provincial-arterial", "ca-provincial", "gb-motorway", "gb-trunk", "gb-primary", "road"); + public static final Set NETWORK_VALUES = + Set.of("us-interstate", "us-highway", "us-state", "ca-transcanada", "ca-provincial-arterial", "ca-provincial", + "gb-motorway", "gb-trunk", "gb-primary", "ie-motorway", "ie-national", "ie-regional", "road"); public static final String CLASS_MOTORWAY = "motorway"; public static final String CLASS_TRUNK = "trunk"; public static final String CLASS_PRIMARY = "primary"; @@ -1504,7 +1516,7 @@ final class FieldMappings { * create a text hierarchy. * * Generated from place.yaml + * "https://github.com/openmaptiles/openmaptiles/blob/edb42f2db3c2b0ec37045367720eed84d7bbd71f/layers/place/place.yaml">place.yaml */ public interface Place extends Layer { double BUFFER_SIZE = 256.0; @@ -1556,6 +1568,7 @@ final class Fields { *
  • "town" *
  • "village" *
  • "hamlet" + *
  • "borough" *
  • "suburb" *
  • "quarter" *
  • "neighbourhood" @@ -1593,13 +1606,14 @@ final class FieldValues { public static final String CLASS_TOWN = "town"; public static final String CLASS_VILLAGE = "village"; public static final String CLASS_HAMLET = "hamlet"; + public static final String CLASS_BOROUGH = "borough"; public static final String CLASS_SUBURB = "suburb"; public static final String CLASS_QUARTER = "quarter"; public static final String CLASS_NEIGHBOURHOOD = "neighbourhood"; public static final String CLASS_ISOLATED_DWELLING = "isolated_dwelling"; public static final String CLASS_ISLAND = "island"; public static final Set CLASS_VALUES = Set.of("continent", "country", "state", "province", "city", "town", - "village", "hamlet", "suburb", "quarter", "neighbourhood", "isolated_dwelling", "island"); + "village", "hamlet", "borough", "suburb", "quarter", "neighbourhood", "isolated_dwelling", "island"); } /** Complex mappings to generate attribute values from OSM element tags in the place layer. */ final class FieldMappings { @@ -1612,7 +1626,7 @@ final class FieldMappings { * housenumber. * * Generated from housenumber.yaml + * "https://github.com/openmaptiles/openmaptiles/blob/edb42f2db3c2b0ec37045367720eed84d7bbd71f/layers/housenumber/housenumber.yaml">housenumber.yaml */ public interface Housenumber extends Layer { double BUFFER_SIZE = 8.0; @@ -1642,7 +1656,7 @@ final class FieldMappings { * of OpenStreetMap tags. Mostly contains amenities, sport, shop and tourist POIs. * * Generated from poi.yaml + * "https://github.com/openmaptiles/openmaptiles/blob/edb42f2db3c2b0ec37045367720eed84d7bbd71f/layers/poi/poi.yaml">poi.yaml */ public interface Poi extends Layer { double BUFFER_SIZE = 64.0; @@ -1861,7 +1875,7 @@ final class FieldMappings { * Aerodrome labels * * Generated from aerodrome_label.yaml + * "https://github.com/openmaptiles/openmaptiles/blob/edb42f2db3c2b0ec37045367720eed84d7bbd71f/layers/aerodrome_label/aerodrome_label.yaml">aerodrome_label.yaml */ public interface AerodromeLabel extends Layer { double BUFFER_SIZE = 64.0; diff --git a/src/main/java/org/openmaptiles/generated/Tables.java b/src/main/java/org/openmaptiles/generated/Tables.java index c49bf3c7..e8cb1f9a 100644 --- a/src/main/java/org/openmaptiles/generated/Tables.java +++ b/src/main/java/org/openmaptiles/generated/Tables.java @@ -51,7 +51,7 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE /** * OSM element parsers generated from the imposm3 table definitions * in the OpenMapTiles + * "https://github.com/openmaptiles/openmaptiles/blob/edb42f2db3c2b0ec37045367720eed84d7bbd71f/openmaptiles.yaml">OpenMapTiles * vector tile schema. * * These filter and parse the raw OSM key/value attribute pairs on tags into records with fields that match the columns @@ -111,7 +111,7 @@ public OsmWaterPolygon(SourceFeature source, String mappingKey) { public static final Expression MAPPING = and( or(matchAny("landuse", "reservoir", "basin", "salt_pond"), matchAny("leisure", "swimming_pool"), matchAny("natural", "water", "bay", "spring"), matchAny("waterway", "dock"), - matchAny("water", "river", "pond", "basin", "wastewater")), + matchAny("water", "river", "stream", "canal", "ditch", "drain", "pond", "basin", "wastewater")), not(matchAny("covered", "yes")), matchType("polygon")); /** @@ -662,9 +662,8 @@ public OsmCityPoint(SourceFeature source, String mappingKey) { } /** Imposm3 "mapping" to filter OSM elements that should appear in this "table". */ - public static final Expression MAPPING = and( - matchAny("place", "city", "town", "village", "hamlet", "suburb", "quarter", "neighbourhood", "isolated_dwelling"), - matchField("name"), matchType("point")); + public static final Expression MAPPING = and(matchAny("place", "city", "town", "village", "hamlet", "borough", + "suburb", "quarter", "neighbourhood", "isolated_dwelling"), matchField("name"), matchType("point")); /** * Interface for layer implementations to extend to subscribe to OSM elements filtered and parsed as From 436e3b384623af70c4176d0af2439979372608c9 Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Fri, 14 Jul 2023 18:35:36 +0200 Subject: [PATCH 016/106] IE road relations processing adjusted to match OMT PR 1466, e.g. handle IE roates in similar way as GB routes --- .../openmaptiles/layers/Transportation.java | 63 ++++++- .../layers/TransportationTest.java | 165 ++++++++++++++++++ 2 files changed, 224 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/openmaptiles/layers/Transportation.java b/src/main/java/org/openmaptiles/layers/Transportation.java index e0c23298..36e9dde7 100644 --- a/src/main/java/org/openmaptiles/layers/Transportation.java +++ b/src/main/java/org/openmaptiles/layers/Transportation.java @@ -105,6 +105,7 @@ public class Transportation implements private static final Logger LOGGER = LoggerFactory.getLogger(Transportation.class); private static final Pattern GREAT_BRITAIN_REF_NETWORK_PATTERN = Pattern.compile("^[ABM][0-9ABM()]+"); + private static final Pattern IRELAND_REF_NETWORK_PATTERN = Pattern.compile("^[MNRL][0-9]+"); private static final MultiExpression.Index classMapping = FieldMappings.Class.index(); private static final Set RAILWAY_RAIL_VALUES = Set.of( FieldValues.SUBCLASS_RAIL, @@ -164,11 +165,13 @@ public class Transportation implements private static final Set ONEWAY_VALUES = Set.of(-1, 1); private static final String LIMIT_MERGE_TAG = "__limit_merge"; private final AtomicBoolean loggedNoGb = new AtomicBoolean(false); + private final AtomicBoolean loggedNoIreland = new AtomicBoolean(false); private final boolean z13Paths; private final Map MINZOOMS; private final Stats stats; private final PlanetilerConfig config; private PreparedGeometry greatBritain = null; + private PreparedGeometry ireland = null; public Transportation(Translations translations, PlanetilerConfig config, Stats stats) { this.config = config; @@ -251,9 +254,12 @@ private static boolean isBridgeOrPier(String manMade) { @Override public void processNaturalEarth(String table, SourceFeature feature, FeatureCollector features) { - if ("ne_10m_admin_0_countries".equals(table) && feature.hasTag("iso_a2", "GB")) { - // multiple threads call this method concurrently, GB polygon *should* only be found - // once, but just to be safe synchronize updates to that field + if (!"ne_10m_admin_0_countries".equals(table)) { + return; + } + // multiple threads call this method concurrently, GB (or IE) polygon *should* only be found + // once, but just to be safe synchronize updates to that field + if (feature.hasTag("iso_a2", "GB")) { synchronized (this) { try { Geometry boundary = feature.polygon().buffer(GeoUtils.metersToPixelAtEquator(0, 10_000) / 256d); @@ -262,6 +268,15 @@ public void processNaturalEarth(String table, SourceFeature feature, LOGGER.error("Failed to get Great Britain Polygon: " + e); } } + } else if (feature.hasTag("iso_a2", "IE")) { + synchronized (this) { + try { + Geometry boundary = feature.polygon().buffer(GeoUtils.metersToPixelAtEquator(0, 10_000) / 256d); + ireland = PreparedGeometryFactory.prepare(boundary); + } catch (GeometryException e) { + LOGGER.error("Failed to get Great Britain Polygon: " + e); + } + } } } @@ -372,6 +387,43 @@ List getRouteRelations(Tables.OsmHighwayLinestring element) { } } } + // Similarly Ireland. + refMatcher = IRELAND_REF_NETWORK_PATTERN.matcher(ref); + if (refMatcher.find()) { + if (ireland == null) { + if (!loggedNoIreland.get() && loggedNoIreland.compareAndSet(false, true)) { + LOGGER.warn("No IE polygon for inferring route network types"); + } + } else { + try { + Geometry wayGeometry = element.source().worldGeometry(); + if (ireland.intersects(wayGeometry)) { + Transportation.RouteNetwork networkType; + String network; + String highway = coalesce(element.highway(), ""); + switch (highway) { + case "motorway" -> { + networkType = Transportation.RouteNetwork.IE_MOTORWAY; + network = "omt-ie-motorway"; + } + case "trunk", "primary" -> { + networkType = RouteNetwork.IE_NATIONAL; + network = "omt-ie-national"; + } + default -> { + networkType = RouteNetwork.IE_REGIONAL; + network = "omt-ie-regional"; + } + } + result.add(new RouteRelation(refMatcher.group(), network, networkType, (byte) -1, + 0)); + } + } catch (GeometryException e) { + e.log(stats, "omt_transportation_name_ie_test", + "Unable to test highway against IE route network: " + element.source().id()); + } + } + } } Collections.sort(result); return result; @@ -615,7 +667,10 @@ enum RouteNetwork { CA_PROVINCIAL("ca-provincial"), GB_MOTORWAY("gb-motorway"), GB_TRUNK("gb-trunk"), - GB_PRIMARY("gb-primary"); + GB_PRIMARY("gb-primary"), + IE_MOTORWAY("ie-motorway"), + IE_NATIONAL("ie-national"), + IE_REGIONAL("ie-regional"); final String name; diff --git a/src/test/java/org/openmaptiles/layers/TransportationTest.java b/src/test/java/org/openmaptiles/layers/TransportationTest.java index ccd11bd9..f38a7c2c 100644 --- a/src/test/java/org/openmaptiles/layers/TransportationTest.java +++ b/src/test/java/org/openmaptiles/layers/TransportationTest.java @@ -1420,6 +1420,171 @@ void testGreatBritainTertiary() { ))); } + @Test + void testIrelandHighway() { + process(SimpleFeature.create( + rectangle(0, 0.1), + Map.of("iso_a2", "IE"), + OpenMapTilesProfile.NATURAL_EARTH_SOURCE, + "ne_10m_admin_0_countries", + 0 + )); + + // in IE + assertFeatures(13, List.of(Map.of( + "_layer", "transportation", + "class", "motorway", + "oneway", 1, + "ramp", "", + "_minzoom", 4 + ), Map.of( + "_layer", "transportation_name", + "class", "motorway", + "ref", "M18", + "ref_length", 3, + "network", "ie-motorway", + "_minzoom", 6 + )), process(SimpleFeature.create( + newLineString(0, 0, 1, 1), + Map.of( + "highway", "motorway", + "oneway", "yes", + "ref", "M18" + ), + OpenMapTilesProfile.OSM_SOURCE, + null, + 0 + ))); + + // not in IE + assertFeatures(13, List.of(Map.of( + "_layer", "transportation", + "class", "motorway", + "oneway", 1, + "ramp", "", + "_minzoom", 4 + ), Map.of( + "_layer", "transportation_name", + "class", "motorway", + "ref", "M18", + "ref_length", 3, + "network", "road", + "_minzoom", 6 + )), process(SimpleFeature.create( + newLineString(1, 0, 0, 1), + Map.of( + "highway", "motorway", + "oneway", "yes", + "ref", "M18" + ), + OpenMapTilesProfile.OSM_SOURCE, + null, + 0 + ))); + } + + @Test + void testIrelandTrunk() { + process(SimpleFeature.create( + rectangle(0, 0.1), + Map.of("iso_a2", "IE"), + OpenMapTilesProfile.NATURAL_EARTH_SOURCE, + "ne_10m_admin_0_countries", + 0 + )); + + // in IE + assertFeatures(13, List.of(Map.of( + "_layer", "transportation", + "class", "trunk", + "_minzoom", 5 + ), Map.of( + "_layer", "transportation_name", + "class", "trunk", + "ref", "N8", + "ref_length", 2, + "network", "ie-national", + "_minzoom", 8 + )), process(SimpleFeature.create( + newLineString(0, 0, 1, 1), + Map.of( + "highway", "trunk", + "ref", "N8" + ), + OpenMapTilesProfile.OSM_SOURCE, + null, + 0 + ))); + } + + @Test + void testIrelandPrimary() { + process(SimpleFeature.create( + rectangle(0, 0.1), + Map.of("iso_a2", "IE"), + OpenMapTilesProfile.NATURAL_EARTH_SOURCE, + "ne_10m_admin_0_countries", + 0 + )); + + // in IE + assertFeatures(13, List.of(Map.of( + "_layer", "transportation", + "class", "primary", + "_minzoom", 7 + ), Map.of( + "_layer", "transportation_name", + "class", "primary", + "ref", "N59", + "ref_length", 3, + "network", "ie-national", + "_minzoom", 12 + )), process(SimpleFeature.create( + newLineString(0, 0, 1, 1), + Map.of( + "highway", "primary", + "ref", "N59" + ), + OpenMapTilesProfile.OSM_SOURCE, + null, + 0 + ))); + } + + @Test + void testIrelandSecondary() { + process(SimpleFeature.create( + rectangle(0, 0.1), + Map.of("iso_a2", "IE"), + OpenMapTilesProfile.NATURAL_EARTH_SOURCE, + "ne_10m_admin_0_countries", + 0 + )); + + // in IE + assertFeatures(13, List.of(Map.of( + "_layer", "transportation", + "class", "secondary", + "_minzoom", 9 + ), Map.of( + "_layer", "transportation_name", + "class", "secondary", + "ref", "R813", + "ref_length", 4, + "network", "ie-regional", + "_minzoom", 12 + )), process(SimpleFeature.create( + newLineString(0, 0, 1, 1), + Map.of( + "highway", "secondary", + "ref", "R813" + ), + OpenMapTilesProfile.OSM_SOURCE, + null, + 0 + ))); + } + @Test void testMergesDisconnectedRoadNameFeatures() throws GeometryException { testMergesLinestrings(Map.of("class", "motorway"), TransportationName.LAYER_NAME, 10, 14); From 6354264ff15dfdb5007526d1ea4ec383e343eaa0 Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Fri, 14 Jul 2023 18:40:05 +0200 Subject: [PATCH 017/106] fixed handling of networkType for secondary GB routes --- src/main/java/org/openmaptiles/layers/Transportation.java | 2 +- src/test/java/org/openmaptiles/layers/TransportationTest.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/openmaptiles/layers/Transportation.java b/src/main/java/org/openmaptiles/layers/Transportation.java index e0c23298..0b0a1fa8 100644 --- a/src/main/java/org/openmaptiles/layers/Transportation.java +++ b/src/main/java/org/openmaptiles/layers/Transportation.java @@ -355,7 +355,7 @@ List getRouteRelations(Tables.OsmHighwayLinestring element) { network = "omt-gb-primary"; } case "secondary" -> { - networkType = null; + networkType = RouteNetwork.GB_PRIMARY; network = "omt-gb-primary"; } default -> { diff --git a/src/test/java/org/openmaptiles/layers/TransportationTest.java b/src/test/java/org/openmaptiles/layers/TransportationTest.java index ccd11bd9..d9fd38c9 100644 --- a/src/test/java/org/openmaptiles/layers/TransportationTest.java +++ b/src/test/java/org/openmaptiles/layers/TransportationTest.java @@ -1372,7 +1372,7 @@ void testGreatBritainSecondary() { "class", "secondary", "ref", "B4558", "ref_length", 5, - "network", "road", + "network", "gb-primary", "_minzoom", 12 )), process(SimpleFeature.create( newLineString(0, 0, 1, 1), From 984b7f2733fe1beb92c3353d7a7648d136cfec4d Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Fri, 14 Jul 2023 18:41:32 +0200 Subject: [PATCH 018/106] clean-up: case statements simplified --- src/main/java/org/openmaptiles/layers/Transportation.java | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/main/java/org/openmaptiles/layers/Transportation.java b/src/main/java/org/openmaptiles/layers/Transportation.java index 0b0a1fa8..f894dcdf 100644 --- a/src/main/java/org/openmaptiles/layers/Transportation.java +++ b/src/main/java/org/openmaptiles/layers/Transportation.java @@ -350,11 +350,7 @@ List getRouteRelations(Tables.OsmHighwayLinestring element) { networkType = RouteNetwork.GB_TRUNK; network = "omt-gb-trunk"; } - case "primary" -> { - networkType = RouteNetwork.GB_PRIMARY; - network = "omt-gb-primary"; - } - case "secondary" -> { + case "primary", "secondary" -> { networkType = RouteNetwork.GB_PRIMARY; network = "omt-gb-primary"; } From 1e1b896f573549f75d422d73f92b4bb68aa27f64 Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Fri, 14 Jul 2023 20:21:19 +0200 Subject: [PATCH 019/106] mvn spotless:apply --- .../openmaptiles/layers/Transportation.java | 4 +- .../layers/TransportationTest.java | 222 +++++++++--------- 2 files changed, 113 insertions(+), 113 deletions(-) diff --git a/src/main/java/org/openmaptiles/layers/Transportation.java b/src/main/java/org/openmaptiles/layers/Transportation.java index ba386492..ea3e90fd 100644 --- a/src/main/java/org/openmaptiles/layers/Transportation.java +++ b/src/main/java/org/openmaptiles/layers/Transportation.java @@ -412,11 +412,11 @@ List getRouteRelations(Tables.OsmHighwayLinestring element) { } } result.add(new RouteRelation(refMatcher.group(), network, networkType, (byte) -1, - 0)); + 0)); } } catch (GeometryException e) { e.log(stats, "omt_transportation_name_ie_test", - "Unable to test highway against IE route network: " + element.source().id()); + "Unable to test highway against IE route network: " + element.source().id()); } } } diff --git a/src/test/java/org/openmaptiles/layers/TransportationTest.java b/src/test/java/org/openmaptiles/layers/TransportationTest.java index c2f9e6b7..108925b8 100644 --- a/src/test/java/org/openmaptiles/layers/TransportationTest.java +++ b/src/test/java/org/openmaptiles/layers/TransportationTest.java @@ -1423,165 +1423,165 @@ void testGreatBritainTertiary() { @Test void testIrelandHighway() { process(SimpleFeature.create( - rectangle(0, 0.1), - Map.of("iso_a2", "IE"), - OpenMapTilesProfile.NATURAL_EARTH_SOURCE, - "ne_10m_admin_0_countries", - 0 + rectangle(0, 0.1), + Map.of("iso_a2", "IE"), + OpenMapTilesProfile.NATURAL_EARTH_SOURCE, + "ne_10m_admin_0_countries", + 0 )); // in IE assertFeatures(13, List.of(Map.of( - "_layer", "transportation", - "class", "motorway", - "oneway", 1, - "ramp", "", - "_minzoom", 4 + "_layer", "transportation", + "class", "motorway", + "oneway", 1, + "ramp", "", + "_minzoom", 4 ), Map.of( - "_layer", "transportation_name", - "class", "motorway", - "ref", "M18", - "ref_length", 3, - "network", "ie-motorway", - "_minzoom", 6 + "_layer", "transportation_name", + "class", "motorway", + "ref", "M18", + "ref_length", 3, + "network", "ie-motorway", + "_minzoom", 6 )), process(SimpleFeature.create( - newLineString(0, 0, 1, 1), - Map.of( - "highway", "motorway", - "oneway", "yes", - "ref", "M18" - ), - OpenMapTilesProfile.OSM_SOURCE, - null, - 0 + newLineString(0, 0, 1, 1), + Map.of( + "highway", "motorway", + "oneway", "yes", + "ref", "M18" + ), + OpenMapTilesProfile.OSM_SOURCE, + null, + 0 ))); // not in IE assertFeatures(13, List.of(Map.of( - "_layer", "transportation", - "class", "motorway", - "oneway", 1, - "ramp", "", - "_minzoom", 4 + "_layer", "transportation", + "class", "motorway", + "oneway", 1, + "ramp", "", + "_minzoom", 4 ), Map.of( - "_layer", "transportation_name", - "class", "motorway", - "ref", "M18", - "ref_length", 3, - "network", "road", - "_minzoom", 6 + "_layer", "transportation_name", + "class", "motorway", + "ref", "M18", + "ref_length", 3, + "network", "road", + "_minzoom", 6 )), process(SimpleFeature.create( - newLineString(1, 0, 0, 1), - Map.of( - "highway", "motorway", - "oneway", "yes", - "ref", "M18" - ), - OpenMapTilesProfile.OSM_SOURCE, - null, - 0 + newLineString(1, 0, 0, 1), + Map.of( + "highway", "motorway", + "oneway", "yes", + "ref", "M18" + ), + OpenMapTilesProfile.OSM_SOURCE, + null, + 0 ))); } @Test void testIrelandTrunk() { process(SimpleFeature.create( - rectangle(0, 0.1), - Map.of("iso_a2", "IE"), - OpenMapTilesProfile.NATURAL_EARTH_SOURCE, - "ne_10m_admin_0_countries", - 0 + rectangle(0, 0.1), + Map.of("iso_a2", "IE"), + OpenMapTilesProfile.NATURAL_EARTH_SOURCE, + "ne_10m_admin_0_countries", + 0 )); // in IE assertFeatures(13, List.of(Map.of( - "_layer", "transportation", - "class", "trunk", - "_minzoom", 5 + "_layer", "transportation", + "class", "trunk", + "_minzoom", 5 ), Map.of( - "_layer", "transportation_name", - "class", "trunk", - "ref", "N8", - "ref_length", 2, - "network", "ie-national", - "_minzoom", 8 + "_layer", "transportation_name", + "class", "trunk", + "ref", "N8", + "ref_length", 2, + "network", "ie-national", + "_minzoom", 8 )), process(SimpleFeature.create( - newLineString(0, 0, 1, 1), - Map.of( - "highway", "trunk", - "ref", "N8" - ), - OpenMapTilesProfile.OSM_SOURCE, - null, - 0 + newLineString(0, 0, 1, 1), + Map.of( + "highway", "trunk", + "ref", "N8" + ), + OpenMapTilesProfile.OSM_SOURCE, + null, + 0 ))); } @Test void testIrelandPrimary() { process(SimpleFeature.create( - rectangle(0, 0.1), - Map.of("iso_a2", "IE"), - OpenMapTilesProfile.NATURAL_EARTH_SOURCE, - "ne_10m_admin_0_countries", - 0 + rectangle(0, 0.1), + Map.of("iso_a2", "IE"), + OpenMapTilesProfile.NATURAL_EARTH_SOURCE, + "ne_10m_admin_0_countries", + 0 )); // in IE assertFeatures(13, List.of(Map.of( - "_layer", "transportation", - "class", "primary", - "_minzoom", 7 + "_layer", "transportation", + "class", "primary", + "_minzoom", 7 ), Map.of( - "_layer", "transportation_name", - "class", "primary", - "ref", "N59", - "ref_length", 3, - "network", "ie-national", - "_minzoom", 12 + "_layer", "transportation_name", + "class", "primary", + "ref", "N59", + "ref_length", 3, + "network", "ie-national", + "_minzoom", 12 )), process(SimpleFeature.create( - newLineString(0, 0, 1, 1), - Map.of( - "highway", "primary", - "ref", "N59" - ), - OpenMapTilesProfile.OSM_SOURCE, - null, - 0 + newLineString(0, 0, 1, 1), + Map.of( + "highway", "primary", + "ref", "N59" + ), + OpenMapTilesProfile.OSM_SOURCE, + null, + 0 ))); } @Test void testIrelandSecondary() { process(SimpleFeature.create( - rectangle(0, 0.1), - Map.of("iso_a2", "IE"), - OpenMapTilesProfile.NATURAL_EARTH_SOURCE, - "ne_10m_admin_0_countries", - 0 + rectangle(0, 0.1), + Map.of("iso_a2", "IE"), + OpenMapTilesProfile.NATURAL_EARTH_SOURCE, + "ne_10m_admin_0_countries", + 0 )); // in IE assertFeatures(13, List.of(Map.of( - "_layer", "transportation", - "class", "secondary", - "_minzoom", 9 + "_layer", "transportation", + "class", "secondary", + "_minzoom", 9 ), Map.of( - "_layer", "transportation_name", - "class", "secondary", - "ref", "R813", - "ref_length", 4, - "network", "ie-regional", - "_minzoom", 12 + "_layer", "transportation_name", + "class", "secondary", + "ref", "R813", + "ref_length", 4, + "network", "ie-regional", + "_minzoom", 12 )), process(SimpleFeature.create( - newLineString(0, 0, 1, 1), - Map.of( - "highway", "secondary", - "ref", "R813" - ), - OpenMapTilesProfile.OSM_SOURCE, - null, - 0 + newLineString(0, 0, 1, 1), + Map.of( + "highway", "secondary", + "ref", "R813" + ), + OpenMapTilesProfile.OSM_SOURCE, + null, + 0 ))); } From 8750c782ad404bd7ba616977e23b1d7fd89350e4 Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Fri, 14 Jul 2023 21:35:17 +0200 Subject: [PATCH 020/106] clazz calculation moved up so that minzoom can be set to 3 for only lakes (to match OMT PR 1475) --- .../org/openmaptiles/layers/WaterName.java | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/main/java/org/openmaptiles/layers/WaterName.java b/src/main/java/org/openmaptiles/layers/WaterName.java index 04c4f96b..9b7b1b73 100644 --- a/src/main/java/org/openmaptiles/layers/WaterName.java +++ b/src/main/java/org/openmaptiles/layers/WaterName.java @@ -188,8 +188,17 @@ public void process(Tables.OsmWaterPolygon element, FeatureCollector features) { try { Geometry centerlineGeometry = lakeCenterlines.get(element.source().id()); FeatureCollector.Feature feature; - int minzoom = 9; String place = element.place(); + int minzoom = 9; + String clazz; + if ("bay".equals(element.natural())) { + clazz = FieldValues.CLASS_BAY; + } else if ("sea".equals(place)) { + clazz = FieldValues.CLASS_SEA; + } else { + clazz = FieldValues.CLASS_LAKE; + minzoom = 3; + } if (centerlineGeometry != null) { // prefer lake centerline if it exists feature = features.geometry(LAYER_NAME, centerlineGeometry) @@ -204,14 +213,6 @@ public void process(Tables.OsmWaterPolygon element, FeatureCollector features) { minzoom = areaToMinZoom(area); } } - String clazz; - if ("bay".equals(element.natural())) { - clazz = FieldValues.CLASS_BAY; - } else if ("sea".equals(place)) { - clazz = FieldValues.CLASS_SEA; - } else { - clazz = FieldValues.CLASS_LAKE; - } feature .setAttr(Fields.CLASS, clazz) .setBufferPixels(BUFFER_SIZE) From 74181174327fa672dd661552c1531b23c208898a Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Fri, 14 Jul 2023 21:55:01 +0200 Subject: [PATCH 021/106] unit tests adjusted + extended to cover 'minzoom=3 fore lakes' change --- .../openmaptiles/layers/WaterNameTest.java | 38 ++++++++++++++++++- 1 file changed, 36 insertions(+), 2 deletions(-) diff --git a/src/test/java/org/openmaptiles/layers/WaterNameTest.java b/src/test/java/org/openmaptiles/layers/WaterNameTest.java index 77ec32e3..364e8e23 100644 --- a/src/test/java/org/openmaptiles/layers/WaterNameTest.java +++ b/src/test/java/org/openmaptiles/layers/WaterNameTest.java @@ -92,7 +92,7 @@ void testWaterNameLakeline() { "_layer", "water_name", "_type", "line", "_geom", new TestUtils.NormGeometry(GeoUtils.latLonToWorldCoords(newLineString(0, 0, 1, 1))), - "_minzoom", 9, + "_minzoom", 3, "_maxzoom", 14, "_minpixelsize", "waterway".length() * 6d )), process(SimpleFeature.create( @@ -142,7 +142,7 @@ void testWaterNameMultipleLakelines() { newLineString(0, 0, 1, 1), newLineString(2, 2, 3, 3) }))), - "_minzoom", 9, + "_minzoom", 3, "_maxzoom", 14, "_minpixelsize", "waterway".length() * 6d )), process(SimpleFeature.create( @@ -159,6 +159,40 @@ void testWaterNameMultipleLakelines() { ))); } + @Test + void testWaterNameBay() { + assertFeatures(11, List.of(), process(SimpleFeature.create( + newLineString(0, 0, 1, 1), + new HashMap<>(Map.of( + "OSM_ID", -10 + )), + OpenMapTilesProfile.LAKE_CENTERLINE_SOURCE, + null, + 0 + ))); + assertFeatures(10, List.of(Map.of( + "name", "bay", + "name:es", "bay es", + + "_layer", "water_name", + "_type", "line", + "_geom", new TestUtils.NormGeometry(GeoUtils.latLonToWorldCoords(newLineString(0, 0, 1, 1))), + "_minzoom", 9, + "_maxzoom", 14, + "_minpixelsize", "bay".length() * 6d + )), process(SimpleFeature.create( + GeoUtils.worldToLatLonCoords(rectangle(0, Math.sqrt(1))), + new HashMap<>(Map.of( + "name", "bay", + "name:es", "bay es", + "natural", "bay" + )), + OpenMapTilesProfile.OSM_SOURCE, + null, + 10 + ))); + } + @Test void testMarinePoint() { assertFeatures(11, List.of(), process(SimpleFeature.create( From bfce42eca5751e12a352688800972b337beb1208 Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Mon, 17 Jul 2023 18:07:39 +0200 Subject: [PATCH 022/106] fixed minor typo from previous PR --- src/test/java/org/openmaptiles/layers/WaterNameTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/org/openmaptiles/layers/WaterNameTest.java b/src/test/java/org/openmaptiles/layers/WaterNameTest.java index 364e8e23..fee396b1 100644 --- a/src/test/java/org/openmaptiles/layers/WaterNameTest.java +++ b/src/test/java/org/openmaptiles/layers/WaterNameTest.java @@ -277,7 +277,7 @@ private void createAreaForMinZoomTest(List testEntries, double side, @Test void testAreaToMinZoom() throws GeometryException { // threshold is 1/4 of tile area, hence ... - // ... side if 1/2 tile side: from pixels to world coord, for say Z14 ... + // ... side is 1/2 tile side: from pixels to world coord, for say Z14 ... //final double HALF_OF_TILE_SIDE = 128d / Math.pow(2d, 14d + 8d); // ... and then for some lower zoom: //double testAreaSide = HALF_OF_TILE_SIDE * Math.pow(2, 14 - zoom); From 213dd1436cfb75eaa3a6285819e474e80adc3ab2 Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Mon, 17 Jul 2023 22:20:44 +0200 Subject: [PATCH 023/106] render POIs for large universities at low zoom (to match OMT PR 1479) --- .../java/org/openmaptiles/layers/Poi.java | 34 +++++++++++- .../java/org/openmaptiles/layers/PoiTest.java | 53 +++++++++++++++++++ 2 files changed, 86 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/openmaptiles/layers/Poi.java b/src/main/java/org/openmaptiles/layers/Poi.java index b12b8307..81b4d2d3 100644 --- a/src/main/java/org/openmaptiles/layers/Poi.java +++ b/src/main/java/org/openmaptiles/layers/Poi.java @@ -48,11 +48,13 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE import com.onthegomap.planetiler.collection.Hppc; import com.onthegomap.planetiler.config.PlanetilerConfig; import com.onthegomap.planetiler.expression.MultiExpression; +import com.onthegomap.planetiler.geo.GeometryException; import com.onthegomap.planetiler.stats.Stats; import com.onthegomap.planetiler.util.Parse; import com.onthegomap.planetiler.util.Translations; import java.util.List; import java.util.Map; +import java.util.Set; import org.openmaptiles.generated.OpenMapTilesSchema; import org.openmaptiles.generated.Tables; import org.openmaptiles.util.OmtLanguageUtils; @@ -99,12 +101,17 @@ public class Poi implements entry(FieldValues.CLASS_CLOTHING_STORE, 700), entry(FieldValues.CLASS_BAR, 800) ); + private static final Set UNIVERSITY_POI_SUBCLASSES = Set.of("university", "college"); + private static final double LOG2 = Math.log(2); + private static final double SQRT10 = Math.sqrt(10); private final MultiExpression.Index classMapping; private final Translations translations; + private final Stats stats; public Poi(Translations translations, PlanetilerConfig config, Stats stats) { this.classMapping = FieldMappings.Class.index(); this.translations = translations; + this.stats = stats; } static int poiClassRank(String clazz) { @@ -125,6 +132,19 @@ private int minzoom(String subclass, String mappingKey) { return lowZoom ? 12 : 14; } + public static int uniAreaToMinZoom(double areaWorld) { + double oneSideWorld = Math.sqrt(areaWorld); + // full(-er) formula (along with comments) is in PoiTest.testUniAreaToMinZoom(), here is simplified reverse of that + double zoom = -(Math.log(oneSideWorld * SQRT10) / LOG2); + + // Say Z13.01 means bellow threshold, Z13.00 is exactly threshold, Z12.99 is over threshold, + // hence Z13.01 and Z13.00 will be rounded to Z14 and Z12.99 to Z13 (e.g. `floor() + 1`). + // And to accommodate for some precision errors (observed for Z9-Z11) we do also `- 0.1e-10`. + int result = (int) Math.floor(zoom - 0.1e-10) + 1; + + return Math.min(14, Math.max(10, result)); + } + @Override public void process(Tables.OsmPoiPoint element, FeatureCollector features) { // TODO handle uic_ref => agg_stop @@ -178,6 +198,18 @@ private testEntries, double side, int expectedZoom, String name) { + double area = Math.pow(side, 2); + var feature = polygonFeatureWithArea(area, Map.of( + "name", name, + "amenity", "university" + )); + testEntries.add(new TestEntry( + feature, + Math.min(14, Math.max(10, expectedZoom)) + )); + } + + @Test + void testUniAreaToMinZoom() throws GeometryException { + // threshold is 1/10 of tile area, hence ... + // ... side is 1/sqrt(10) tile side: from pixels to world coord, for say Z14 ... + //final double PORTION_OF_TILE_SIDE = (256d / Math.sqrt(10)) / Math.pow(2d, 14d + 8d); + // ... and then for some lower zoom: + //double testAreaSide = PORTION_OF_TILE_SIDE * Math.pow(2, 14 - zoom); + // all this then simplified to `testAreaSide` calculation bellow + + final double SQRT10 = Math.sqrt(10); + final List testEntries = new ArrayList<>(); + for (int zoom = 14; zoom >= 0; zoom--) { + double testAreaSide = Math.pow(2, -zoom) / SQRT10; + + // slightly bellow the threshold + createUniAreaForMinZoomTest(testEntries, testAreaSide * 0.999, zoom + 1, "uni-"); + // precisely at the threshold + createUniAreaForMinZoomTest(testEntries, testAreaSide, zoom, "uni="); + // slightly over the threshold + createUniAreaForMinZoomTest(testEntries, testAreaSide * 1.001, zoom, "uni+"); + } + + for (var entry : testEntries) { + assertFeatures(14, List.of(Map.of( + "_layer", "landuse", + "class", "university" + ), Map.of( + "_layer", "poi", + "_type", "point", + "_minzoom", entry.expectedZoom, + "_maxzoom", 14 + )), process(entry.feature)); + } + } } From 03f2b04ad817e2d1817073f0a4698fa0fdd616c6 Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Tue, 18 Jul 2023 09:45:27 +0200 Subject: [PATCH 024/106] clean-up, to make the diff/PR smaller --- src/main/java/org/openmaptiles/layers/WaterName.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/openmaptiles/layers/WaterName.java b/src/main/java/org/openmaptiles/layers/WaterName.java index 9b7b1b73..18621048 100644 --- a/src/main/java/org/openmaptiles/layers/WaterName.java +++ b/src/main/java/org/openmaptiles/layers/WaterName.java @@ -188,8 +188,8 @@ public void process(Tables.OsmWaterPolygon element, FeatureCollector features) { try { Geometry centerlineGeometry = lakeCenterlines.get(element.source().id()); FeatureCollector.Feature feature; - String place = element.place(); int minzoom = 9; + String place = element.place(); String clazz; if ("bay".equals(element.natural())) { clazz = FieldValues.CLASS_BAY; From e46c44c464f756ade05f70b8786c2fc732371b2f Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Mon, 25 Sep 2023 18:49:20 +0200 Subject: [PATCH 025/106] regenerate-openmaptiles.sh 5e9b7c475d53a5bd5ea394da361594d3f4ce2d66 (to match content of OMT PR 1485) --- .../generated/OpenMapTilesSchema.java | 49 +++++++------- .../org/openmaptiles/generated/Tables.java | 66 +++++++++++++------ 2 files changed, 70 insertions(+), 45 deletions(-) diff --git a/src/main/java/org/openmaptiles/generated/OpenMapTilesSchema.java b/src/main/java/org/openmaptiles/generated/OpenMapTilesSchema.java index ee3d08e1..bff7ff4b 100644 --- a/src/main/java/org/openmaptiles/generated/OpenMapTilesSchema.java +++ b/src/main/java/org/openmaptiles/generated/OpenMapTilesSchema.java @@ -49,8 +49,8 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE /** * All vector tile layer definitions, attributes, and allowed values generated from the OpenMapTiles - * vector tile schema edb42f2db3c2b0ec37045367720eed84d7bbd71f. + * "https://github.com/openmaptiles/openmaptiles/blob/5e9b7c475d53a5bd5ea394da361594d3f4ce2d66/openmaptiles.yaml">OpenMapTiles + * vector tile schema 5e9b7c475d53a5bd5ea394da361594d3f4ce2d66. */ @SuppressWarnings("unused") public class OpenMapTilesSchema { @@ -59,11 +59,11 @@ public class OpenMapTilesSchema { public static final String VERSION = "3.14.0"; public static final String ATTRIBUTION = "© OpenMapTiles © OpenStreetMap contributors"; - public static final List LANGUAGES = List.of("am", "ar", "az", "be", "bg", "br", "bs", "ca", "co", "cs", "cy", - "da", "de", "el", "en", "eo", "es", "et", "eu", "fi", "fr", "fy", "ga", "gd", "he", "hi", "hr", "hu", "hy", "id", - "is", "it", "ja", "ja_kana", "ja_rm", "ja-Latn", "ja-Hira", "ka", "kk", "kn", "ko", "ko-Latn", "ku", "la", "lb", - "lt", "lv", "mk", "mt", "ml", "nl", "no", "oc", "pl", "pt", "rm", "ro", "ru", "sk", "sl", "sq", "sr", "sr-Latn", - "sv", "ta", "te", "th", "tr", "uk", "zh"); + public static final List LANGUAGES = List.of("am", "ar", "az", "be", "bg", "bn", "br", "bs", "ca", "co", "cs", + "cy", "da", "de", "el", "en", "eo", "es", "et", "eu", "fa", "fi", "fr", "fy", "ga", "gd", "he", "hi", "hr", "hu", + "hy", "id", "is", "it", "ja", "ja_kana", "ja_rm", "ja-Latn", "ja-Hira", "ka", "kk", "kn", "ko", "ko-Latn", "ku", + "la", "lb", "lt", "lv", "mk", "mt", "ml", "nl", "no", "oc", "pa", "pnb", "pl", "pt", "rm", "ro", "ru", "sk", "sl", + "sq", "sr", "sr-Latn", "sv", "ta", "te", "th", "tr", "uk", "ur", "vi", "zh", "zh-Hant", "zh-Hans"); /** Returns a list of expected layer implementation instances from the {@code layers} package. */ public static List createInstances(Translations translations, PlanetilerConfig config, Stats stats) { @@ -96,7 +96,7 @@ public static List createInstances(Translations translations, PlanetilerC * boundaries show up. So you might not be able to use border styling for ocean water features. * * Generated from water.yaml + * "https://github.com/openmaptiles/openmaptiles/blob/5e9b7c475d53a5bd5ea394da361594d3f4ce2d66/layers/water/water.yaml">water.yaml */ public interface Water extends Layer { double BUFFER_SIZE = 4.0; @@ -195,7 +195,7 @@ final class FieldMappings { * field applied. Waterways do not have a subclass field. * * Generated from waterway.yaml + * "https://github.com/openmaptiles/openmaptiles/blob/5e9b7c475d53a5bd5ea394da361594d3f4ce2d66/layers/waterway/waterway.yaml">waterway.yaml */ public interface Waterway extends Layer { double BUFFER_SIZE = 4.0; @@ -280,7 +280,7 @@ final class FieldMappings { * layer is to style wood (class=wood) and grass (class=grass) areas. * * Generated from landcover.yaml + * "https://github.com/openmaptiles/openmaptiles/blob/5e9b7c475d53a5bd5ea394da361594d3f4ce2d66/layers/landcover/landcover.yaml">landcover.yaml */ public interface Landcover extends Layer { double BUFFER_SIZE = 4.0; @@ -437,7 +437,7 @@ final class FieldMappings { * residential (urban) areas and at higher zoom levels mostly OSM landuse tags. * * Generated from landuse.yaml + * "https://github.com/openmaptiles/openmaptiles/blob/5e9b7c475d53a5bd5ea394da361594d3f4ce2d66/layers/landuse/landuse.yaml">landuse.yaml */ public interface Landuse extends Layer { double BUFFER_SIZE = 4.0; @@ -533,7 +533,7 @@ final class FieldMappings { * Natural peaks * * Generated from mountain_peak.yaml + * "https://github.com/openmaptiles/openmaptiles/blob/5e9b7c475d53a5bd5ea394da361594d3f4ce2d66/layers/mountain_peak/mountain_peak.yaml">mountain_peak.yaml */ public interface MountainPeak extends Layer { double BUFFER_SIZE = 64.0; @@ -608,7 +608,7 @@ final class FieldMappings { * leisure=nature_reserve. * * Generated from park.yaml + * "https://github.com/openmaptiles/openmaptiles/blob/5e9b7c475d53a5bd5ea394da361594d3f4ce2d66/layers/park/park.yaml">park.yaml */ public interface Park extends Layer { double BUFFER_SIZE = 4.0; @@ -668,7 +668,7 @@ final class FieldMappings { * but for most styles it makes sense to just style admin_level=2 and admin_level=4. * * Generated from boundary.yaml + * "https://github.com/openmaptiles/openmaptiles/blob/5e9b7c475d53a5bd5ea394da361594d3f4ce2d66/layers/boundary/boundary.yaml">boundary.yaml */ public interface Boundary extends Layer { double BUFFER_SIZE = 4.0; @@ -769,7 +769,7 @@ final class FieldMappings { * in the aeroway layer. * * Generated from aeroway.yaml + * "https://github.com/openmaptiles/openmaptiles/blob/5e9b7c475d53a5bd5ea394da361594d3f4ce2d66/layers/aeroway/aeroway.yaml">aeroway.yaml */ public interface Aeroway extends Layer { double BUFFER_SIZE = 4.0; @@ -829,7 +829,7 @@ final class FieldMappings { * features like plazas. * * Generated from transportation.yaml + * "https://github.com/openmaptiles/openmaptiles/blob/5e9b7c475d53a5bd5ea394da361594d3f4ce2d66/layers/transportation/transportation.yaml">transportation.yaml */ public interface Transportation extends Layer { double BUFFER_SIZE = 4.0; @@ -1174,7 +1174,7 @@ final class FieldMappings { * location:underground are excluded. * * Generated from building.yaml + * "https://github.com/openmaptiles/openmaptiles/blob/5e9b7c475d53a5bd5ea394da361594d3f4ce2d66/layers/building/building.yaml">building.yaml */ public interface Building extends Layer { double BUFFER_SIZE = 4.0; @@ -1216,7 +1216,7 @@ final class FieldMappings { * from OSM water bodies. Only the most important lakes contain labels. * * Generated from water_name.yaml + * "https://github.com/openmaptiles/openmaptiles/blob/5e9b7c475d53a5bd5ea394da361594d3f4ce2d66/layers/water_name/water_name.yaml">water_name.yaml */ public interface WaterName extends Layer { double BUFFER_SIZE = 256.0; @@ -1286,7 +1286,7 @@ final class FieldMappings { * while for other roads you should use name. * * Generated from transportation_name.yaml + * "https://github.com/openmaptiles/openmaptiles/blob/5e9b7c475d53a5bd5ea394da361594d3f4ce2d66/layers/transportation_name/transportation_name.yaml">transportation_name.yaml */ public interface TransportationName extends Layer { double BUFFER_SIZE = 8.0; @@ -1516,7 +1516,7 @@ final class FieldMappings { * create a text hierarchy. * * Generated from place.yaml + * "https://github.com/openmaptiles/openmaptiles/blob/5e9b7c475d53a5bd5ea394da361594d3f4ce2d66/layers/place/place.yaml">place.yaml */ public interface Place extends Layer { double BUFFER_SIZE = 256.0; @@ -1623,10 +1623,11 @@ final class FieldMappings { /** * Everything in OpenStreetMap which contains a addr:housenumber tag useful for labelling housenumbers on * a map. This adds significant size to z14. For buildings the centroid of the building is used as - * housenumber. + * housenumber. Duplicates within a tile are dropped if they have the same street/block_number (records without name + * tag are prioritized for preservation). * * Generated from housenumber.yaml + * "https://github.com/openmaptiles/openmaptiles/blob/5e9b7c475d53a5bd5ea394da361594d3f4ce2d66/layers/housenumber/housenumber.yaml">housenumber.yaml */ public interface Housenumber extends Layer { double BUFFER_SIZE = 8.0; @@ -1656,7 +1657,7 @@ final class FieldMappings { * of OpenStreetMap tags. Mostly contains amenities, sport, shop and tourist POIs. * * Generated from poi.yaml + * "https://github.com/openmaptiles/openmaptiles/blob/5e9b7c475d53a5bd5ea394da361594d3f4ce2d66/layers/poi/poi.yaml">poi.yaml */ public interface Poi extends Layer { double BUFFER_SIZE = 64.0; @@ -1875,7 +1876,7 @@ final class FieldMappings { * Aerodrome labels * * Generated from aerodrome_label.yaml + * "https://github.com/openmaptiles/openmaptiles/blob/5e9b7c475d53a5bd5ea394da361594d3f4ce2d66/layers/aerodrome_label/aerodrome_label.yaml">aerodrome_label.yaml */ public interface AerodromeLabel extends Layer { double BUFFER_SIZE = 64.0; diff --git a/src/main/java/org/openmaptiles/generated/Tables.java b/src/main/java/org/openmaptiles/generated/Tables.java index e8cb1f9a..00e4bc50 100644 --- a/src/main/java/org/openmaptiles/generated/Tables.java +++ b/src/main/java/org/openmaptiles/generated/Tables.java @@ -51,7 +51,7 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE /** * OSM element parsers generated from the imposm3 table definitions * in the OpenMapTiles + * "https://github.com/openmaptiles/openmaptiles/blob/5e9b7c475d53a5bd5ea394da361594d3f4ce2d66/openmaptiles.yaml">OpenMapTiles * vector tile schema. * * These filter and parse the raw OSM key/value attribute pairs on tags into records with fields that match the columns @@ -319,25 +319,27 @@ public interface Handler { } } /** An OSM element that would appear in the {@code osm_highway_linestring} table generated by imposm3. */ - public record OsmHighwayLinestring(@Override String highway, @Override String construction, @Override String ref, - @Override String network, @Override int zOrder, @Override long layer, @Override long level, - @Override boolean indoor, @Override String name, @Override String nameEn, @Override String nameDe, - @Override String shortName, @Override boolean isTunnel, @Override boolean isBridge, @Override boolean isRamp, - @Override boolean isFord, @Override int isOneway, @Override boolean isArea, @Override String service, - @Override String access, @Override boolean toll, @Override String usage, @Override String publicTransport, - @Override String manMade, @Override String bicycle, @Override String foot, @Override String horse, - @Override String mtbScale, @Override String sacScale, @Override String surface, @Override boolean expressway, - @Override SourceFeature source) implements Row, WithHighway, WithConstruction, WithRef, WithNetwork, WithZOrder, - WithLayer, WithLevel, WithIndoor, WithName, WithNameEn, WithNameDe, WithShortName, WithIsTunnel, WithIsBridge, - WithIsRamp, WithIsFord, WithIsOneway, WithIsArea, WithService, WithAccess, WithToll, WithUsage, WithPublicTransport, + public record OsmHighwayLinestring(@Override String highway, @Override String construction, + @Override String tracktype, @Override String ref, @Override String network, @Override int zOrder, + @Override long layer, @Override long level, @Override boolean indoor, @Override String name, + @Override String nameEn, @Override String nameDe, @Override String shortName, @Override boolean isTunnel, + @Override boolean isBridge, @Override boolean isRamp, @Override boolean isFord, @Override int isOneway, + @Override boolean isArea, @Override String service, @Override String access, @Override boolean toll, + @Override String usage, @Override String publicTransport, @Override String manMade, @Override String bicycle, + @Override String foot, @Override String horse, @Override String mtbScale, @Override String sacScale, + @Override String surface, @Override boolean expressway, @Override SourceFeature source) + implements Row, WithHighway, WithConstruction, WithTracktype, WithRef, WithNetwork, WithZOrder, WithLayer, + WithLevel, WithIndoor, WithName, WithNameEn, WithNameDe, WithShortName, WithIsTunnel, WithIsBridge, WithIsRamp, + WithIsFord, WithIsOneway, WithIsArea, WithService, WithAccess, WithToll, WithUsage, WithPublicTransport, WithManMade, WithBicycle, WithFoot, WithHorse, WithMtbScale, WithSacScale, WithSurface, WithExpressway, WithSource { public OsmHighwayLinestring(SourceFeature source, String mappingKey) { - this(source.getString("highway"), source.getString("construction"), source.getString("ref"), - source.getString("network"), source.getWayZorder(), source.getLong("layer"), source.getLong("level"), - source.getBoolean("indoor"), source.getString("name"), source.getString("name:en"), source.getString("name:de"), - source.getString("short_name"), source.getBoolean("tunnel"), source.getBoolean("bridge"), - source.getBoolean("ramp"), source.getBoolean("ford"), source.getDirection("oneway"), source.getBoolean("area"), - source.getString("service"), source.getString("access"), source.getBoolean("toll"), source.getString("usage"), + this(source.getString("highway"), source.getString("construction"), source.getString("tracktype"), + source.getString("ref"), source.getString("network"), source.getWayZorder(), source.getLong("layer"), + source.getLong("level"), source.getBoolean("indoor"), source.getString("name"), source.getString("name:en"), + source.getString("name:de"), source.getString("short_name"), source.getBoolean("tunnel"), + source.getBoolean("bridge"), source.getBoolean("ramp"), source.getBoolean("ford"), + source.getDirection("oneway"), source.getBoolean("area"), source.getString("service"), + source.getString("access"), source.getBoolean("toll"), source.getString("usage"), source.getString("public_transport"), source.getString("man_made"), source.getString("bicycle"), source.getString("foot"), source.getString("horse"), source.getString("mtb:scale"), source.getString("sac_scale"), source.getString("surface"), source.getBoolean("expressway"), source); @@ -674,10 +676,12 @@ public interface Handler { } } /** An OSM element that would appear in the {@code osm_housenumber_point} table generated by imposm3. */ - public record OsmHousenumberPoint(@Override String housenumber, @Override SourceFeature source) - implements Row, WithHousenumber, WithSource { + public record OsmHousenumberPoint(@Override String housenumber, @Override String street, @Override String blockNumber, + @Override String hasName, @Override SourceFeature source) + implements Row, WithHousenumber, WithStreet, WithBlockNumber, WithHasName, WithSource { public OsmHousenumberPoint(SourceFeature source, String mappingKey) { - this(source.getString("addr:housenumber"), source); + this(source.getString("addr:housenumber"), source.getString("addr:street"), source.getString("addr:block_number"), + source.getString("name"), source); } /** Imposm3 "mapping" to filter OSM elements that should appear in this "table". */ @@ -890,6 +894,11 @@ public interface WithBicycle { String bicycle(); } + /** Rows with a String blockNumber attribute. */ + public interface WithBlockNumber { + String blockNumber(); + } + /** Rows with a String boundary attribute. */ public interface WithBoundary { String boundary(); @@ -970,6 +979,11 @@ public interface WithFunicular { String funicular(); } + /** Rows with a String hasName attribute. */ + public interface WithHasName { + String hasName(); + } + /** Rows with a String height attribute. */ public interface WithHeight { String height(); @@ -1230,6 +1244,11 @@ public interface WithStation { String station(); } + /** Rows with a String street attribute. */ + public interface WithStreet { + String street(); + } + /** Rows with a String subclass attribute. */ public interface WithSubclass { String subclass(); @@ -1250,6 +1269,11 @@ public interface WithTourism { String tourism(); } + /** Rows with a String tracktype attribute. */ + public interface WithTracktype { + String tracktype(); + } + /** Rows with a String uicRef attribute. */ public interface WithUicRef { String uicRef(); From 258db805918deece934ff566a79662424dfa6fda Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Mon, 25 Sep 2023 19:04:44 +0200 Subject: [PATCH 026/106] handle 'grade1' and 'tracktype' as per OMT PR 1485 --- .../openmaptiles/layers/Transportation.java | 4 ++-- .../layers/TransportationTest.java | 24 +++++++++++++++++++ 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/openmaptiles/layers/Transportation.java b/src/main/java/org/openmaptiles/layers/Transportation.java index ea3e90fd..64d1cd6e 100644 --- a/src/main/java/org/openmaptiles/layers/Transportation.java +++ b/src/main/java/org/openmaptiles/layers/Transportation.java @@ -134,7 +134,7 @@ public class Transportation implements ); private static final Set SURFACE_PAVED_VALUES = Set.of( "paved", "asphalt", "cobblestone", "concrete", "concrete:lanes", "concrete:plates", "metal", - "paving_stones", "sett", "unhewn_cobblestone", "wood" + "paving_stones", "sett", "unhewn_cobblestone", "wood", "grade1" ); private static final Set ACCESS_NO_VALUES = Set.of( "private", "no" @@ -479,7 +479,7 @@ public void process(Tables.OsmHighwayLinestring element, FeatureCollector featur // z12+ .setAttrWithMinzoom(Fields.SERVICE, service, 12) .setAttrWithMinzoom(Fields.ONEWAY, nullIfInt(element.isOneway(), 0), 12) - .setAttrWithMinzoom(Fields.SURFACE, surface(element.surface()), 12) + .setAttrWithMinzoom(Fields.SURFACE, surface(coalesce(element.surface(), element.tracktype())), 12) .setMinPixelSize(0) // merge during post-processing, then limit by size .setSortKey(element.zOrder()) .setMinZoom(minzoom); diff --git a/src/test/java/org/openmaptiles/layers/TransportationTest.java b/src/test/java/org/openmaptiles/layers/TransportationTest.java index 108925b8..943c10aa 100644 --- a/src/test/java/org/openmaptiles/layers/TransportationTest.java +++ b/src/test/java/org/openmaptiles/layers/TransportationTest.java @@ -1928,4 +1928,28 @@ void testIssue86() { "service", "driveway" )))); } + + @Test + void testGrade1SurfacePath() { + assertFeatures(14, List.of(Map.of( + "_layer", "transportation", + "class", "track", + "surface", "paved" + )), process(lineFeature(Map.of( + "surface", "grade1", + "highway", "track" + )))); + } + + @Test + void testGrade1TracktypePath() { + assertFeatures(14, List.of(Map.of( + "_layer", "transportation", + "class", "track", + "surface", "paved" + )), process(lineFeature(Map.of( + "tracktype", "grade1", + "highway", "track" + )))); + } } From d3429c46372ea7cb2cc6f6e7200eef2a4e189c2e Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Tue, 26 Sep 2023 15:23:45 +0200 Subject: [PATCH 027/106] added implementation of agg_stop It is based on OMT PR 1480 (which contains latest the fix) and the rest of older code (which was not worling properly until the fix). --- .../java/org/openmaptiles/layers/Poi.java | 141 +++++++++++++++- .../java/org/openmaptiles/layers/PoiTest.java | 158 ++++++++++++++++++ 2 files changed, 294 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/openmaptiles/layers/Poi.java b/src/main/java/org/openmaptiles/layers/Poi.java index 81b4d2d3..6885de18 100644 --- a/src/main/java/org/openmaptiles/layers/Poi.java +++ b/src/main/java/org/openmaptiles/layers/Poi.java @@ -48,16 +48,27 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE import com.onthegomap.planetiler.collection.Hppc; import com.onthegomap.planetiler.config.PlanetilerConfig; import com.onthegomap.planetiler.expression.MultiExpression; +import com.onthegomap.planetiler.geo.GeoUtils; import com.onthegomap.planetiler.geo.GeometryException; +import com.onthegomap.planetiler.reader.SimpleFeature; import com.onthegomap.planetiler.stats.Stats; import com.onthegomap.planetiler.util.Parse; import com.onthegomap.planetiler.util.Translations; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.function.Consumer; +import org.locationtech.jts.geom.Geometry; +import org.locationtech.jts.geom.Point; +import org.openmaptiles.OpenMapTilesProfile; import org.openmaptiles.generated.OpenMapTilesSchema; import org.openmaptiles.generated.Tables; import org.openmaptiles.util.OmtLanguageUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * Defines the logic for generating map elements for things like shops, parks, and schools in the {@code poi} layer from @@ -70,13 +81,15 @@ public class Poi implements OpenMapTilesSchema.Poi, Tables.OsmPoiPoint.Handler, Tables.OsmPoiPolygon.Handler, - ForwardingProfile.FeaturePostProcessor { + ForwardingProfile.FeaturePostProcessor, + OpenMapTilesProfile.FinishHandler { /* * process() creates the raw POI feature from OSM elements and postProcess() * assigns the feature rank from order in the tile at render-time. */ + private static final Logger LOGGER = LoggerFactory.getLogger(Poi.class); private static final Map CLASS_RANKS = Map.ofEntries( entry(FieldValues.CLASS_HOSPITAL, 20), entry(FieldValues.CLASS_RAILWAY, 40), @@ -102,11 +115,20 @@ public class Poi implements entry(FieldValues.CLASS_BAR, 800) ); private static final Set UNIVERSITY_POI_SUBCLASSES = Set.of("university", "college"); + private static final Map AGG_STOP_SUBCLASS_ORDER = Map.ofEntries( + entry("subway", 0), + entry("tram_stop", 1), + entry("bus_station", 2), + entry("bus_stop", 3) + ); + private static final Comparator BY_SUBCLASS = Comparator + .comparingInt(s -> AGG_STOP_SUBCLASS_ORDER.get(s.subclass())); private static final double LOG2 = Math.log(2); private static final double SQRT10 = Math.sqrt(10); private final MultiExpression.Index classMapping; private final Translations translations; private final Stats stats; + private final Map> aggStops = new HashMap<>(); public Poi(Translations translations, PlanetilerConfig config, Stats stats) { this.classMapping = FieldMappings.Class.index(); @@ -145,19 +167,127 @@ public static int uniAreaToMinZoom(double areaWorld) { return Math.min(14, Math.max(10, result)); } + @Override + public void release() { + aggStops.clear(); + } + @Override public void process(Tables.OsmPoiPoint element, FeatureCollector features) { - // TODO handle uic_ref => agg_stop - setupPoiFeature(element, features.point(LAYER_NAME)); + if (element.uicRef() != null && AGG_STOP_SUBCLASS_ORDER.containsKey(element.subclass())) { + // multiple threads may update this concurrently + synchronized (this) { + aggStops.computeIfAbsent(element.uicRef(), key -> new ArrayList<>()).add(element); + } + } else { + setupPoiFeature(element, features.point(LAYER_NAME), null); + } + } + + private void processAggStop(Tables.OsmPoiPoint element, FeatureCollector.Factory featureCollectors, + Consumer emit, Integer aggStop) { + try { + var features = featureCollectors.get(SimpleFeature.fromWorldGeometry(element.source().worldGeometry())); + setupPoiFeature(element, features.point(LAYER_NAME), aggStop); + for (var feature : features) { + emit.accept(feature); + } + } catch (GeometryException e) { + e.log(stats, "agg_stop_geometry_2", + "Error getting geometry for the stop " + element.source().id() + " (agg_stop)"); + } + } + + /** + * We've put aside some stops for {@code agg_stop} processing and we do that processing here. + *

    + * The main point is to group together stops with same {@code uid_ref} and then order them first based on subclass + * (see {@code AGG_STOP_ORDER}) and then based on distance from centroid (calculated from all the stops). The first + * one gets {@code agg_stop=1}, the rest will be "normal" (e.g. no {@code agg_stop} attribute). + *

    + * ref: poi_stop_agg.sql + */ + @Override + public void finish(String sourceName, FeatureCollector.Factory featureCollectors, + Consumer emit) { + if (OpenMapTilesProfile.OSM_SOURCE.equals(sourceName)) { + var timer = stats.startStage("agg_stop"); + LOGGER.info("Processing {} agg_stop sets", aggStops.size()); + + // possible TODO: run in parallel? + for (var aggStopSet : aggStops.values()) { + if (aggStopSet.size() == 1) { + processAggStop(aggStopSet.get(0), featureCollectors, emit, 1); + continue; + } + try { + // find first based on subclass + var firstSubclass = aggStopSet.stream().sorted(BY_SUBCLASS).toArray(Tables.OsmPoiPoint[]::new)[0].subclass(); + var topAggStops = + aggStopSet.stream().filter(s -> firstSubclass.equals(s.subclass())).toArray(Tables.OsmPoiPoint[]::new); + if (topAggStops.length <= 2) { + // one found => straightforward: flag it and emit + // two found => both will be same distance from centroid: pick first one, flag it and emit + processAggStop(topAggStops[0], featureCollectors, emit, 1); + // and emit the rest + aggStopSet.stream() + .filter(s -> !topAggStops[0].equals(s)) + .forEach(s -> processAggStop(s, featureCollectors, emit, null)); + continue; + } + + // easy cases handled, now we need also centroid + List aggStopPoints = new ArrayList<>(aggStopSet.size()); + for (var aggStop : aggStopSet) { + aggStopPoints.add(aggStop.source().worldGeometry().getCentroid()); + } + var aggStopCentroid = GeoUtils.combinePoints(aggStopPoints).getCentroid(); + + // find nearest + double minDistance = Double.MAX_VALUE; + Tables.OsmPoiPoint nearest = null; + for (var aggStop : topAggStops) { + double distance = aggStopCentroid.distance(aggStop.source().worldGeometry()); + if (distance < minDistance) { + minDistance = distance; + nearest = aggStop; + } + } + + // emit nearest + if (nearest != null) { + processAggStop(nearest, featureCollectors, emit, 1); + } else { + stats.dataError("agg_stop_nearest"); + LOGGER.warn("Failed to find nearest stop for agg_stop UIC ref. {}", aggStopSet.get(0).uicRef()); + } + + // emit the rest + final var alreadyDone = nearest; + aggStopSet.stream() + .filter(s -> !s.equals(alreadyDone)) + .forEach(s -> processAggStop(s, featureCollectors, emit, null)); + } catch (GeometryException e) { + e.log(stats, "agg_stop_geometry_1", + "Error getting geometry for some of the stops with UIC ref. " + aggStopSet.get(0).uicRef() + " (agg_stop)"); + // we're not able to calculate agg_stop, so simply dump them as-is + aggStopSet + .forEach(s -> processAggStop(s, featureCollectors, emit, null)); + } + } + + timer.stop(); + } } @Override public void process(Tables.OsmPoiPolygon element, FeatureCollector features) { - setupPoiFeature(element, features.centroidIfConvex(LAYER_NAME)); + setupPoiFeature(element, features.centroidIfConvex(LAYER_NAME), null); } private void setupPoiFeature( - T element, FeatureCollector.Feature output) { + T element, FeatureCollector.Feature output, Integer aggStop) { String rawSubclass = element.subclass(); if ("station".equals(rawSubclass) && "subway".equals(element.station())) { rawSubclass = "subway"; @@ -216,6 +346,7 @@ private testAggStops(List sourceFeatures) { + sourceFeatures.forEach(this::process); + + List features = new ArrayList<>(); + profile.finish(OpenMapTilesProfile.OSM_SOURCE, featureCollectorFactory, features::add); + + return features; + } + + @Test + void testAggStopJustOne() { + var result = testAggStops(List.of(pointFeature(Map.of( + "highway", "bus_stop", + "name", "station", + "uic_ref", "1" + )))); + assertFeatures(14, List.of(Map.of( + "_layer", "poi", + "class", "bus", + "subclass", "bus_stop", + "agg_stop", 1, + "_minzoom", 14 + )), result); + } + + @Test + void testAggStopTwoWithSameSubclass() { + var result = testAggStops(List.of( + pointFeature(Map.of( + "railway", "tram_stop", + "name", "station 1", + "uic_ref", "1" + )), + pointFeature(Map.of( + "railway", "tram_stop", + "name", "station 2", + "uic_ref", "1" + )) + )); + assertFeatures(14, List.of( + Map.of( + "_layer", "poi", + "name", "station 1", + "class", "railway", + "subclass", "tram_stop", + "agg_stop", 1, + "_minzoom", 14 + ), + Map.of( + "_layer", "poi", + "name", "station 2", + "class", "railway", + "subclass", "tram_stop", + "agg_stop", "", + "_minzoom", 14 + ) + ), result); + } + + @Test + void testAggStopThreeWithMixedSubclass() { + var result = testAggStops(List.of( + pointFeature(Map.of( + "highway", "bus_stop", + "name", "station 1", + "uic_ref", "1" + )), + pointFeature(Map.of( + "highway", "bus_stop", + "name", "station 2", + "uic_ref", "1" + )), + pointFeature(Map.of( + "railway", "tram_stop", + "name", "station 3", + "uic_ref", "1" + )) + )); + assertFeatures(14, List.of( + Map.of( + "_layer", "poi", + "name", "station 3", + "class", "railway", + "subclass", "tram_stop", + "agg_stop", 1, + "_minzoom", 14 + ), + Map.of( + "_layer", "poi", + "name", "station 1", + "class", "bus", + "subclass", "bus_stop", + "agg_stop", "", + "_minzoom", 14 + ), + Map.of( + "_layer", "poi", + "name", "station 2", + "class", "bus", + "subclass", "bus_stop", + "agg_stop", "", + "_minzoom", 14 + ) + ), result); + } + + @Test + void testAggStopThreeWithSameSubclass() { + var result = testAggStops(List.of( + SimpleFeature.create(newPoint(0, 0), Map.of( + "highway", "bus_stop", + "name", "station 1", + "uic_ref", "1" + ), OpenMapTilesProfile.OSM_SOURCE, null, 0), + SimpleFeature.create(newPoint(1, 0), Map.of( + "highway", "bus_stop", + "name", "station 2", + "uic_ref", "1" + ), OpenMapTilesProfile.OSM_SOURCE, null, 1), + SimpleFeature.create(newPoint(2, 0), Map.of( + "highway", "bus_stop", + "name", "station 3", + "uic_ref", "1" + ), OpenMapTilesProfile.OSM_SOURCE, null, 2) + )); + assertFeatures(14, List.of( + Map.of( + "_layer", "poi", + "name", "station 2", + "class", "bus", + "subclass", "bus_stop", + "agg_stop", 1, + "_minzoom", 14 + ), + Map.of( + "_layer", "poi", + "name", "station 1", + "class", "bus", + "subclass", "bus_stop", + "agg_stop", "", + "_minzoom", 14 + ), + Map.of( + "_layer", "poi", + "name", "station 3", + "class", "bus", + "subclass", "bus_stop", + "agg_stop", "", + "_minzoom", 14 + ) + ), result); + } + @ParameterizedTest @ValueSource(booleans = {false, true}) void testPlaceOfWorshipFromReligionTag(boolean area) { From 6a099de138c1741d24ec063971e3a8603846dcc4 Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Mon, 2 Oct 2023 20:30:03 +0200 Subject: [PATCH 028/106] clean-up: mvn spotless:apply --- .../java/org/openmaptiles/layers/Poi.java | 1 - .../layers/TransportationTest.java | 20 +++++++++---------- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/src/main/java/org/openmaptiles/layers/Poi.java b/src/main/java/org/openmaptiles/layers/Poi.java index 6885de18..e8eb03d4 100644 --- a/src/main/java/org/openmaptiles/layers/Poi.java +++ b/src/main/java/org/openmaptiles/layers/Poi.java @@ -61,7 +61,6 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE import java.util.Map; import java.util.Set; import java.util.function.Consumer; -import org.locationtech.jts.geom.Geometry; import org.locationtech.jts.geom.Point; import org.openmaptiles.OpenMapTilesProfile; import org.openmaptiles.generated.OpenMapTilesSchema; diff --git a/src/test/java/org/openmaptiles/layers/TransportationTest.java b/src/test/java/org/openmaptiles/layers/TransportationTest.java index 943c10aa..4496c85b 100644 --- a/src/test/java/org/openmaptiles/layers/TransportationTest.java +++ b/src/test/java/org/openmaptiles/layers/TransportationTest.java @@ -1932,24 +1932,24 @@ void testIssue86() { @Test void testGrade1SurfacePath() { assertFeatures(14, List.of(Map.of( - "_layer", "transportation", - "class", "track", - "surface", "paved" + "_layer", "transportation", + "class", "track", + "surface", "paved" )), process(lineFeature(Map.of( - "surface", "grade1", - "highway", "track" + "surface", "grade1", + "highway", "track" )))); } @Test void testGrade1TracktypePath() { assertFeatures(14, List.of(Map.of( - "_layer", "transportation", - "class", "track", - "surface", "paved" + "_layer", "transportation", + "class", "track", + "surface", "paved" )), process(lineFeature(Map.of( - "tracktype", "grade1", - "highway", "track" + "tracktype", "grade1", + "highway", "track" )))); } } From afbd863de1bb73557fbb1f16698eda10eea545aa Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Mon, 2 Oct 2023 20:52:52 +0200 Subject: [PATCH 029/106] Long ferries (as per OMT PR 1486) --- src/main/java/org/openmaptiles/layers/Transportation.java | 2 +- src/test/java/org/openmaptiles/layers/TransportationTest.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/openmaptiles/layers/Transportation.java b/src/main/java/org/openmaptiles/layers/Transportation.java index ad65bc22..74b05cb7 100644 --- a/src/main/java/org/openmaptiles/layers/Transportation.java +++ b/src/main/java/org/openmaptiles/layers/Transportation.java @@ -609,7 +609,7 @@ public void process(Tables.OsmShipwayLinestring element, FeatureCollector featur .setAttr(Fields.LAYER, nullIfLong(element.layer(), 0)) .setSortKey(element.zOrder()) .setMinPixelSize(0) // merge during post-processing, then limit by size - .setMinZoom(11); + .setMinZoom(4); } @Override diff --git a/src/test/java/org/openmaptiles/layers/TransportationTest.java b/src/test/java/org/openmaptiles/layers/TransportationTest.java index 4496c85b..bec2aeb9 100644 --- a/src/test/java/org/openmaptiles/layers/TransportationTest.java +++ b/src/test/java/org/openmaptiles/layers/TransportationTest.java @@ -1747,7 +1747,7 @@ void testFerry() { "_layer", "transportation", "class", "ferry", - "_minzoom", 11, + "_minzoom", 4, "_maxzoom", 14, "_type", "line" ), Map.of( From 19a75a0d888b6a869ac5d89d725393fe3a44df94 Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Mon, 2 Oct 2023 21:17:15 +0200 Subject: [PATCH 030/106] regenerate-openmaptiles.sh b3d67ed5b327c9059aeea0b3304772c6b4c8c7e9 (to match content of OMT PR 1489) --- .../generated/OpenMapTilesSchema.java | 38 +++++++++---------- .../org/openmaptiles/generated/Tables.java | 7 ++-- 2 files changed, 22 insertions(+), 23 deletions(-) diff --git a/src/main/java/org/openmaptiles/generated/OpenMapTilesSchema.java b/src/main/java/org/openmaptiles/generated/OpenMapTilesSchema.java index bff7ff4b..d9b47b88 100644 --- a/src/main/java/org/openmaptiles/generated/OpenMapTilesSchema.java +++ b/src/main/java/org/openmaptiles/generated/OpenMapTilesSchema.java @@ -49,8 +49,8 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE /** * All vector tile layer definitions, attributes, and allowed values generated from the OpenMapTiles - * vector tile schema 5e9b7c475d53a5bd5ea394da361594d3f4ce2d66. + * "https://github.com/openmaptiles/openmaptiles/blob/b3d67ed5b327c9059aeea0b3304772c6b4c8c7e9/openmaptiles.yaml">OpenMapTiles + * vector tile schema b3d67ed5b327c9059aeea0b3304772c6b4c8c7e9. */ @SuppressWarnings("unused") public class OpenMapTilesSchema { @@ -96,7 +96,7 @@ public static List createInstances(Translations translations, PlanetilerC * boundaries show up. So you might not be able to use border styling for ocean water features. * * Generated from water.yaml + * "https://github.com/openmaptiles/openmaptiles/blob/b3d67ed5b327c9059aeea0b3304772c6b4c8c7e9/layers/water/water.yaml">water.yaml */ public interface Water extends Layer { double BUFFER_SIZE = 4.0; @@ -195,7 +195,7 @@ final class FieldMappings { * field applied. Waterways do not have a subclass field. * * Generated from waterway.yaml + * "https://github.com/openmaptiles/openmaptiles/blob/b3d67ed5b327c9059aeea0b3304772c6b4c8c7e9/layers/waterway/waterway.yaml">waterway.yaml */ public interface Waterway extends Layer { double BUFFER_SIZE = 4.0; @@ -280,7 +280,7 @@ final class FieldMappings { * layer is to style wood (class=wood) and grass (class=grass) areas. * * Generated from landcover.yaml + * "https://github.com/openmaptiles/openmaptiles/blob/b3d67ed5b327c9059aeea0b3304772c6b4c8c7e9/layers/landcover/landcover.yaml">landcover.yaml */ public interface Landcover extends Layer { double BUFFER_SIZE = 4.0; @@ -437,7 +437,7 @@ final class FieldMappings { * residential (urban) areas and at higher zoom levels mostly OSM landuse tags. * * Generated from landuse.yaml + * "https://github.com/openmaptiles/openmaptiles/blob/b3d67ed5b327c9059aeea0b3304772c6b4c8c7e9/layers/landuse/landuse.yaml">landuse.yaml */ public interface Landuse extends Layer { double BUFFER_SIZE = 4.0; @@ -533,7 +533,7 @@ final class FieldMappings { * Natural peaks * * Generated from mountain_peak.yaml + * "https://github.com/openmaptiles/openmaptiles/blob/b3d67ed5b327c9059aeea0b3304772c6b4c8c7e9/layers/mountain_peak/mountain_peak.yaml">mountain_peak.yaml */ public interface MountainPeak extends Layer { double BUFFER_SIZE = 64.0; @@ -608,7 +608,7 @@ final class FieldMappings { * leisure=nature_reserve. * * Generated from park.yaml + * "https://github.com/openmaptiles/openmaptiles/blob/b3d67ed5b327c9059aeea0b3304772c6b4c8c7e9/layers/park/park.yaml">park.yaml */ public interface Park extends Layer { double BUFFER_SIZE = 4.0; @@ -630,7 +630,7 @@ final class Fields { * nature_reserve is the class of protection_title=Nature Reserve and * leisure=nature_reserve. The class for other * protection_title values is - * similarly assigned. + * similarly assigned. The class for boundary=aboriginal_lands is aboriginal_lands. */ public static final String CLASS = "class"; /** @@ -668,7 +668,7 @@ final class FieldMappings { * but for most styles it makes sense to just style admin_level=2 and admin_level=4. * * Generated from boundary.yaml + * "https://github.com/openmaptiles/openmaptiles/blob/b3d67ed5b327c9059aeea0b3304772c6b4c8c7e9/layers/boundary/boundary.yaml">boundary.yaml */ public interface Boundary extends Layer { double BUFFER_SIZE = 4.0; @@ -769,7 +769,7 @@ final class FieldMappings { * in the aeroway layer. * * Generated from aeroway.yaml + * "https://github.com/openmaptiles/openmaptiles/blob/b3d67ed5b327c9059aeea0b3304772c6b4c8c7e9/layers/aeroway/aeroway.yaml">aeroway.yaml */ public interface Aeroway extends Layer { double BUFFER_SIZE = 4.0; @@ -829,7 +829,7 @@ final class FieldMappings { * features like plazas. * * Generated from transportation.yaml + * "https://github.com/openmaptiles/openmaptiles/blob/b3d67ed5b327c9059aeea0b3304772c6b4c8c7e9/layers/transportation/transportation.yaml">transportation.yaml */ public interface Transportation extends Layer { double BUFFER_SIZE = 4.0; @@ -1174,7 +1174,7 @@ final class FieldMappings { * location:underground are excluded. * * Generated from building.yaml + * "https://github.com/openmaptiles/openmaptiles/blob/b3d67ed5b327c9059aeea0b3304772c6b4c8c7e9/layers/building/building.yaml">building.yaml */ public interface Building extends Layer { double BUFFER_SIZE = 4.0; @@ -1216,7 +1216,7 @@ final class FieldMappings { * from OSM water bodies. Only the most important lakes contain labels. * * Generated from water_name.yaml + * "https://github.com/openmaptiles/openmaptiles/blob/b3d67ed5b327c9059aeea0b3304772c6b4c8c7e9/layers/water_name/water_name.yaml">water_name.yaml */ public interface WaterName extends Layer { double BUFFER_SIZE = 256.0; @@ -1286,7 +1286,7 @@ final class FieldMappings { * while for other roads you should use name. * * Generated from transportation_name.yaml + * "https://github.com/openmaptiles/openmaptiles/blob/b3d67ed5b327c9059aeea0b3304772c6b4c8c7e9/layers/transportation_name/transportation_name.yaml">transportation_name.yaml */ public interface TransportationName extends Layer { double BUFFER_SIZE = 8.0; @@ -1516,7 +1516,7 @@ final class FieldMappings { * create a text hierarchy. * * Generated from place.yaml + * "https://github.com/openmaptiles/openmaptiles/blob/b3d67ed5b327c9059aeea0b3304772c6b4c8c7e9/layers/place/place.yaml">place.yaml */ public interface Place extends Layer { double BUFFER_SIZE = 256.0; @@ -1627,7 +1627,7 @@ final class FieldMappings { * tag are prioritized for preservation). * * Generated from housenumber.yaml + * "https://github.com/openmaptiles/openmaptiles/blob/b3d67ed5b327c9059aeea0b3304772c6b4c8c7e9/layers/housenumber/housenumber.yaml">housenumber.yaml */ public interface Housenumber extends Layer { double BUFFER_SIZE = 8.0; @@ -1657,7 +1657,7 @@ final class FieldMappings { * of OpenStreetMap tags. Mostly contains amenities, sport, shop and tourist POIs. * * Generated from poi.yaml + * "https://github.com/openmaptiles/openmaptiles/blob/b3d67ed5b327c9059aeea0b3304772c6b4c8c7e9/layers/poi/poi.yaml">poi.yaml */ public interface Poi extends Layer { double BUFFER_SIZE = 64.0; @@ -1876,7 +1876,7 @@ final class FieldMappings { * Aerodrome labels * * Generated from aerodrome_label.yaml + * "https://github.com/openmaptiles/openmaptiles/blob/b3d67ed5b327c9059aeea0b3304772c6b4c8c7e9/layers/aerodrome_label/aerodrome_label.yaml">aerodrome_label.yaml */ public interface AerodromeLabel extends Layer { double BUFFER_SIZE = 64.0; diff --git a/src/main/java/org/openmaptiles/generated/Tables.java b/src/main/java/org/openmaptiles/generated/Tables.java index 00e4bc50..d1b8456c 100644 --- a/src/main/java/org/openmaptiles/generated/Tables.java +++ b/src/main/java/org/openmaptiles/generated/Tables.java @@ -51,7 +51,7 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE /** * OSM element parsers generated from the imposm3 table definitions * in the OpenMapTiles + * "https://github.com/openmaptiles/openmaptiles/blob/b3d67ed5b327c9059aeea0b3304772c6b4c8c7e9/openmaptiles.yaml">OpenMapTiles * vector tile schema. * * These filter and parse the raw OSM key/value attribute pairs on tags into records with fields that match the columns @@ -249,9 +249,8 @@ public OsmParkPolygon(SourceFeature source, String mappingKey) { } /** Imposm3 "mapping" to filter OSM elements that should appear in this "table". */ - public static final Expression MAPPING = - and(or(matchAny("leisure", "nature_reserve"), matchAny("boundary", "national_park", "protected_area")), - matchType("polygon")); + public static final Expression MAPPING = and(or(matchAny("leisure", "nature_reserve"), + matchAny("boundary", "national_park", "protected_area", "aboriginal_lands")), matchType("polygon")); /** * Interface for layer implementations to extend to subscribe to OSM elements filtered and parsed as From 899a0c5718734d5a746cdd84df2e1308614954c0 Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Mon, 2 Oct 2023 22:22:39 +0200 Subject: [PATCH 031/106] Add aboriginal lands (as per OMT PR 1489) --- .../java/org/openmaptiles/layers/Park.java | 21 +++++++++------- .../org/openmaptiles/layers/ParkTest.java | 24 +++++++++++++++++++ 2 files changed, 37 insertions(+), 8 deletions(-) diff --git a/src/main/java/org/openmaptiles/layers/Park.java b/src/main/java/org/openmaptiles/layers/Park.java index 6bcc6531..20ed1562 100644 --- a/src/main/java/org/openmaptiles/layers/Park.java +++ b/src/main/java/org/openmaptiles/layers/Park.java @@ -91,15 +91,20 @@ public Park(Translations translations, PlanetilerConfig config, Stats stats) { @Override public void process(Tables.OsmParkPolygon element, FeatureCollector features) { - String protectionTitle = element.protectionTitle(); - if (protectionTitle != null) { - protectionTitle = protectionTitle.replace(' ', '_').toLowerCase(Locale.ROOT); + String clazz; + if ("aboriginal_lands".equals(element.boundary())) { + clazz = "aboriginal_lands"; + } else { + String protectionTitle = element.protectionTitle(); + if (protectionTitle != null) { + protectionTitle = protectionTitle.replace(' ', '_').toLowerCase(Locale.ROOT); + } + clazz = coalesce( + nullIfEmpty(protectionTitle), + nullIfEmpty(element.boundary()), + nullIfEmpty(element.leisure()) + ); } - String clazz = coalesce( - nullIfEmpty(protectionTitle), - nullIfEmpty(element.boundary()), - nullIfEmpty(element.leisure()) - ); // park shape var outline = features.polygon(LAYER_NAME).setBufferPixels(BUFFER_SIZE) diff --git a/src/test/java/org/openmaptiles/layers/ParkTest.java b/src/test/java/org/openmaptiles/layers/ParkTest.java index a393b211..63ee4ba6 100644 --- a/src/test/java/org/openmaptiles/layers/ParkTest.java +++ b/src/test/java/org/openmaptiles/layers/ParkTest.java @@ -45,6 +45,30 @@ void testNationalPark() { )))); } + @Test + void testAbotiginalLand() { + assertFeatures(13, List.of(Map.of( + "_layer", "park", + "_type", "polygon", + "class", "aboriginal_lands", + "name", "Hualapai Tribe", + "_minpixelsize", 2d, + "_minzoom", 4, + "_maxzoom", 14 + ), Map.of( + "_layer", "park", + "_type", "point", + "class", "aboriginal_lands", + "name", "Hualapai Tribe", + "_minzoom", 5, + "_maxzoom", 14 + )), process(polygonFeature(Map.of( + "boundary", "aboriginal_lands", + "name", "Hualapai Tribe", + "protection_title", "National Park" + )))); + } + @Test void testSmallerPark() { double z11area = Math.pow((GeoUtils.metersToPixelAtEquator(0, Math.sqrt(70_000)) / 256d), 2) * Math.pow(2, 20 - 11); From 7043e8c931daf68e2fae701d30b05462365e3d5c Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Wed, 4 Oct 2023 21:23:13 +0200 Subject: [PATCH 032/106] handle duplicate route relations (to match OMT PR 1501) --- .../layers/TransportationName.java | 13 +++--- .../layers/TransportationTest.java | 45 +++++++++++++++++++ 2 files changed, 53 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/openmaptiles/layers/TransportationName.java b/src/main/java/org/openmaptiles/layers/TransportationName.java index 86b5c657..35f1c968 100644 --- a/src/main/java/org/openmaptiles/layers/TransportationName.java +++ b/src/main/java/org/openmaptiles/layers/TransportationName.java @@ -273,11 +273,14 @@ public void process(Tables.OsmHighwayLinestring element, FeatureCollector featur .setSortKey(element.zOrder()) .setMinZoom(minzoom); - // populate route_1, route_2, ... tags - for (int i = 0; i < Math.min(CONCURRENT_ROUTE_KEYS.size(), relations.size()); i++) { - Transportation.RouteRelation routeRelation = relations.get(i); - feature.setAttr(CONCURRENT_ROUTE_KEYS.get(i), routeRelation.network() == null ? null : - routeRelation.network() + "=" + coalesce(routeRelation.ref(), "")); + // populate route_1, route_2, ... tags; take only unique ones + Object[] routes = relations.stream() + .map(r -> r.network() == null ? null : r.network() + "=" + coalesce(r.ref(), "")) + .distinct() + .limit(CONCURRENT_ROUTE_KEYS.size()) + .toArray(); + for (int i = 0; i < routes.length; i++) { + feature.setAttr(CONCURRENT_ROUTE_KEYS.get(i), routes[i]); } if (brunnel) { diff --git a/src/test/java/org/openmaptiles/layers/TransportationTest.java b/src/test/java/org/openmaptiles/layers/TransportationTest.java index 943c10aa..25a02326 100644 --- a/src/test/java/org/openmaptiles/layers/TransportationTest.java +++ b/src/test/java/org/openmaptiles/layers/TransportationTest.java @@ -341,6 +341,51 @@ void testInterstateMotorway() { )), features); } + @Test + void testDuplicateRoute() { + var rel1 = new OsmElement.Relation(1); + rel1.setTag("type", "route"); + rel1.setTag("route", "road"); + rel1.setTag("network", "US:OK"); + rel1.setTag("ref", "104"); + rel1.setTag("direction", "north"); + var rel2 = new OsmElement.Relation(2); + rel2.setTag("type", "route"); + rel2.setTag("route", "road"); + rel2.setTag("network", "US:OK"); + rel2.setTag("ref", "104"); + rel2.setTag("direction", "south"); + + FeatureCollector features = process(lineFeatureWithRelation( + Stream.concat( + profile.preprocessOsmRelation(rel2).stream(), + profile.preprocessOsmRelation(rel1).stream() + ).toList(), + Map.of( + "highway", "trunk", + "ref", "US 23;SR 104", + "lanes", 5, + "maxspeed", "55 mph", + "expressway", "no" + ))); + + assertFeatures(13, List.of(mapOf( + "_layer", "transportation", + "class", "trunk", + "network", "us-state", + "_minzoom", 5 + ), Map.of( + "_layer", "transportation_name", + "class", "trunk", + "ref", "104", + "ref_length", 3, + "network", "us-state", + "route_1", "US:OK=104", + "route_2", "", + "_minzoom", 8 + )), features); + } + @Test void testRouteWithoutNetworkType() { var rel1 = new OsmElement.Relation(1); From b597ecf678f5c4a4cab9e1d59ad136ec262396e6 Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Fri, 6 Oct 2023 10:44:43 +0200 Subject: [PATCH 033/106] regenerate-openmaptiles.sh master, to match several OMT PRs which adjusted only YML --- .../generated/OpenMapTilesSchema.java | 83 ++++++++++--------- .../org/openmaptiles/generated/Tables.java | 22 +++-- 2 files changed, 55 insertions(+), 50 deletions(-) diff --git a/src/main/java/org/openmaptiles/generated/OpenMapTilesSchema.java b/src/main/java/org/openmaptiles/generated/OpenMapTilesSchema.java index bff7ff4b..55ec4e54 100644 --- a/src/main/java/org/openmaptiles/generated/OpenMapTilesSchema.java +++ b/src/main/java/org/openmaptiles/generated/OpenMapTilesSchema.java @@ -48,9 +48,9 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE import org.openmaptiles.Layer; /** - * All vector tile layer definitions, attributes, and allowed values generated from the OpenMapTiles - * vector tile schema 5e9b7c475d53a5bd5ea394da361594d3f4ce2d66. + * All vector tile layer definitions, attributes, and allowed values generated from the + * OpenMapTiles vector tile schema + * master. */ @SuppressWarnings("unused") public class OpenMapTilesSchema { @@ -95,8 +95,8 @@ public static List createInstances(Translations translations, PlanetilerC * polygons to improve rendering performance. This however can lead to less rendering options in clients since these * boundaries show up. So you might not be able to use border styling for ocean water features. * - * Generated from water.yaml + * Generated from + * water.yaml */ public interface Water extends Layer { double BUFFER_SIZE = 4.0; @@ -194,8 +194,8 @@ final class FieldMappings { * there is also canal generated, starting z13 there is no generalization according to class * field applied. Waterways do not have a subclass field. * - * Generated from waterway.yaml + * Generated from + * waterway.yaml */ public interface Waterway extends Layer { double BUFFER_SIZE = 4.0; @@ -280,7 +280,7 @@ final class FieldMappings { * layer is to style wood (class=wood) and grass (class=grass) areas. * * Generated from landcover.yaml + * "https://github.com/openmaptiles/openmaptiles/blob/master/layers/landcover/landcover.yaml">landcover.yaml */ public interface Landcover extends Layer { double BUFFER_SIZE = 4.0; @@ -436,8 +436,8 @@ final class FieldMappings { * Landuse is used to describe use of land by humans. At lower zoom levels this is from Natural Earth data for * residential (urban) areas and at higher zoom levels mostly OSM landuse tags. * - * Generated from landuse.yaml + * Generated from + * landuse.yaml */ public interface Landuse extends Layer { double BUFFER_SIZE = 4.0; @@ -533,7 +533,7 @@ final class FieldMappings { * Natural peaks * * Generated from mountain_peak.yaml + * "https://github.com/openmaptiles/openmaptiles/blob/master/layers/mountain_peak/mountain_peak.yaml">mountain_peak.yaml */ public interface MountainPeak extends Layer { double BUFFER_SIZE = 64.0; @@ -601,14 +601,18 @@ final class FieldMappings { } } /** - * The park layer contains parks from OpenStreetMap tagged with - * boundary=national_park, + * The park layer in OpenMapTiles contains natural and protected areas from OpenStreetMap, such as parks tagged with + * boundary=national_park, * boundary=protected_area, or - * leisure=nature_reserve. + * "https://wiki.openstreetmap.org/wiki/Tag:boundary%3Dprotected_area">boundary=protected_area, or + * leisure=nature_reserve. + * This layer also includes boundaries for indigenous lands tagged with boundary=aboriginal_lands. + * Indigenous boundaries are not parks, but they are included in this layer for technical reasons related to data + * processing. These boundaries represent areas with special legal and administrative status for indigenous peoples. * - * Generated from park.yaml + * Generated from + * park.yaml */ public interface Park extends Layer { double BUFFER_SIZE = 4.0; @@ -622,15 +626,16 @@ default String name() { /** Attribute names for map elements in the park layer. */ final class Fields { /** - * Use the class to differentiate between different parks. The class for - * boundary=protected_area parks is the lower-case of the + * Use the class to differentiate between different kinds of features in the parks + * layer, for example between parks and non-parks. The class for boundary=protected_area parks is the + * lower-case of the * protection_title value with * blanks replaced by _. national_park is the class of * protection_title=National Park and boundary=national_park. * nature_reserve is the class of protection_title=Nature Reserve and * leisure=nature_reserve. The class for other * protection_title values is - * similarly assigned. + * similarly assigned. The class for boundary=aboriginal_lands is aboriginal_lands. */ public static final String CLASS = "class"; /** @@ -667,8 +672,8 @@ final class FieldMappings { * admin_level * but for most styles it makes sense to just style admin_level=2 and admin_level=4. * - * Generated from boundary.yaml + * Generated from + * boundary.yaml */ public interface Boundary extends Layer { double BUFFER_SIZE = 4.0; @@ -768,8 +773,8 @@ final class FieldMappings { * buildings are contained in the building layer but all other airport related polygons can be found * in the aeroway layer. * - * Generated from aeroway.yaml + * Generated from + * aeroway.yaml */ public interface Aeroway extends Layer { double BUFFER_SIZE = 4.0; @@ -829,7 +834,7 @@ final class FieldMappings { * features like plazas. * * Generated from transportation.yaml + * "https://github.com/openmaptiles/openmaptiles/blob/master/layers/transportation/transportation.yaml">transportation.yaml */ public interface Transportation extends Layer { double BUFFER_SIZE = 4.0; @@ -1173,8 +1178,8 @@ final class FieldMappings { * (building= ). Only buildings with tag * location:underground are excluded. * - * Generated from building.yaml + * Generated from + * building.yaml */ public interface Building extends Layer { double BUFFER_SIZE = 4.0; @@ -1216,7 +1221,7 @@ final class FieldMappings { * from OSM water bodies. Only the most important lakes contain labels. * * Generated from water_name.yaml + * "https://github.com/openmaptiles/openmaptiles/blob/master/layers/water_name/water_name.yaml">water_name.yaml */ public interface WaterName extends Layer { double BUFFER_SIZE = 256.0; @@ -1286,7 +1291,7 @@ final class FieldMappings { * while for other roads you should use name. * * Generated from transportation_name.yaml + * "https://github.com/openmaptiles/openmaptiles/blob/master/layers/transportation_name/transportation_name.yaml">transportation_name.yaml */ public interface TransportationName extends Layer { double BUFFER_SIZE = 8.0; @@ -1515,8 +1520,8 @@ final class FieldMappings { * of the more important layers to create a beautiful map. We suggest you use different font styles and sizes to * create a text hierarchy. * - * Generated from place.yaml + * Generated from + * place.yaml */ public interface Place extends Layer { double BUFFER_SIZE = 256.0; @@ -1627,7 +1632,7 @@ final class FieldMappings { * tag are prioritized for preservation). * * Generated from housenumber.yaml + * "https://github.com/openmaptiles/openmaptiles/blob/master/layers/housenumber/housenumber.yaml">housenumber.yaml */ public interface Housenumber extends Layer { double BUFFER_SIZE = 8.0; @@ -1640,7 +1645,10 @@ default String name() { /** Attribute names for map elements in the housenumber layer. */ final class Fields { - /** Value of the addr:housenumber tag. */ + /** + * Value of the addr:housenumber tag. If + * there are multiple values separated by semi-colons, the first and last value separated by a dash. + */ public static final String HOUSENUMBER = "housenumber"; } /** Attribute values for map elements in the housenumber layer. */ @@ -1656,8 +1664,7 @@ final class FieldMappings { * Points of interests containing a of a variety * of OpenStreetMap tags. Mostly contains amenities, sport, shop and tourist POIs. * - * Generated from poi.yaml + * Generated from poi.yaml */ public interface Poi extends Layer { double BUFFER_SIZE = 64.0; @@ -1828,8 +1835,8 @@ final class FieldMappings { "erotic", "electronics", "fabric", "florist", "frozen_food", "furniture", "video_games", "video", "general", "gift", "hardware", "hearing_aids", "hifi", "ice_cream", "interior_decoration", "jewelry", "kiosk", "locksmith", "lamps", "mall", "massage", "motorcycle", "mobile_phone", "newsagent", "optician", "outdoor", - "perfumery", "perfume", "pet", "photo", "second_hand", "shoes", "sports", "stationery", "tailor", "tattoo", - "ticket", "tobacco", "toys", "travel_agency", "watches", "weapons", "wholesale")), + "paint", "perfumery", "perfume", "pet", "photo", "second_hand", "shoes", "sports", "stationery", "tailor", + "tattoo", "ticket", "tobacco", "toys", "travel_agency", "watches", "weapons", "wholesale")), MultiExpression.entry("town_hall", matchAny("subclass", "townhall", "public_building", "courthouse", "community_centre")), MultiExpression.entry("golf", matchAny("subclass", "golf", "golf_course", "miniature_golf")), @@ -1876,7 +1883,7 @@ final class FieldMappings { * Aerodrome labels * * Generated from aerodrome_label.yaml + * "https://github.com/openmaptiles/openmaptiles/blob/master/layers/aerodrome_label/aerodrome_label.yaml">aerodrome_label.yaml */ public interface AerodromeLabel extends Layer { double BUFFER_SIZE = 64.0; diff --git a/src/main/java/org/openmaptiles/generated/Tables.java b/src/main/java/org/openmaptiles/generated/Tables.java index 00e4bc50..5effebba 100644 --- a/src/main/java/org/openmaptiles/generated/Tables.java +++ b/src/main/java/org/openmaptiles/generated/Tables.java @@ -50,9 +50,8 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE /** * OSM element parsers generated from the imposm3 table definitions - * in the OpenMapTiles - * vector tile schema. + * in the OpenMapTiles vector tile + * schema. * * These filter and parse the raw OSM key/value attribute pairs on tags into records with fields that match the columns * in the tables that imposm3 would generate. Layer implementations can "subscribe" to elements from each "table" but @@ -249,9 +248,8 @@ public OsmParkPolygon(SourceFeature source, String mappingKey) { } /** Imposm3 "mapping" to filter OSM elements that should appear in this "table". */ - public static final Expression MAPPING = - and(or(matchAny("leisure", "nature_reserve"), matchAny("boundary", "national_park", "protected_area")), - matchType("polygon")); + public static final Expression MAPPING = and(or(matchAny("leisure", "nature_reserve"), + matchAny("boundary", "national_park", "protected_area", "aboriginal_lands")), matchType("polygon")); /** * Interface for layer implementations to extend to subscribe to OSM elements filtered and parsed as @@ -740,9 +738,9 @@ public OsmPoiPoint(SourceFeature source, String mappingKey) { "erotic", "fabric", "florist", "frozen_food", "furniture", "garden_centre", "general", "gift", "greengrocer", "hairdresser", "hardware", "hearing_aids", "hifi", "ice_cream", "interior_decoration", "jewelry", "kiosk", "lamps", "laundry", "locksmith", "mall", "massage", "mobile_phone", "motorcycle", "music", "musical_instrument", - "newsagent", "optician", "outdoor", "perfume", "perfumery", "pet", "photo", "second_hand", "shoes", "sports", - "stationery", "supermarket", "tailor", "tattoo", "ticket", "tobacco", "toys", "travel_agency", "video", - "video_games", "watches", "weapons", "wholesale", "wine"), + "newsagent", "optician", "outdoor", "paint", "perfume", "perfumery", "pet", "photo", "second_hand", "shoes", + "sports", "stationery", "supermarket", "tailor", "tattoo", "ticket", "tobacco", "toys", "travel_agency", + "video", "video_games", "watches", "weapons", "wholesale", "wine"), matchAny("sport", "american_football", "archery", "athletics", "australian_football", "badminton", "baseball", "basketball", "beachvolleyball", "billiards", "bmx", "boules", "bowls", "boxing", "canadian_football", "canoe", "chess", "climbing", "climbing_adventure", "cricket", "cricket_nets", "croquet", "curling", "cycling", @@ -810,9 +808,9 @@ public OsmPoiPolygon(SourceFeature source, String mappingKey) { "erotic", "fabric", "florist", "frozen_food", "furniture", "garden_centre", "general", "gift", "greengrocer", "hairdresser", "hardware", "hearing_aids", "hifi", "ice_cream", "interior_decoration", "jewelry", "kiosk", "lamps", "laundry", "locksmith", "mall", "massage", "mobile_phone", "motorcycle", "music", "musical_instrument", - "newsagent", "optician", "outdoor", "perfume", "perfumery", "pet", "photo", "second_hand", "shoes", "sports", - "stationery", "supermarket", "tailor", "tattoo", "ticket", "tobacco", "toys", "travel_agency", "video", - "video_games", "watches", "weapons", "wholesale", "wine"), + "newsagent", "optician", "outdoor", "paint", "perfume", "perfumery", "pet", "photo", "second_hand", "shoes", + "sports", "stationery", "supermarket", "tailor", "tattoo", "ticket", "tobacco", "toys", "travel_agency", + "video", "video_games", "watches", "weapons", "wholesale", "wine"), matchAny("sport", "american_football", "archery", "athletics", "australian_football", "badminton", "baseball", "basketball", "beachvolleyball", "billiards", "bmx", "boules", "bowls", "boxing", "canadian_football", "canoe", "chess", "climbing", "climbing_adventure", "cricket", "cricket_nets", "croquet", "curling", "cycling", From 604f22b5620618587827dfce4fe644ca2d76c205 Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Mon, 9 Oct 2023 17:20:32 +0200 Subject: [PATCH 034/106] URLs in comments adjusted to match OMT PR 1560 --- src/main/java/org/openmaptiles/OpenMapTilesProfile.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/openmaptiles/OpenMapTilesProfile.java b/src/main/java/org/openmaptiles/OpenMapTilesProfile.java index e6d20e7d..d5d992fc 100644 --- a/src/main/java/org/openmaptiles/OpenMapTilesProfile.java +++ b/src/main/java/org/openmaptiles/OpenMapTilesProfile.java @@ -220,12 +220,12 @@ public interface NaturalEarthProcessor { /** * Layers should implement this interface to subscribe to elements from - * OSM lake centerlines source. + * OSM lake centerlines source. */ public interface LakeCenterlineProcessor { /** - * Process an element from the OSM lake centerlines + * Process an element from the OSM lake centerlines * source * * @see Profile#processFeature(SourceFeature, FeatureCollector) From ed7e357d7d8cc4e44ceb41462f7b6423a65d8e5e Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Mon, 9 Oct 2023 20:36:42 +0200 Subject: [PATCH 035/106] Convert separated addresses to dashed addresses --- .../org/openmaptiles/layers/Housenumber.java | 60 ++++++++++++++++++- .../openmaptiles/layers/HousenumberTest.java | 41 +++++++++++++ 2 files changed, 99 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/openmaptiles/layers/Housenumber.java b/src/main/java/org/openmaptiles/layers/Housenumber.java index 66338597..cebfb8a3 100644 --- a/src/main/java/org/openmaptiles/layers/Housenumber.java +++ b/src/main/java/org/openmaptiles/layers/Housenumber.java @@ -43,9 +43,15 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE import com.onthegomap.planetiler.geo.GeometryException; import com.onthegomap.planetiler.stats.Stats; import com.onthegomap.planetiler.util.Translations; +import java.util.Arrays; import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; import org.openmaptiles.generated.OpenMapTilesSchema; import org.openmaptiles.generated.Tables; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * Defines the logic for generating map elements in the {@code housenumber} layer from source features. @@ -59,13 +65,63 @@ public class Housenumber implements Tables.OsmHousenumberPoint.Handler, ForwardingProfile.FeaturePostProcessor { - public Housenumber(Translations translations, PlanetilerConfig config, Stats stats) {} + private static final Logger LOGGER = LoggerFactory.getLogger(Housenumber.class); + private static final Character OSM_SEPARATOR = ';'; + private static final String DISPLAY_SEPARATOR = "–"; + private static final Pattern NO_CONVERSION_PATTERN = Pattern.compile("[^0-9;]"); + private final Stats stats; + + public Housenumber(Translations translations, PlanetilerConfig config, Stats stats) { + this.stats = stats; + } + + private static String displayHousenumberNonumeric(String[] numbers) { + return numbers[0] + .concat(DISPLAY_SEPARATOR) + .concat(numbers[numbers.length - 1]); + } + + protected static String displayHousenumber(String housenumber) { + if (housenumber.indexOf(OSM_SEPARATOR) < 0) { + return housenumber; + } + + String[] numbers = Arrays.stream(housenumber.split(OSM_SEPARATOR.toString())) + .filter(n -> !n.trim().isEmpty()) + .toArray(String[]::new); + if (numbers.length <= 0) { + // not much to do with strange/invalid entries like "3;" or ";" etc. + return housenumber; + } + + Matcher matcher = NO_CONVERSION_PATTERN.matcher(housenumber); + if (matcher.find()) { + return displayHousenumberNonumeric(numbers); + } + + // numeric display house number + var statistics = Arrays.stream(numbers) + .collect(Collectors.summarizingInt(Integer::parseUnsignedInt)); + return String.valueOf(statistics.getMin()) + .concat(DISPLAY_SEPARATOR) + .concat(String.valueOf(statistics.getMax())); + } @Override public void process(Tables.OsmHousenumberPoint element, FeatureCollector features) { + String housenumber; + try { + housenumber = displayHousenumber(element.housenumber()); + } catch (NumberFormatException e) { + // should not be happening (thanks to NO_CONVERSION_PATTERN) but ... + stats.dataError("housenumber_range"); + LOGGER.warn("Failed to convert housenumber range: {}", element.housenumber()); + housenumber = element.housenumber(); + } + features.centroidIfConvex(LAYER_NAME) .setBufferPixels(BUFFER_SIZE) - .setAttr(Fields.HOUSENUMBER, element.housenumber()) + .setAttr(Fields.HOUSENUMBER, housenumber) .setMinZoom(14); } diff --git a/src/test/java/org/openmaptiles/layers/HousenumberTest.java b/src/test/java/org/openmaptiles/layers/HousenumberTest.java index 6ead5e96..bacbcd7f 100644 --- a/src/test/java/org/openmaptiles/layers/HousenumberTest.java +++ b/src/test/java/org/openmaptiles/layers/HousenumberTest.java @@ -1,5 +1,7 @@ package org.openmaptiles.layers; +import static org.junit.jupiter.api.Assertions.assertEquals; + import java.util.List; import java.util.Map; import org.junit.jupiter.api.Test; @@ -27,4 +29,43 @@ void testHousenumber() { "addr:housenumber", "10" )))); } + + @Test + void testDisplayHousenumberNonRange() { + final String HOUSENUMBER = "1"; + assertEquals(HOUSENUMBER, Housenumber.displayHousenumber(HOUSENUMBER)); + } + + @Test + void testDisplayHousenumberNumericRange() { + final String HOUSENUMBER = "1;1a;2;2/b;20;3"; + assertEquals("1–3", Housenumber.displayHousenumber(HOUSENUMBER)); + } + + @Test + void testDisplayHousenumberNumericRangeBroken() { + final String HOUSENUMBER = "1;1a;2;2/b;20;3;"; + assertEquals("1–3", Housenumber.displayHousenumber(HOUSENUMBER)); + } + + @Test + void testDisplayHousenumberNonnumericRange() { + final String HOUSENUMBER = "1;2;20;3"; + assertEquals("1–20", Housenumber.displayHousenumber(HOUSENUMBER)); + } + + @Test + void testDisplayHousenumberNonnumericRangeBroken() { + final String HOUSENUMBER = "1;2;20;3;"; + assertEquals("1–20", Housenumber.displayHousenumber(HOUSENUMBER)); + } + + @Test + void testDisplayHousenumberExtraBroken() { + final String HOUSENUMBER_1 = ";"; + assertEquals(HOUSENUMBER_1, Housenumber.displayHousenumber(HOUSENUMBER_1)); + + final String HOUSENUMBER_2 = ";;"; + assertEquals(HOUSENUMBER_2, Housenumber.displayHousenumber(HOUSENUMBER_2)); + } } From 3470989a10010e5335730dd48fe2ba4eebe299dd Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Tue, 10 Oct 2023 16:03:24 +0200 Subject: [PATCH 036/106] add brunnel (and layer) attributes only for certain zoomlevels, depending on feature size (matching OMT PR 1579) --- .../openmaptiles/layers/Transportation.java | 38 +++++++-- .../layers/TransportationName.java | 9 +- .../layers/AbstractLayerTest.java | 10 +++ .../layers/TransportationTest.java | 82 ++++++++++++++++--- 4 files changed, 123 insertions(+), 16 deletions(-) diff --git a/src/main/java/org/openmaptiles/layers/Transportation.java b/src/main/java/org/openmaptiles/layers/Transportation.java index ad65bc22..eba3aa31 100644 --- a/src/main/java/org/openmaptiles/layers/Transportation.java +++ b/src/main/java/org/openmaptiles/layers/Transportation.java @@ -163,11 +163,13 @@ public class Transportation implements .thenComparingInt(r -> r.ref().length()) .thenComparing(RouteRelation::ref); private static final Set ONEWAY_VALUES = Set.of(-1, 1); + private final Map MINZOOMS; + private static final int BRUNNEL_FALLBACK_MINZOOM = 12; private static final String LIMIT_MERGE_TAG = "__limit_merge"; + private static final double LOG2 = Math.log(2); private final AtomicBoolean loggedNoGb = new AtomicBoolean(false); private final AtomicBoolean loggedNoIreland = new AtomicBoolean(false); private final boolean z13Paths; - private final Map MINZOOMS; private final Stats stats; private final PlanetilerConfig config; private PreparedGeometry greatBritain = null; @@ -449,6 +451,14 @@ public void process(Tables.OsmHighwayLinestring element, FeatureCollector featur } int minzoom = getMinzoom(element, highwayClass); + String brunnelValue = brunnel(element.isBridge(), element.isTunnel(), element.isFord()); + int brunnelMinzoom; + if (brunnelValue != null) { + brunnelMinzoom = getBrunnelMinzoom(element); + } else { + brunnelMinzoom = minzoom; + } + if (minzoom > config.maxzoom()) { return; } @@ -462,13 +472,11 @@ public void process(Tables.OsmHighwayLinestring element, FeatureCollector featur .setAttr(Fields.CLASS, highwayClass) .setAttr(Fields.SUBCLASS, highwaySubclass(highwayClass, element.publicTransport(), highway)) .setAttr(Fields.NETWORK, networkType != null ? networkType.name : null) - // TODO: including brunnel at low zooms leads to some large 300-400+kb z4-7 tiles, instead - // we should only set brunnel if the line is above a certain length - .setAttr(Fields.BRUNNEL, brunnel(element.isBridge(), element.isTunnel(), element.isFord())) + .setAttrWithMinzoom(Fields.BRUNNEL, brunnelValue, brunnelMinzoom) // z8+ .setAttrWithMinzoom(Fields.EXPRESSWAY, element.expressway() && !"motorway".equals(highway) ? 1 : null, 8) // z9+ - .setAttrWithMinzoom(Fields.LAYER, nullIfLong(element.layer(), 0), 9) + .setAttrWithMinzoom(Fields.LAYER, nullIfLong(element.layer(), 0), brunnelValue == null ? 9 : brunnelMinzoom) .setAttrWithMinzoom(Fields.BICYCLE, nullIfEmpty(element.bicycle()), 9) .setAttrWithMinzoom(Fields.FOOT, nullIfEmpty(element.foot()), 9) .setAttrWithMinzoom(Fields.HORSE, nullIfEmpty(element.horse()), 9) @@ -534,6 +542,26 @@ int getMinzoom(Tables.OsmHighwayLinestring element, String highwayClass) { return minzoom; } + int getBrunnelMinzoom(Tables.OsmHighwayLinestring element) { + // full(-er) formula (along with comments) is in TranportationTest.testGetBrunnelMinzoom(), here is simplified reverse of that + double zoom; + try { + zoom = -(Math.log(element.source().length()) / LOG2) - 6; + } catch (GeometryException e) { + e.log(stats, "omt_brunnel_minzoom", + "Unable to calculate brunnel minzoom for " + element.source().id()); + // brunnel is optional (depends on feature size) for Z9-Z11, it is always present for Z12+, hence 12 as fallback + return BRUNNEL_FALLBACK_MINZOOM; + } + + // Say Z13.01 means bellow threshold, Z13.00 is exactly threshold, Z12.99 is over threshold, + // hence Z13.01 and Z13.00 will be rounded to Z14 and Z12.99 to Z13 (e.g. `floor() + 1`). + // And to accommodate for some precision errors (observed for Z9-Z11) we do also `- 0.1e-10`. + int result = (int) Math.floor(zoom - 0.1e-10) + 1; + + return Math.min(config.maxzoom(), Math.max(9, result)); + } + private boolean isPierPolygon(Tables.OsmHighwayLinestring element) { if ("pier".equals(element.manMade())) { try { diff --git a/src/main/java/org/openmaptiles/layers/TransportationName.java b/src/main/java/org/openmaptiles/layers/TransportationName.java index 86b5c657..1080576e 100644 --- a/src/main/java/org/openmaptiles/layers/TransportationName.java +++ b/src/main/java/org/openmaptiles/layers/TransportationName.java @@ -281,7 +281,14 @@ public void process(Tables.OsmHighwayLinestring element, FeatureCollector featur } if (brunnel) { - feature.setAttr(Fields.BRUNNEL, brunnel(element.isBridge(), element.isTunnel(), element.isFord())); + String brunnelValue = brunnel(element.isBridge(), element.isTunnel(), element.isFord()); + int brunnelMinzoom; + if (brunnelValue != null) { + brunnelMinzoom = transportation.getBrunnelMinzoom(element); + } else { + brunnelMinzoom = minzoom; + } + feature.setAttrWithMinzoom(Fields.BRUNNEL, brunnelValue, brunnelMinzoom); } /* diff --git a/src/test/java/org/openmaptiles/layers/AbstractLayerTest.java b/src/test/java/org/openmaptiles/layers/AbstractLayerTest.java index 05e94d99..e3f649e3 100644 --- a/src/test/java/org/openmaptiles/layers/AbstractLayerTest.java +++ b/src/test/java/org/openmaptiles/layers/AbstractLayerTest.java @@ -137,6 +137,16 @@ SourceFeature lineFeature(Map props) { ); } + SourceFeature lineFeatureWithLength(double length, Map props) { + return SimpleFeature.create( + GeoUtils.worldToLatLonCoords(newLineString(0, 0, 0, length)), + new HashMap<>(props), + OpenMapTilesProfile.OSM_SOURCE, + null, + 0 + ); + } + SourceFeature closedWayFeature(Map props) { return SimpleFeature.createFakeOsmFeature( newLineString(0, 0, 1, 0, 1, 1, 0, 1, 0, 0), diff --git a/src/test/java/org/openmaptiles/layers/TransportationTest.java b/src/test/java/org/openmaptiles/layers/TransportationTest.java index 943c10aa..3c52e643 100644 --- a/src/test/java/org/openmaptiles/layers/TransportationTest.java +++ b/src/test/java/org/openmaptiles/layers/TransportationTest.java @@ -13,6 +13,7 @@ import com.onthegomap.planetiler.reader.SourceFeature; import com.onthegomap.planetiler.reader.osm.OsmElement; import com.onthegomap.planetiler.stats.Stats; +import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.stream.Stream; @@ -1932,24 +1933,85 @@ void testIssue86() { @Test void testGrade1SurfacePath() { assertFeatures(14, List.of(Map.of( - "_layer", "transportation", - "class", "track", - "surface", "paved" + "_layer", "transportation", + "class", "track", + "surface", "paved" )), process(lineFeature(Map.of( - "surface", "grade1", - "highway", "track" + "surface", "grade1", + "highway", "track" )))); } @Test void testGrade1TracktypePath() { assertFeatures(14, List.of(Map.of( - "_layer", "transportation", - "class", "track", - "surface", "paved" + "_layer", "transportation", + "class", "track", + "surface", "paved" )), process(lineFeature(Map.of( - "tracktype", "grade1", - "highway", "track" + "tracktype", "grade1", + "highway", "track" )))); } + + private record TestEntry( + SourceFeature feature, + int expectedZoom + ) {} + + private void createBrunnelForMinZoomTest(List testEntries, double length, int expectedZoom, String name) { + var feature = lineFeatureWithLength(length, Map.of( + "name", name, + "bridge", "yes", + "highway", "motorway" + )); + testEntries.add(new TestEntry( + feature, + Math.min(14, Math.max(9, expectedZoom)) + )); + } + + @Test + void testGetBrunnelMinzoom() throws GeometryException { + // Threshold is 2% of tile size, but that is not a nice number when working with 2^N and LOG2 + // so 1/64-th (e.g. around 1.6%) is used here, hence ... + // ... side is 1/64 tile side: from pixels to world coord, for say Z14 ... + //final double PORTION_OF_TILE_SIDE = (256d / 64) / Math.pow(2d, 14d + 8d); + // ... and then for some lower zoom: + //double testLineSide = PORTION_OF_TILE_SIDE * Math.pow(2, 14 - zoom); + // all this then simplified to `testLength` calculation bellow + + final List testEntries = new ArrayList<>(); + for (int zoom = 14; zoom >= 0; zoom--) { + double testLength = Math.pow(2, -zoom - 6); + + // slightly bellow the threshold + createBrunnelForMinZoomTest(testEntries, testLength * 0.999, zoom + 1, "brunnel-"); + // precisely at the threshold + createBrunnelForMinZoomTest(testEntries, testLength, zoom, "brunnel="); + // slightly over the threshold + createBrunnelForMinZoomTest(testEntries, testLength * 1.001, zoom, "brunnel+"); + } + + for (var entry : testEntries) { + var result = process(entry.feature); + + assertFeatures(entry.expectedZoom, List.of(Map.of( + "_layer", "transportation", + "_type", "line", + "class", "motorway", + "brunnel", "bridge" + ), Map.of( + "_layer", "transportation_name", + "_type", "line" + )), result); + + assertFeatures(entry.expectedZoom - 1, List.of(Map.of( + "_layer", "transportation", + "brunnel", "" + ), Map.of( + "_layer", "transportation_name" + )), result); + } + } } From 3876d6b206940330998fa1b51226793c2906df56 Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Tue, 10 Oct 2023 16:13:05 +0200 Subject: [PATCH 037/106] unit test testInterstateMotorway(): brunnel tag for test line no longer available at Z8 --- src/test/java/org/openmaptiles/layers/TransportationTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/org/openmaptiles/layers/TransportationTest.java b/src/test/java/org/openmaptiles/layers/TransportationTest.java index 3c52e643..b4dae8f2 100644 --- a/src/test/java/org/openmaptiles/layers/TransportationTest.java +++ b/src/test/java/org/openmaptiles/layers/TransportationTest.java @@ -326,7 +326,7 @@ void testInterstateMotorway() { "bicycle", "", "foot", "", "horse", "", - "brunnel", "bridge", + "brunnel", "", "_minzoom", 4 ), Map.of( "_layer", "transportation_name", From bca12e213be6268410c6d5aa6423f55a15ae144c Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Tue, 10 Oct 2023 16:16:18 +0200 Subject: [PATCH 038/106] unit test testInterstateMotorway() clean-up: Z13 was tested twice --- .../layers/TransportationTest.java | 25 ------------------- 1 file changed, 25 deletions(-) diff --git a/src/test/java/org/openmaptiles/layers/TransportationTest.java b/src/test/java/org/openmaptiles/layers/TransportationTest.java index b4dae8f2..f37116c9 100644 --- a/src/test/java/org/openmaptiles/layers/TransportationTest.java +++ b/src/test/java/org/openmaptiles/layers/TransportationTest.java @@ -268,31 +268,6 @@ void testInterstateMotorway() { "bridge", "yes" ))); - assertFeatures(13, List.of(mapOf( - "_layer", "transportation", - "class", "motorway", - "surface", "paved", - "oneway", 1, - "ramp", "", - "bicycle", "no", - "foot", "no", - "horse", "no", - "brunnel", "bridge", - "network", "us-interstate", - "_minzoom", 4 - ), Map.of( - "_layer", "transportation_name", - "class", "motorway", - "name", "Massachusetts Turnpike", - "name_en", "Massachusetts Turnpike", - "ref", "90", - "ref_length", 2, - "network", "us-interstate", - "brunnel", "", - "route_1", "US:I=90", - "_minzoom", 6 - )), features); - assertFeatures(13, List.of(Map.of( "_layer", "transportation", "class", "motorway", From ac6bb21a0e1fb84e9e65f0052f4e5ef3e7141825 Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Wed, 11 Oct 2023 14:32:56 +0200 Subject: [PATCH 039/106] minor clean-up: fixed unit test naming --- .../java/org/openmaptiles/layers/HousenumberTest.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/test/java/org/openmaptiles/layers/HousenumberTest.java b/src/test/java/org/openmaptiles/layers/HousenumberTest.java index bacbcd7f..e4083203 100644 --- a/src/test/java/org/openmaptiles/layers/HousenumberTest.java +++ b/src/test/java/org/openmaptiles/layers/HousenumberTest.java @@ -37,25 +37,25 @@ void testDisplayHousenumberNonRange() { } @Test - void testDisplayHousenumberNumericRange() { + void testDisplayHousenumberNonnumericRange() { final String HOUSENUMBER = "1;1a;2;2/b;20;3"; assertEquals("1–3", Housenumber.displayHousenumber(HOUSENUMBER)); } @Test - void testDisplayHousenumberNumericRangeBroken() { + void testDisplayHousenumberNonnumericRangeBroken() { final String HOUSENUMBER = "1;1a;2;2/b;20;3;"; assertEquals("1–3", Housenumber.displayHousenumber(HOUSENUMBER)); } @Test - void testDisplayHousenumberNonnumericRange() { + void testDisplayHousenumberNumericRange() { final String HOUSENUMBER = "1;2;20;3"; assertEquals("1–20", Housenumber.displayHousenumber(HOUSENUMBER)); } @Test - void testDisplayHousenumberNonnumericRangeBroken() { + void testDisplayHousenumberNumericRangeBroken() { final String HOUSENUMBER = "1;2;20;3;"; assertEquals("1–20", Housenumber.displayHousenumber(HOUSENUMBER)); } From 72abc212c3925eae354cfd3e3f0c879de27bea3d Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Thu, 12 Oct 2023 18:15:41 +0200 Subject: [PATCH 040/106] partial fix for differences in transportation_name layer The difference is between OpenMapTiles/master (OMT) and planetiler-openmaptiles/omt_3_15_0 (PT-OMT) (e.g. development versions). The point is, that while PT-OMT was using limit of "8km" for Z9-Z11, OMT is using limit "ST_Length(geometry) > 8000 / POWER(2, zoom_level - 9) AND zoom_level BETWEEN 9 AND 11". Some further differences still visible, hence further commits expected. --- src/main/java/org/openmaptiles/layers/TransportationName.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/openmaptiles/layers/TransportationName.java b/src/main/java/org/openmaptiles/layers/TransportationName.java index 1080576e..34c7fcdb 100644 --- a/src/main/java/org/openmaptiles/layers/TransportationName.java +++ b/src/main/java/org/openmaptiles/layers/TransportationName.java @@ -109,8 +109,8 @@ public class TransportationName implements .put(7, 20_000) .put(8, 14_000) .put(9, 8_000) - .put(10, 8_000) - .put(11, 8_000); + .put(10, 4_000) + .put(11, 2_000); private static final List CONCURRENT_ROUTE_KEYS = List.of( Fields.ROUTE_1, Fields.ROUTE_2, From b7e7f4c45772d320980b6c40eccab2beae86fa97 Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Tue, 17 Oct 2023 19:46:55 +0200 Subject: [PATCH 041/106] further adjustments to better match what is done with ferries in OMT ... (as per OMT PR 1486) But FERRY_MIN_PIXEL_SIZE is "too much" in the contexct of Planetiler, since it is applied within tiles, hence causes gaps in lines if a line "strikes a little" certain tile. Hence we will need to divert a little. --- .../openmaptiles/layers/Transportation.java | 57 +++++++++++++++++-- 1 file changed, 52 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/openmaptiles/layers/Transportation.java b/src/main/java/org/openmaptiles/layers/Transportation.java index 74b05cb7..4004b1fa 100644 --- a/src/main/java/org/openmaptiles/layers/Transportation.java +++ b/src/main/java/org/openmaptiles/layers/Transportation.java @@ -155,6 +155,13 @@ public class Transportation implements .put(6, 100) .put(5, 500) .put(4, 1_000); + // "shipway_linestring_gen_z10: ... sql_filter: ST_Length(geometry)>ZRES5", in "world coordinates" + private static final double FERRY_MIN_LENTH_XXX = GeoUtils.metersPerPixelAtEquator(5) / GeoUtils.WORLD_CIRCUMFERENCE_METERS; + private static final double FERRY_MIN_LENTH = 1 / Math.pow(2d, 5d + 8d); // TODO: 5 + 8 -> 13 + // "shipway_linestring_gen_z5: ... tolerance: ZRES6", etc. when recalculated from meters to pixels is always: + private static final double FERRY_TOLERANCE = 0.5; + // "ST_Length(geometry)>ZRES5 for Z10", etc. when recalculated from meters to pixels is always: + private static final double FERRY_MIN_PIXEL_SIZE = 32; // ORDER BY network_type, network, LENGTH(ref), ref) private static final Comparator RELATION_ORDERING = Comparator .comparingInt( @@ -599,6 +606,18 @@ public void process(Tables.OsmAerialwayLinestring element, FeatureCollector feat @Override public void process(Tables.OsmShipwayLinestring element, FeatureCollector features) { + try { + // In OpenMapTiles there are different limits (in kilometers) per zoom level which we are not able to do here. + // Hence, we do at least filter on `min(ZRES5, ZRES4, ...)`, e.g. ZRES5, for all zoom levels here: + // we get what we want at Z10 and only "few more" are Z4-Z9. + if (element.source().length() < FERRY_MIN_LENTH) { + return; + } + } catch (GeometryException e) { + e.log(stats, "omt_ferry_length", "Unable to check length: " + element); + return; + } + features.line(LAYER_NAME).setBufferPixels(BUFFER_SIZE) .setAttr(Fields.CLASS, element.shipway()) // "ferry" // no subclass @@ -632,11 +651,7 @@ public void process(Tables.OsmHighwayPolygon element, FeatureCollector features) } } - @Override - public List postProcess(int zoom, List items) { - double tolerance = config.tolerance(zoom); - double minLength = coalesce(MIN_LENGTH.apply(zoom), 0).doubleValue(); - + private List postProcessItems(List items, double minLength, double tolerance) { // don't merge road segments with oneway tag // TODO merge preserving oneway instead ignoring int onewayId = 1; @@ -655,6 +670,38 @@ public List postProcess(int zoom, List i return merged; } + private List postProcessAllOrNonFerry(int zoom, List items) { + return postProcessItems( + items, + coalesce(MIN_LENGTH.apply(zoom), 0).doubleValue(), + config.tolerance(zoom)); + } + + private List postProcessFerry(List items) { + return postProcessItems(items, FERRY_MIN_PIXEL_SIZE, FERRY_TOLERANCE); + } + + @Override + public List postProcess(int zoom, List items) { + if (zoom < 4 || zoom > 10) { + return postProcessAllOrNonFerry(zoom, items); + } else { + // ferries at Z4-Z10 need different treatment + List ferryItems = new ArrayList<>(); + List otherItems = new ArrayList<>(); + for (var item : items) { + if (FieldValues.CLASS_FERRY.equals(item.attrs().get(Fields.CLASS))) { + ferryItems.add(item); + } else { + otherItems.add(item); + } + } + var result = postProcessFerry(ferryItems); + result.addAll(postProcessAllOrNonFerry(zoom, otherItems)); + return result; + } + } + enum RouteNetwork { US_INTERSTATE("us-interstate"), From 00560b39ab8f4e36496bea4ef2076bcd32fefa8e Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Tue, 17 Oct 2023 19:59:33 +0200 Subject: [PATCH 042/106] ferry minLength tweak + clean-up --- .../org/openmaptiles/layers/Transportation.java | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/src/main/java/org/openmaptiles/layers/Transportation.java b/src/main/java/org/openmaptiles/layers/Transportation.java index 4004b1fa..9e11bb20 100644 --- a/src/main/java/org/openmaptiles/layers/Transportation.java +++ b/src/main/java/org/openmaptiles/layers/Transportation.java @@ -156,12 +156,9 @@ public class Transportation implements .put(5, 500) .put(4, 1_000); // "shipway_linestring_gen_z10: ... sql_filter: ST_Length(geometry)>ZRES5", in "world coordinates" - private static final double FERRY_MIN_LENTH_XXX = GeoUtils.metersPerPixelAtEquator(5) / GeoUtils.WORLD_CIRCUMFERENCE_METERS; - private static final double FERRY_MIN_LENTH = 1 / Math.pow(2d, 5d + 8d); // TODO: 5 + 8 -> 13 + private static final double FERRY_MIN_LENTH = 1 / Math.pow(2d, 13d); // "shipway_linestring_gen_z5: ... tolerance: ZRES6", etc. when recalculated from meters to pixels is always: private static final double FERRY_TOLERANCE = 0.5; - // "ST_Length(geometry)>ZRES5 for Z10", etc. when recalculated from meters to pixels is always: - private static final double FERRY_MIN_PIXEL_SIZE = 32; // ORDER BY network_type, network, LENGTH(ref), ref) private static final Comparator RELATION_ORDERING = Comparator .comparingInt( @@ -651,7 +648,9 @@ public void process(Tables.OsmHighwayPolygon element, FeatureCollector features) } } - private List postProcessItems(List items, double minLength, double tolerance) { + private List postProcessItems(int zoom, List items, double tolerance) { + double minLength = coalesce(MIN_LENGTH.apply(zoom), 0).doubleValue(); + // don't merge road segments with oneway tag // TODO merge preserving oneway instead ignoring int onewayId = 1; @@ -672,13 +671,13 @@ private List postProcessItems(List items private List postProcessAllOrNonFerry(int zoom, List items) { return postProcessItems( + zoom, items, - coalesce(MIN_LENGTH.apply(zoom), 0).doubleValue(), config.tolerance(zoom)); } - private List postProcessFerry(List items) { - return postProcessItems(items, FERRY_MIN_PIXEL_SIZE, FERRY_TOLERANCE); + private List postProcessFerry(int zoom, List items) { + return postProcessItems(zoom, items, FERRY_TOLERANCE); } @Override @@ -696,7 +695,7 @@ public List postProcess(int zoom, List i otherItems.add(item); } } - var result = postProcessFerry(ferryItems); + var result = postProcessFerry(zoom, ferryItems); result.addAll(postProcessAllOrNonFerry(zoom, otherItems)); return result; } From 5d0872030578a7f186de60f28a672b59c8a8ea95 Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Tue, 17 Oct 2023 20:00:16 +0200 Subject: [PATCH 043/106] mvn spotless:apply --- src/main/java/org/openmaptiles/layers/Transportation.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/openmaptiles/layers/Transportation.java b/src/main/java/org/openmaptiles/layers/Transportation.java index 9e11bb20..5489e3ba 100644 --- a/src/main/java/org/openmaptiles/layers/Transportation.java +++ b/src/main/java/org/openmaptiles/layers/Transportation.java @@ -671,9 +671,9 @@ private List postProcessItems(int zoom, List postProcessAllOrNonFerry(int zoom, List items) { return postProcessItems( - zoom, - items, - config.tolerance(zoom)); + zoom, + items, + config.tolerance(zoom)); } private List postProcessFerry(int zoom, List items) { @@ -697,7 +697,7 @@ public List postProcess(int zoom, List i } var result = postProcessFerry(zoom, ferryItems); result.addAll(postProcessAllOrNonFerry(zoom, otherItems)); - return result; + return result; } } From 397efef694bc8b6da20ea89d00f6e2c5aeb5b9a2 Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Tue, 17 Oct 2023 20:09:23 +0200 Subject: [PATCH 044/106] fixed minor typo --- src/main/java/org/openmaptiles/layers/Transportation.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/openmaptiles/layers/Transportation.java b/src/main/java/org/openmaptiles/layers/Transportation.java index 5489e3ba..31456226 100644 --- a/src/main/java/org/openmaptiles/layers/Transportation.java +++ b/src/main/java/org/openmaptiles/layers/Transportation.java @@ -606,7 +606,7 @@ public void process(Tables.OsmShipwayLinestring element, FeatureCollector featur try { // In OpenMapTiles there are different limits (in kilometers) per zoom level which we are not able to do here. // Hence, we do at least filter on `min(ZRES5, ZRES4, ...)`, e.g. ZRES5, for all zoom levels here: - // we get what we want at Z10 and only "few more" are Z4-Z9. + // we get what we want at Z10 and only "few more" at Z4-Z9. if (element.source().length() < FERRY_MIN_LENTH) { return; } From dca96714568b302439b429728ba56759d908dc51 Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Tue, 17 Oct 2023 20:10:22 +0200 Subject: [PATCH 045/106] minor reformatting --- src/main/java/org/openmaptiles/layers/Transportation.java | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/main/java/org/openmaptiles/layers/Transportation.java b/src/main/java/org/openmaptiles/layers/Transportation.java index 31456226..39b7dbf3 100644 --- a/src/main/java/org/openmaptiles/layers/Transportation.java +++ b/src/main/java/org/openmaptiles/layers/Transportation.java @@ -670,10 +670,7 @@ private List postProcessItems(int zoom, List postProcessAllOrNonFerry(int zoom, List items) { - return postProcessItems( - zoom, - items, - config.tolerance(zoom)); + return postProcessItems(zoom, items, config.tolerance(zoom)); } private List postProcessFerry(int zoom, List items) { From 8654d8649049caf7a30008120bc2b0d5be78c7f5 Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Wed, 18 Oct 2023 11:34:59 +0200 Subject: [PATCH 046/106] ferry line length filter replaced with min. zoom calculation hence the results are much closer to what OMT is doing for Z4-Z9 --- .../openmaptiles/layers/Transportation.java | 36 ++++++++----- .../layers/AbstractLayerTest.java | 10 ++++ .../layers/TransportationTest.java | 54 +++++++++++++++++++ 3 files changed, 87 insertions(+), 13 deletions(-) diff --git a/src/main/java/org/openmaptiles/layers/Transportation.java b/src/main/java/org/openmaptiles/layers/Transportation.java index 39b7dbf3..ed14f312 100644 --- a/src/main/java/org/openmaptiles/layers/Transportation.java +++ b/src/main/java/org/openmaptiles/layers/Transportation.java @@ -168,6 +168,7 @@ public class Transportation implements .thenComparing(RouteRelation::ref); private static final Set ONEWAY_VALUES = Set.of(-1, 1); private static final String LIMIT_MERGE_TAG = "__limit_merge"; + private static final double LOG2 = Math.log(2); private final AtomicBoolean loggedNoGb = new AtomicBoolean(false); private final AtomicBoolean loggedNoIreland = new AtomicBoolean(false); private final boolean z13Paths; @@ -603,18 +604,6 @@ public void process(Tables.OsmAerialwayLinestring element, FeatureCollector feat @Override public void process(Tables.OsmShipwayLinestring element, FeatureCollector features) { - try { - // In OpenMapTiles there are different limits (in kilometers) per zoom level which we are not able to do here. - // Hence, we do at least filter on `min(ZRES5, ZRES4, ...)`, e.g. ZRES5, for all zoom levels here: - // we get what we want at Z10 and only "few more" at Z4-Z9. - if (element.source().length() < FERRY_MIN_LENTH) { - return; - } - } catch (GeometryException e) { - e.log(stats, "omt_ferry_length", "Unable to check length: " + element); - return; - } - features.line(LAYER_NAME).setBufferPixels(BUFFER_SIZE) .setAttr(Fields.CLASS, element.shipway()) // "ferry" // no subclass @@ -625,7 +614,28 @@ public void process(Tables.OsmShipwayLinestring element, FeatureCollector featur .setAttr(Fields.LAYER, nullIfLong(element.layer(), 0)) .setSortKey(element.zOrder()) .setMinPixelSize(0) // merge during post-processing, then limit by size - .setMinZoom(4); + .setMinZoom(getFerryMinzoom(element)); + } + + // We will have several of those for 3.15 => TODO: unify/simplify. + int getFerryMinzoom(Tables.OsmShipwayLinestring element) { + // full(-er) formula (along with comments) is in TranportationTest.testGetFerryMinzoom(), here is simplified reverse of that + double zoom; + try { + zoom = -(Math.log(element.source().length()) / LOG2) - 3; + } catch (GeometryException e) { + e.log(stats, "omt_ferry_minzoom", + "Unable to calculate ferry minzoom for " + element.source().id()); + // ferries are supposed to be included in Z4-Z10 depending on their length (=this min. zoom calculation), for Z11+ always, hence 11 as fallback + return 11; + } + + // Say Z13.01 means bellow threshold, Z13.00 is exactly threshold, Z12.99 is over threshold, + // hence Z13.01 and Z13.00 will be rounded to Z14 and Z12.99 to Z13 (e.g. `floor() + 1`). + // And to accommodate for some precision errors (observed for Z9-Z11) we do also `- 0.1e-10`. + int result = (int) Math.floor(zoom - 0.1e-10) + 1; + + return Math.min(11, Math.max(4, result)); } @Override diff --git a/src/test/java/org/openmaptiles/layers/AbstractLayerTest.java b/src/test/java/org/openmaptiles/layers/AbstractLayerTest.java index 05e94d99..e3f649e3 100644 --- a/src/test/java/org/openmaptiles/layers/AbstractLayerTest.java +++ b/src/test/java/org/openmaptiles/layers/AbstractLayerTest.java @@ -137,6 +137,16 @@ SourceFeature lineFeature(Map props) { ); } + SourceFeature lineFeatureWithLength(double length, Map props) { + return SimpleFeature.create( + GeoUtils.worldToLatLonCoords(newLineString(0, 0, 0, length)), + new HashMap<>(props), + OpenMapTilesProfile.OSM_SOURCE, + null, + 0 + ); + } + SourceFeature closedWayFeature(Map props) { return SimpleFeature.createFakeOsmFeature( newLineString(0, 0, 1, 0, 1, 1, 0, 1, 0, 0), diff --git a/src/test/java/org/openmaptiles/layers/TransportationTest.java b/src/test/java/org/openmaptiles/layers/TransportationTest.java index 2dda8831..9d2fe895 100644 --- a/src/test/java/org/openmaptiles/layers/TransportationTest.java +++ b/src/test/java/org/openmaptiles/layers/TransportationTest.java @@ -13,6 +13,7 @@ import com.onthegomap.planetiler.reader.SourceFeature; import com.onthegomap.planetiler.reader.osm.OsmElement; import com.onthegomap.planetiler.stats.Stats; +import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.stream.Stream; @@ -1997,4 +1998,57 @@ void testGrade1TracktypePath() { "highway", "track" )))); } + + private record TestEntry( + SourceFeature feature, + int expectedZoom + ) {} + + private void createFerryForMinZoomTest(List testEntries, double length, int expectedZoom, String name) { + var feature = lineFeatureWithLength(length, Map.of( + "name", name, + "route", "ferry" + )); + testEntries.add(new TestEntry( + feature, + Math.min(11, Math.max(4, expectedZoom)) + )); + } + + @Test + void testGetFerryMinzoom() throws GeometryException { + // Threshold is 1/8 of tile size, hence ... + // ... side is 1/8 tile side: from pixels to world coord, for say Z14 ... + //final double PORTION_OF_TILE_SIDE = (256d / 8) / Math.pow(2d, 14d + 8d); + // ... and then for some lower zoom: + //double testLineSide = PORTION_OF_TILE_SIDE * Math.pow(2, 14 - zoom); + // all this then simplified to `testLength` calculation bellow + + final List testEntries = new ArrayList<>(); + for (int zoom = 14; zoom >= 0; zoom--) { + double testLength = Math.pow(2, -zoom - 3); + + // slightly bellow the threshold + createFerryForMinZoomTest(testEntries, testLength * 0.999, zoom + 1, "ferry-"); + // precisely at the threshold + createFerryForMinZoomTest(testEntries, testLength, zoom, "ferry="); + // slightly over the threshold + createFerryForMinZoomTest(testEntries, testLength * 1.001, zoom, "ferry+"); + } + + for (var entry : testEntries) { + assertFeatures(14, List.of(Map.of( + "_layer", "transportation", + "class", "ferry", + "_minzoom", entry.expectedZoom, + "_maxzoom", 14 + ), Map.of( + "_layer", "transportation_name", + "_type", "line" + )), process(entry.feature)); + /* + process(entry.feature); + */ + } + } } From 8e1703b7e4f3710b59836836ff00cdca348fc76a Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Wed, 18 Oct 2023 11:38:35 +0200 Subject: [PATCH 047/106] testFerry() adjusted to match previous commit ferry test polygon with area 1 now qualifies for min. zoom 5 --- src/test/java/org/openmaptiles/layers/TransportationTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/org/openmaptiles/layers/TransportationTest.java b/src/test/java/org/openmaptiles/layers/TransportationTest.java index 9d2fe895..d9806149 100644 --- a/src/test/java/org/openmaptiles/layers/TransportationTest.java +++ b/src/test/java/org/openmaptiles/layers/TransportationTest.java @@ -1793,7 +1793,7 @@ void testFerry() { "_layer", "transportation", "class", "ferry", - "_minzoom", 4, + "_minzoom", 5, "_maxzoom", 14, "_type", "line" ), Map.of( From 6c05d7dc1f82d4abc4049abeb3cf29309a9e7d02 Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Wed, 18 Oct 2023 11:41:22 +0200 Subject: [PATCH 048/106] clea-up of unused stuff + mvn spotless:apply --- .../java/org/openmaptiles/layers/Transportation.java | 4 +--- .../org/openmaptiles/layers/TransportationTest.java | 12 ++++++------ 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/src/main/java/org/openmaptiles/layers/Transportation.java b/src/main/java/org/openmaptiles/layers/Transportation.java index ed14f312..0a4a7669 100644 --- a/src/main/java/org/openmaptiles/layers/Transportation.java +++ b/src/main/java/org/openmaptiles/layers/Transportation.java @@ -155,8 +155,6 @@ public class Transportation implements .put(6, 100) .put(5, 500) .put(4, 1_000); - // "shipway_linestring_gen_z10: ... sql_filter: ST_Length(geometry)>ZRES5", in "world coordinates" - private static final double FERRY_MIN_LENTH = 1 / Math.pow(2d, 13d); // "shipway_linestring_gen_z5: ... tolerance: ZRES6", etc. when recalculated from meters to pixels is always: private static final double FERRY_TOLERANCE = 0.5; // ORDER BY network_type, network, LENGTH(ref), ref) @@ -625,7 +623,7 @@ int getFerryMinzoom(Tables.OsmShipwayLinestring element) { zoom = -(Math.log(element.source().length()) / LOG2) - 3; } catch (GeometryException e) { e.log(stats, "omt_ferry_minzoom", - "Unable to calculate ferry minzoom for " + element.source().id()); + "Unable to calculate ferry minzoom for " + element.source().id()); // ferries are supposed to be included in Z4-Z10 depending on their length (=this min. zoom calculation), for Z11+ always, hence 11 as fallback return 11; } diff --git a/src/test/java/org/openmaptiles/layers/TransportationTest.java b/src/test/java/org/openmaptiles/layers/TransportationTest.java index d9806149..254b6795 100644 --- a/src/test/java/org/openmaptiles/layers/TransportationTest.java +++ b/src/test/java/org/openmaptiles/layers/TransportationTest.java @@ -2038,13 +2038,13 @@ void testGetFerryMinzoom() throws GeometryException { for (var entry : testEntries) { assertFeatures(14, List.of(Map.of( - "_layer", "transportation", - "class", "ferry", - "_minzoom", entry.expectedZoom, - "_maxzoom", 14 + "_layer", "transportation", + "class", "ferry", + "_minzoom", entry.expectedZoom, + "_maxzoom", 14 ), Map.of( - "_layer", "transportation_name", - "_type", "line" + "_layer", "transportation_name", + "_type", "line" )), process(entry.feature)); /* process(entry.feature); From b6cda03804949e769da0bec537936b36e4446060 Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Wed, 18 Oct 2023 11:42:17 +0200 Subject: [PATCH 049/106] mvn spotless:apply --- .../java/org/openmaptiles/layers/Park.java | 6 ++-- .../org/openmaptiles/layers/ParkTest.java | 32 +++++++++---------- 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/src/main/java/org/openmaptiles/layers/Park.java b/src/main/java/org/openmaptiles/layers/Park.java index 20ed1562..e862021f 100644 --- a/src/main/java/org/openmaptiles/layers/Park.java +++ b/src/main/java/org/openmaptiles/layers/Park.java @@ -100,9 +100,9 @@ public void process(Tables.OsmParkPolygon element, FeatureCollector features) { protectionTitle = protectionTitle.replace(' ', '_').toLowerCase(Locale.ROOT); } clazz = coalesce( - nullIfEmpty(protectionTitle), - nullIfEmpty(element.boundary()), - nullIfEmpty(element.leisure()) + nullIfEmpty(protectionTitle), + nullIfEmpty(element.boundary()), + nullIfEmpty(element.leisure()) ); } diff --git a/src/test/java/org/openmaptiles/layers/ParkTest.java b/src/test/java/org/openmaptiles/layers/ParkTest.java index 63ee4ba6..2383a136 100644 --- a/src/test/java/org/openmaptiles/layers/ParkTest.java +++ b/src/test/java/org/openmaptiles/layers/ParkTest.java @@ -48,24 +48,24 @@ void testNationalPark() { @Test void testAbotiginalLand() { assertFeatures(13, List.of(Map.of( - "_layer", "park", - "_type", "polygon", - "class", "aboriginal_lands", - "name", "Hualapai Tribe", - "_minpixelsize", 2d, - "_minzoom", 4, - "_maxzoom", 14 + "_layer", "park", + "_type", "polygon", + "class", "aboriginal_lands", + "name", "Hualapai Tribe", + "_minpixelsize", 2d, + "_minzoom", 4, + "_maxzoom", 14 ), Map.of( - "_layer", "park", - "_type", "point", - "class", "aboriginal_lands", - "name", "Hualapai Tribe", - "_minzoom", 5, - "_maxzoom", 14 + "_layer", "park", + "_type", "point", + "class", "aboriginal_lands", + "name", "Hualapai Tribe", + "_minzoom", 5, + "_maxzoom", 14 )), process(polygonFeature(Map.of( - "boundary", "aboriginal_lands", - "name", "Hualapai Tribe", - "protection_title", "National Park" + "boundary", "aboriginal_lands", + "name", "Hualapai Tribe", + "protection_title", "National Park" )))); } From 9b59981292e11a5f793cdaec7d11dd06588cc552 Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Wed, 18 Oct 2023 21:57:44 +0200 Subject: [PATCH 050/106] added TODO node for follow-up pull-request/simplification --- src/main/java/org/openmaptiles/layers/Transportation.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/org/openmaptiles/layers/Transportation.java b/src/main/java/org/openmaptiles/layers/Transportation.java index 6cde9c5e..8c96aca1 100644 --- a/src/main/java/org/openmaptiles/layers/Transportation.java +++ b/src/main/java/org/openmaptiles/layers/Transportation.java @@ -705,6 +705,7 @@ private List postProcessItems(int zoom, List postProcessAllOrNonFerry(int zoom, List items) { + // TODO: use same tolerance as for ferries (see tolerances in OMT `transportation/mapping.yaml`), hence no need to split ferries from the rest in postProcess() return postProcessItems(zoom, items, config.tolerance(zoom)); } From a7bcfa9cc411efa04316898d3c71c56fa626254c Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Wed, 18 Oct 2023 23:03:28 +0200 Subject: [PATCH 051/106] clean-up: common getMinZoom() code moved to Utils --- .../java/org/openmaptiles/layers/Poi.java | 2 +- .../openmaptiles/layers/Transportation.java | 26 ++---------- .../org/openmaptiles/layers/WaterName.java | 12 ++---- .../java/org/openmaptiles/util/Utils.java | 41 +++++++++++++++++++ .../layers/TransportationTest.java | 15 ------- 5 files changed, 49 insertions(+), 47 deletions(-) diff --git a/src/main/java/org/openmaptiles/layers/Poi.java b/src/main/java/org/openmaptiles/layers/Poi.java index e8eb03d4..70e4aa34 100644 --- a/src/main/java/org/openmaptiles/layers/Poi.java +++ b/src/main/java/org/openmaptiles/layers/Poi.java @@ -155,7 +155,7 @@ private int minzoom(String subclass, String mappingKey) { public static int uniAreaToMinZoom(double areaWorld) { double oneSideWorld = Math.sqrt(areaWorld); - // full(-er) formula (along with comments) is in PoiTest.testUniAreaToMinZoom(), here is simplified reverse of that + // adjusted formula from `Utils.getMinZoomForLength()`, given that 1/10 does not match `1 / (2^)` double zoom = -(Math.log(oneSideWorld * SQRT10) / LOG2); // Say Z13.01 means bellow threshold, Z13.00 is exactly threshold, Z12.99 is over threshold, diff --git a/src/main/java/org/openmaptiles/layers/Transportation.java b/src/main/java/org/openmaptiles/layers/Transportation.java index 8c96aca1..ddeaee85 100644 --- a/src/main/java/org/openmaptiles/layers/Transportation.java +++ b/src/main/java/org/openmaptiles/layers/Transportation.java @@ -74,6 +74,7 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE import org.openmaptiles.OpenMapTilesProfile; import org.openmaptiles.generated.OpenMapTilesSchema; import org.openmaptiles.generated.Tables; +import org.openmaptiles.util.Utils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -545,23 +546,14 @@ int getMinzoom(Tables.OsmHighwayLinestring element, String highwayClass) { } int getBrunnelMinzoom(Tables.OsmHighwayLinestring element) { - // full(-er) formula (along with comments) is in TranportationTest.testGetBrunnelMinzoom(), here is simplified reverse of that - double zoom; try { - zoom = -(Math.log(element.source().length()) / LOG2) - 6; + return Utils.getClippedMinZoomForLength(element.source().length(), 6, 9, 12); } catch (GeometryException e) { e.log(stats, "omt_brunnel_minzoom", "Unable to calculate brunnel minzoom for " + element.source().id()); // brunnel is optional (depends on feature size) for Z9-Z11, it is always present for Z12+, hence 12 as fallback - return BRUNNEL_FALLBACK_MINZOOM; + return 12; } - - // Say Z13.01 means bellow threshold, Z13.00 is exactly threshold, Z12.99 is over threshold, - // hence Z13.01 and Z13.00 will be rounded to Z14 and Z12.99 to Z13 (e.g. `floor() + 1`). - // And to accommodate for some precision errors (observed for Z9-Z11) we do also `- 0.1e-10`. - int result = (int) Math.floor(zoom - 0.1e-10) + 1; - - return Math.min(config.maxzoom(), Math.max(9, result)); } private boolean isPierPolygon(Tables.OsmHighwayLinestring element) { @@ -642,25 +634,15 @@ public void process(Tables.OsmShipwayLinestring element, FeatureCollector featur .setMinZoom(getFerryMinzoom(element)); } - // We will have several of those for 3.15 => TODO: unify/simplify. int getFerryMinzoom(Tables.OsmShipwayLinestring element) { - // full(-er) formula (along with comments) is in TranportationTest.testGetFerryMinzoom(), here is simplified reverse of that - double zoom; try { - zoom = -(Math.log(element.source().length()) / LOG2) - 3; + return Utils.getClippedMinZoomForLength(element.source().length(), 3, 4, 11); } catch (GeometryException e) { e.log(stats, "omt_ferry_minzoom", "Unable to calculate ferry minzoom for " + element.source().id()); // ferries are supposed to be included in Z4-Z10 depending on their length (=this min. zoom calculation), for Z11+ always, hence 11 as fallback return 11; } - - // Say Z13.01 means bellow threshold, Z13.00 is exactly threshold, Z12.99 is over threshold, - // hence Z13.01 and Z13.00 will be rounded to Z14 and Z12.99 to Z13 (e.g. `floor() + 1`). - // And to accommodate for some precision errors (observed for Z9-Z11) we do also `- 0.1e-10`. - int result = (int) Math.floor(zoom - 0.1e-10) + 1; - - return Math.min(11, Math.max(4, result)); } @Override diff --git a/src/main/java/org/openmaptiles/layers/WaterName.java b/src/main/java/org/openmaptiles/layers/WaterName.java index 18621048..5529d2a8 100644 --- a/src/main/java/org/openmaptiles/layers/WaterName.java +++ b/src/main/java/org/openmaptiles/layers/WaterName.java @@ -56,6 +56,7 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE import org.openmaptiles.generated.OpenMapTilesSchema; import org.openmaptiles.generated.Tables; import org.openmaptiles.util.OmtLanguageUtils; +import org.openmaptiles.util.Utils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -227,14 +228,7 @@ public void process(Tables.OsmWaterPolygon element, FeatureCollector features) { public static int areaToMinZoom(double areaWorld) { double oneSideWorld = Math.sqrt(areaWorld); - // full(-er) formula (along with comments) is in WaterNameTest.testAreaToMinZoom(), here is simplified reverse of that - double zoom = -(Math.log(oneSideWorld) / LOG2) - 1; - - // Say Z13.01 means bellow threshold, Z13.00 is exactly threshold, Z12.99 is over threshold, - // hence Z13.01 and Z13.00 will be rounded to Z14 and Z12.99 to Z13 (e.g. `floor() + 1`). - // And to accommodate for some precision errors (observed for Z9-Z11) we do also `- 0.1e-11`. - int result = (int) Math.floor(zoom - 0.1e-11) + 1; - - return Math.min(14, Math.max(3, result)); + // OMT does "feature area is 1/4 of tile area", which is same as "feature side is 1/2 of tile side" + return Utils.getClippedMinZoomForLength(oneSideWorld, 1, 3, 14); } } diff --git a/src/main/java/org/openmaptiles/util/Utils.java b/src/main/java/org/openmaptiles/util/Utils.java index de9f4bf7..31936057 100644 --- a/src/main/java/org/openmaptiles/util/Utils.java +++ b/src/main/java/org/openmaptiles/util/Utils.java @@ -8,6 +8,8 @@ */ public class Utils { + private static final double LOG2 = Math.log(2); + public static T coalesce(T a, T b) { return a != null ? a : b; } @@ -75,4 +77,43 @@ public static String brunnel(boolean isBridge, boolean isTunnel, boolean isFord) return isBridge ? "bridge" : isTunnel ? "tunnel" : isFord ? "ford" : null; } + /** + * Calculate minzoom for a feature with given length based on threshold: if a feature's length is least + * {@code 1 / (2^threshold)} of a tile at certain zoom level, it will be shown at that and higher zoom levels, e.g. + * that particular zoom level is minzoom. + *

    + * {@code threshold} is calculated in such way to avoid calculating log(2) os it during the runtime. Use 1 for 1/2, 2 + * for 1/4, 3 for 1/8, etc. + * + * @param length length of the feature + * @param threshold threshold + * @return minzoom for a feature with given length and given threshold + */ + public static int getMinZoomForLength(double length, double threshold) { + // Say threshold is 1/8 (threshold variable = 8) of tile size, hence ... + // ... from pixels to world coord, for say Z14, the minimum length is: + // PORTION_OF_TILE_SIDE = (256d / 8) / Math.pow(2d, 14d + 8d); + // ... and then minimum length for some lower zoom: + // PORTION_OF_TILE_SIDE * Math.pow(2, 14 - zoom); + // all this then reversed and simplified to: + double zoom = -(Math.log(length) / LOG2) - threshold; + + // Say Z13.01 means bellow threshold, Z13.00 is exactly threshold, Z12.99 is over threshold, + // hence Z13.01 and Z13.00 will be rounded to Z14 and Z12.99 to Z13 (e.g. `floor() + 1`). + // And to accommodate for some precision errors (observed for Z9-Z11) we do also `- 0.1e-10`. + return (int) Math.floor(zoom - 0.1e-10) + 1; + } + + /** + * Same as {@link #getMinZoomForLength(double, double)} but with result within the given minimum and maximim. + * + * @param length length of the feature + * @param threshold threshold + * @param min clip the result to this value if lower + * @param max clip the result to this value if higher + * @return minzoom for a feature with given length and given threshold clipped to not exceed given minimum and maximum + */ + public static int getClippedMinZoomForLength(double length, double threshold, int min, int max) { + return Math.min(max, Math.max(min, getMinZoomForLength(length, threshold))); + } } diff --git a/src/test/java/org/openmaptiles/layers/TransportationTest.java b/src/test/java/org/openmaptiles/layers/TransportationTest.java index 61558488..85cc8ea2 100644 --- a/src/test/java/org/openmaptiles/layers/TransportationTest.java +++ b/src/test/java/org/openmaptiles/layers/TransportationTest.java @@ -1993,14 +1993,6 @@ private void createBrunnelForMinZoomTest(List testEntries, double len @Test void testGetBrunnelMinzoom() throws GeometryException { - // Threshold is 2% of tile size, but that is not a nice number when working with 2^N and LOG2 - // so 1/64-th (e.g. around 1.6%) is used here, hence ... - // ... side is 1/64 tile side: from pixels to world coord, for say Z14 ... - //final double PORTION_OF_TILE_SIDE = (256d / 64) / Math.pow(2d, 14d + 8d); - // ... and then for some lower zoom: - //double testLineSide = PORTION_OF_TILE_SIDE * Math.pow(2, 14 - zoom); - // all this then simplified to `testLength` calculation bellow - final List testEntries = new ArrayList<>(); for (int zoom = 14; zoom >= 0; zoom--) { double testLength = Math.pow(2, -zoom - 6); @@ -2048,13 +2040,6 @@ private void createFerryForMinZoomTest(List testEntries, double lengt @Test void testGetFerryMinzoom() throws GeometryException { - // Threshold is 1/8 of tile size, hence ... - // ... side is 1/8 tile side: from pixels to world coord, for say Z14 ... - //final double PORTION_OF_TILE_SIDE = (256d / 8) / Math.pow(2d, 14d + 8d); - // ... and then for some lower zoom: - //double testLineSide = PORTION_OF_TILE_SIDE * Math.pow(2, 14 - zoom); - // all this then simplified to `testLength` calculation bellow - final List testEntries = new ArrayList<>(); for (int zoom = 14; zoom >= 0; zoom--) { double testLength = Math.pow(2, -zoom - 3); From 30dab9fafbfbfbb060e24d1ead24de21b0e6ec60 Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Wed, 18 Oct 2023 23:11:08 +0200 Subject: [PATCH 052/106] minzoom clipping for brunnel was adjusted do Z9-Z12 -> test adjusted too --- src/test/java/org/openmaptiles/layers/TransportationTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/org/openmaptiles/layers/TransportationTest.java b/src/test/java/org/openmaptiles/layers/TransportationTest.java index 85cc8ea2..2b66e0e1 100644 --- a/src/test/java/org/openmaptiles/layers/TransportationTest.java +++ b/src/test/java/org/openmaptiles/layers/TransportationTest.java @@ -1987,7 +1987,7 @@ private void createBrunnelForMinZoomTest(List testEntries, double len )); testEntries.add(new TestEntry( feature, - Math.min(14, Math.max(9, expectedZoom)) + Math.min(12, Math.max(9, expectedZoom)) )); } From 1ddfb9a917b8d1ddded215cf691c27d46ff9ce10 Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Mon, 23 Oct 2023 14:41:12 +0200 Subject: [PATCH 053/106] clean-up --- .../java/org/openmaptiles/layers/Transportation.java | 9 +-------- .../java/org/openmaptiles/layers/TransportationName.java | 7 +------ src/main/java/org/openmaptiles/layers/WaterName.java | 1 - 3 files changed, 2 insertions(+), 15 deletions(-) diff --git a/src/main/java/org/openmaptiles/layers/Transportation.java b/src/main/java/org/openmaptiles/layers/Transportation.java index ddeaee85..6408c015 100644 --- a/src/main/java/org/openmaptiles/layers/Transportation.java +++ b/src/main/java/org/openmaptiles/layers/Transportation.java @@ -167,9 +167,7 @@ public class Transportation implements .thenComparing(RouteRelation::ref); private static final Set ONEWAY_VALUES = Set.of(-1, 1); private final Map MINZOOMS; - private static final int BRUNNEL_FALLBACK_MINZOOM = 12; private static final String LIMIT_MERGE_TAG = "__limit_merge"; - private static final double LOG2 = Math.log(2); private final AtomicBoolean loggedNoGb = new AtomicBoolean(false); private final AtomicBoolean loggedNoIreland = new AtomicBoolean(false); private final boolean z13Paths; @@ -455,12 +453,7 @@ public void process(Tables.OsmHighwayLinestring element, FeatureCollector featur int minzoom = getMinzoom(element, highwayClass); String brunnelValue = brunnel(element.isBridge(), element.isTunnel(), element.isFord()); - int brunnelMinzoom; - if (brunnelValue != null) { - brunnelMinzoom = getBrunnelMinzoom(element); - } else { - brunnelMinzoom = minzoom; - } + int brunnelMinzoom = brunnelValue != null ? getBrunnelMinzoom(element) : minzoom; if (minzoom > config.maxzoom()) { return; diff --git a/src/main/java/org/openmaptiles/layers/TransportationName.java b/src/main/java/org/openmaptiles/layers/TransportationName.java index a30d9eaa..25d19f8c 100644 --- a/src/main/java/org/openmaptiles/layers/TransportationName.java +++ b/src/main/java/org/openmaptiles/layers/TransportationName.java @@ -285,12 +285,7 @@ public void process(Tables.OsmHighwayLinestring element, FeatureCollector featur if (brunnel) { String brunnelValue = brunnel(element.isBridge(), element.isTunnel(), element.isFord()); - int brunnelMinzoom; - if (brunnelValue != null) { - brunnelMinzoom = transportation.getBrunnelMinzoom(element); - } else { - brunnelMinzoom = minzoom; - } + int brunnelMinzoom = brunnelValue != null ? transportation.getBrunnelMinzoom(element) : minzoom; feature.setAttrWithMinzoom(Fields.BRUNNEL, brunnelValue, brunnelMinzoom); } diff --git a/src/main/java/org/openmaptiles/layers/WaterName.java b/src/main/java/org/openmaptiles/layers/WaterName.java index 5529d2a8..29d2f5bf 100644 --- a/src/main/java/org/openmaptiles/layers/WaterName.java +++ b/src/main/java/org/openmaptiles/layers/WaterName.java @@ -83,7 +83,6 @@ public class WaterName implements */ private static final Logger LOGGER = LoggerFactory.getLogger(WaterName.class); - private static final double LOG2 = Math.log(2); private static final Set SEA_OR_OCEAN_PLACE = Set.of("sea", "ocean"); private final Translations translations; // need to synchronize updates from multiple threads From 0a2565de763397a7a8dfb948a0335118f23294da Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Mon, 23 Oct 2023 14:53:07 +0200 Subject: [PATCH 054/106] use same tolerance for all transportation items, like OSM does --- src/main/java/org/openmaptiles/layers/Transportation.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/openmaptiles/layers/Transportation.java b/src/main/java/org/openmaptiles/layers/Transportation.java index 6408c015..3dd3dbba 100644 --- a/src/main/java/org/openmaptiles/layers/Transportation.java +++ b/src/main/java/org/openmaptiles/layers/Transportation.java @@ -157,7 +157,7 @@ public class Transportation implements .put(5, 500) .put(4, 1_000); // "shipway_linestring_gen_z5: ... tolerance: ZRES6", etc. when recalculated from meters to pixels is always: - private static final double FERRY_TOLERANCE = 0.5; + private static final double TOLERANCE = 0.5; // ORDER BY network_type, network, LENGTH(ref), ref) private static final Comparator RELATION_ORDERING = Comparator .comparingInt( @@ -680,12 +680,11 @@ private List postProcessItems(int zoom, List postProcessAllOrNonFerry(int zoom, List items) { - // TODO: use same tolerance as for ferries (see tolerances in OMT `transportation/mapping.yaml`), hence no need to split ferries from the rest in postProcess() - return postProcessItems(zoom, items, config.tolerance(zoom)); + return postProcessItems(zoom, items, TOLERANCE); } private List postProcessFerry(int zoom, List items) { - return postProcessItems(zoom, items, FERRY_TOLERANCE); + return postProcessItems(zoom, items, TOLERANCE); } @Override From 59b3078cbabc35cf51d30bf08bc8e53b661cb91b Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Mon, 23 Oct 2023 15:05:05 +0200 Subject: [PATCH 055/106] clean-up, since ferry and non-ferry procesing is now same --- .../openmaptiles/layers/Transportation.java | 37 ++----------------- 1 file changed, 4 insertions(+), 33 deletions(-) diff --git a/src/main/java/org/openmaptiles/layers/Transportation.java b/src/main/java/org/openmaptiles/layers/Transportation.java index 3dd3dbba..1a6d9411 100644 --- a/src/main/java/org/openmaptiles/layers/Transportation.java +++ b/src/main/java/org/openmaptiles/layers/Transportation.java @@ -156,8 +156,6 @@ public class Transportation implements .put(6, 100) .put(5, 500) .put(4, 1_000); - // "shipway_linestring_gen_z5: ... tolerance: ZRES6", etc. when recalculated from meters to pixels is always: - private static final double TOLERANCE = 0.5; // ORDER BY network_type, network, LENGTH(ref), ref) private static final Comparator RELATION_ORDERING = Comparator .comparingInt( @@ -658,7 +656,8 @@ public void process(Tables.OsmHighwayPolygon element, FeatureCollector features) } } - private List postProcessItems(int zoom, List items, double tolerance) { + @Override + public List postProcess(int zoom, List items) { double minLength = coalesce(MIN_LENGTH.apply(zoom), 0).doubleValue(); // don't merge road segments with oneway tag @@ -671,7 +670,8 @@ private List postProcessItems(int zoom, List postProcessItems(int zoom, List postProcessAllOrNonFerry(int zoom, List items) { - return postProcessItems(zoom, items, TOLERANCE); - } - - private List postProcessFerry(int zoom, List items) { - return postProcessItems(zoom, items, TOLERANCE); - } - - @Override - public List postProcess(int zoom, List items) { - if (zoom < 4 || zoom > 10) { - return postProcessAllOrNonFerry(zoom, items); - } else { - // ferries at Z4-Z10 need different treatment - List ferryItems = new ArrayList<>(); - List otherItems = new ArrayList<>(); - for (var item : items) { - if (FieldValues.CLASS_FERRY.equals(item.attrs().get(Fields.CLASS))) { - ferryItems.add(item); - } else { - otherItems.add(item); - } - } - var result = postProcessFerry(zoom, ferryItems); - result.addAll(postProcessAllOrNonFerry(zoom, otherItems)); - return result; - } - } - enum RouteNetwork { US_INTERSTATE("us-interstate"), From 43d16a903f36ce1dc162495b2863e6f372220c9f Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Fri, 3 Nov 2023 18:44:45 +0100 Subject: [PATCH 056/106] we need regenerate to work with master branch for now --- scripts/regenerate-openmaptiles.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scripts/regenerate-openmaptiles.sh b/scripts/regenerate-openmaptiles.sh index 54fb8d72..2597334f 100755 --- a/scripts/regenerate-openmaptiles.sh +++ b/scripts/regenerate-openmaptiles.sh @@ -4,7 +4,8 @@ set -o errexit set -o pipefail set -o nounset -TAG="${1:-"v3.14"}" +# TODO: change to "v3.15" once that is released +TAG="${1:-"master"}" echo "tag=${TAG}" BASE_URL="${2:-"https://raw.githubusercontent.com/openmaptiles/openmaptiles/"}" From 269575baa94fe40d2e2cc9ef012a2d28a8f82bec Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Fri, 3 Nov 2023 19:36:30 +0100 Subject: [PATCH 057/106] first sub-class search for agg_stop simplified a little --- src/main/java/org/openmaptiles/layers/Poi.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/openmaptiles/layers/Poi.java b/src/main/java/org/openmaptiles/layers/Poi.java index 70e4aa34..c8cbbad6 100644 --- a/src/main/java/org/openmaptiles/layers/Poi.java +++ b/src/main/java/org/openmaptiles/layers/Poi.java @@ -222,7 +222,7 @@ public void finish(String sourceName, FeatureCollector.Factory featureCollectors } try { // find first based on subclass - var firstSubclass = aggStopSet.stream().sorted(BY_SUBCLASS).toArray(Tables.OsmPoiPoint[]::new)[0].subclass(); + var firstSubclass = aggStopSet.stream().min(BY_SUBCLASS).get().subclass(); var topAggStops = aggStopSet.stream().filter(s -> firstSubclass.equals(s.subclass())).toArray(Tables.OsmPoiPoint[]::new); if (topAggStops.length <= 2) { From 45abdbe11bddfd5eed5a43a3b03e4b0eca0a20e7 Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Thu, 16 Nov 2023 14:28:34 +0100 Subject: [PATCH 058/106] contains() used instead of indexOf() for better readability --- src/main/java/org/openmaptiles/layers/Housenumber.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/openmaptiles/layers/Housenumber.java b/src/main/java/org/openmaptiles/layers/Housenumber.java index cebfb8a3..44487543 100644 --- a/src/main/java/org/openmaptiles/layers/Housenumber.java +++ b/src/main/java/org/openmaptiles/layers/Housenumber.java @@ -66,7 +66,7 @@ public class Housenumber implements ForwardingProfile.FeaturePostProcessor { private static final Logger LOGGER = LoggerFactory.getLogger(Housenumber.class); - private static final Character OSM_SEPARATOR = ';'; + private static final String OSM_SEPARATOR = ";"; private static final String DISPLAY_SEPARATOR = "–"; private static final Pattern NO_CONVERSION_PATTERN = Pattern.compile("[^0-9;]"); private final Stats stats; @@ -82,11 +82,11 @@ private static String displayHousenumberNonumeric(String[] numbers) { } protected static String displayHousenumber(String housenumber) { - if (housenumber.indexOf(OSM_SEPARATOR) < 0) { + if (!housenumber.contains(OSM_SEPARATOR)) { return housenumber; } - String[] numbers = Arrays.stream(housenumber.split(OSM_SEPARATOR.toString())) + String[] numbers = Arrays.stream(housenumber.split(OSM_SEPARATOR)) .filter(n -> !n.trim().isEmpty()) .toArray(String[]::new); if (numbers.length <= 0) { From 124d0001d650f71421955f39244c8623fb2102ad Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Thu, 16 Nov 2023 14:31:13 +0100 Subject: [PATCH 059/106] numbers as list, not array, so that getFirst() and getLast() can be used --- .../java/org/openmaptiles/layers/Housenumber.java | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/main/java/org/openmaptiles/layers/Housenumber.java b/src/main/java/org/openmaptiles/layers/Housenumber.java index 44487543..3c3c1410 100644 --- a/src/main/java/org/openmaptiles/layers/Housenumber.java +++ b/src/main/java/org/openmaptiles/layers/Housenumber.java @@ -75,10 +75,10 @@ public Housenumber(Translations translations, PlanetilerConfig config, Stats sta this.stats = stats; } - private static String displayHousenumberNonumeric(String[] numbers) { - return numbers[0] + private static String displayHousenumberNonumeric(List numbers) { + return numbers.getFirst() .concat(DISPLAY_SEPARATOR) - .concat(numbers[numbers.length - 1]); + .concat(numbers.getLast()); } protected static String displayHousenumber(String housenumber) { @@ -86,10 +86,10 @@ protected static String displayHousenumber(String housenumber) { return housenumber; } - String[] numbers = Arrays.stream(housenumber.split(OSM_SEPARATOR)) + List numbers = Arrays.stream(housenumber.split(OSM_SEPARATOR)) .filter(n -> !n.trim().isEmpty()) - .toArray(String[]::new); - if (numbers.length <= 0) { + .toList(); + if (numbers.size() <= 0) { // not much to do with strange/invalid entries like "3;" or ";" etc. return housenumber; } @@ -100,7 +100,7 @@ protected static String displayHousenumber(String housenumber) { } // numeric display house number - var statistics = Arrays.stream(numbers) + var statistics = numbers.stream() .collect(Collectors.summarizingInt(Integer::parseUnsignedInt)); return String.valueOf(statistics.getMin()) .concat(DISPLAY_SEPARATOR) From 91d5e827deee80efe731cdceb0bc6897d5358f4f Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Thu, 16 Nov 2023 14:35:38 +0100 Subject: [PATCH 060/106] better trimming and filtring of housenumbers --- src/main/java/org/openmaptiles/layers/Housenumber.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/openmaptiles/layers/Housenumber.java b/src/main/java/org/openmaptiles/layers/Housenumber.java index 3c3c1410..06943796 100644 --- a/src/main/java/org/openmaptiles/layers/Housenumber.java +++ b/src/main/java/org/openmaptiles/layers/Housenumber.java @@ -45,6 +45,7 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE import com.onthegomap.planetiler.util.Translations; import java.util.Arrays; import java.util.List; +import java.util.function.Predicate; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; @@ -87,7 +88,8 @@ protected static String displayHousenumber(String housenumber) { } List numbers = Arrays.stream(housenumber.split(OSM_SEPARATOR)) - .filter(n -> !n.trim().isEmpty()) + .map(String::trim) + .filter(Predicate.not(String::isEmpty)) .toList(); if (numbers.size() <= 0) { // not much to do with strange/invalid entries like "3;" or ";" etc. From d93ca1fb269f08ad054c8310d4b6877bf2deb1b0 Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Thu, 16 Nov 2023 14:53:40 +0100 Subject: [PATCH 061/106] adjusted handling of large house numbers --- src/main/java/org/openmaptiles/layers/Housenumber.java | 2 +- .../java/org/openmaptiles/layers/HousenumberTest.java | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/openmaptiles/layers/Housenumber.java b/src/main/java/org/openmaptiles/layers/Housenumber.java index 06943796..d220e2cc 100644 --- a/src/main/java/org/openmaptiles/layers/Housenumber.java +++ b/src/main/java/org/openmaptiles/layers/Housenumber.java @@ -103,7 +103,7 @@ protected static String displayHousenumber(String housenumber) { // numeric display house number var statistics = numbers.stream() - .collect(Collectors.summarizingInt(Integer::parseUnsignedInt)); + .collect(Collectors.summarizingLong(Long::parseUnsignedLong)); return String.valueOf(statistics.getMin()) .concat(DISPLAY_SEPARATOR) .concat(String.valueOf(statistics.getMax())); diff --git a/src/test/java/org/openmaptiles/layers/HousenumberTest.java b/src/test/java/org/openmaptiles/layers/HousenumberTest.java index e4083203..1aec64d8 100644 --- a/src/test/java/org/openmaptiles/layers/HousenumberTest.java +++ b/src/test/java/org/openmaptiles/layers/HousenumberTest.java @@ -5,6 +5,8 @@ import java.util.List; import java.util.Map; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; class HousenumberTest extends AbstractLayerTest { @@ -68,4 +70,12 @@ void testDisplayHousenumberExtraBroken() { final String HOUSENUMBER_2 = ";;"; assertEquals(HOUSENUMBER_2, Housenumber.displayHousenumber(HOUSENUMBER_2)); } + + @ParameterizedTest + @CsvSource({ + "2712;935803935803, 2712–935803935803", + }) + void testDisplayHousenumberOutliers(String outlier, String expected) { + assertEquals(expected, Housenumber.displayHousenumber(outlier)); + } } From 8380e2f1098b868ae2a352e59bdb19134f12a9fc Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Thu, 16 Nov 2023 14:59:47 +0100 Subject: [PATCH 062/106] several unit tests collapsed to one with @ParameterizedTest + @CsvSource --- .../openmaptiles/layers/HousenumberTest.java | 48 ++++--------------- 1 file changed, 8 insertions(+), 40 deletions(-) diff --git a/src/test/java/org/openmaptiles/layers/HousenumberTest.java b/src/test/java/org/openmaptiles/layers/HousenumberTest.java index 1aec64d8..7a5be879 100644 --- a/src/test/java/org/openmaptiles/layers/HousenumberTest.java +++ b/src/test/java/org/openmaptiles/layers/HousenumberTest.java @@ -32,50 +32,18 @@ void testHousenumber() { )))); } - @Test - void testDisplayHousenumberNonRange() { - final String HOUSENUMBER = "1"; - assertEquals(HOUSENUMBER, Housenumber.displayHousenumber(HOUSENUMBER)); - } - - @Test - void testDisplayHousenumberNonnumericRange() { - final String HOUSENUMBER = "1;1a;2;2/b;20;3"; - assertEquals("1–3", Housenumber.displayHousenumber(HOUSENUMBER)); - } - - @Test - void testDisplayHousenumberNonnumericRangeBroken() { - final String HOUSENUMBER = "1;1a;2;2/b;20;3;"; - assertEquals("1–3", Housenumber.displayHousenumber(HOUSENUMBER)); - } - - @Test - void testDisplayHousenumberNumericRange() { - final String HOUSENUMBER = "1;2;20;3"; - assertEquals("1–20", Housenumber.displayHousenumber(HOUSENUMBER)); - } - - @Test - void testDisplayHousenumberNumericRangeBroken() { - final String HOUSENUMBER = "1;2;20;3;"; - assertEquals("1–20", Housenumber.displayHousenumber(HOUSENUMBER)); - } - - @Test - void testDisplayHousenumberExtraBroken() { - final String HOUSENUMBER_1 = ";"; - assertEquals(HOUSENUMBER_1, Housenumber.displayHousenumber(HOUSENUMBER_1)); - - final String HOUSENUMBER_2 = ";;"; - assertEquals(HOUSENUMBER_2, Housenumber.displayHousenumber(HOUSENUMBER_2)); - } - @ParameterizedTest @CsvSource({ + "1, 1", + "1;1a;2;2/b;20;3, 1–3", + "1;1a;2;2/b;20;3;, 1–3", + "1;2;20;3, 1–20", + "1;2;20;3;, 1–20", + ";, ;", + ";;, ;;", "2712;935803935803, 2712–935803935803", }) - void testDisplayHousenumberOutliers(String outlier, String expected) { + void testDisplayHousenumber(String outlier, String expected) { assertEquals(expected, Housenumber.displayHousenumber(outlier)); } } From 1a9a333d2149744145323ed73488275c0425eea3 Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Thu, 16 Nov 2023 15:03:25 +0100 Subject: [PATCH 063/106] AGG_STOP_SUBCLASS_ORDER simplified from Map to List --- src/main/java/org/openmaptiles/layers/Poi.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/openmaptiles/layers/Poi.java b/src/main/java/org/openmaptiles/layers/Poi.java index c8cbbad6..a0cb45ed 100644 --- a/src/main/java/org/openmaptiles/layers/Poi.java +++ b/src/main/java/org/openmaptiles/layers/Poi.java @@ -114,14 +114,14 @@ public class Poi implements entry(FieldValues.CLASS_BAR, 800) ); private static final Set UNIVERSITY_POI_SUBCLASSES = Set.of("university", "college"); - private static final Map AGG_STOP_SUBCLASS_ORDER = Map.ofEntries( - entry("subway", 0), - entry("tram_stop", 1), - entry("bus_station", 2), - entry("bus_stop", 3) + private static final List AGG_STOP_SUBCLASS_ORDER = List.of( + "subway", + "tram_stop", + "bus_station", + "bus_stop" ); private static final Comparator BY_SUBCLASS = Comparator - .comparingInt(s -> AGG_STOP_SUBCLASS_ORDER.get(s.subclass())); + .comparingInt(s -> AGG_STOP_SUBCLASS_ORDER.indexOf(s.subclass())); private static final double LOG2 = Math.log(2); private static final double SQRT10 = Math.sqrt(10); private final MultiExpression.Index classMapping; From 9886cb15aa055753a1aa48588c2c4357fe2d549a Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Thu, 16 Nov 2023 15:07:40 +0100 Subject: [PATCH 064/106] fixed major omission from previous commit --- src/main/java/org/openmaptiles/layers/Poi.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/openmaptiles/layers/Poi.java b/src/main/java/org/openmaptiles/layers/Poi.java index a0cb45ed..56020638 100644 --- a/src/main/java/org/openmaptiles/layers/Poi.java +++ b/src/main/java/org/openmaptiles/layers/Poi.java @@ -173,7 +173,7 @@ public void release() { @Override public void process(Tables.OsmPoiPoint element, FeatureCollector features) { - if (element.uicRef() != null && AGG_STOP_SUBCLASS_ORDER.containsKey(element.subclass())) { + if (element.uicRef() != null && AGG_STOP_SUBCLASS_ORDER.contains(element.subclass())) { // multiple threads may update this concurrently synchronized (this) { aggStops.computeIfAbsent(element.uicRef(), key -> new ArrayList<>()).add(element); From 654e08ade93c981126eb49062675ae0757c997fb Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Thu, 16 Nov 2023 15:14:03 +0100 Subject: [PATCH 065/106] clamp() used to replace min()&max() combo --- src/main/java/org/openmaptiles/layers/Park.java | 2 +- src/main/java/org/openmaptiles/layers/Place.java | 4 ++-- src/main/java/org/openmaptiles/layers/Poi.java | 2 +- src/main/java/org/openmaptiles/util/Utils.java | 2 +- src/test/java/org/openmaptiles/layers/PoiTest.java | 2 +- src/test/java/org/openmaptiles/layers/TransportationTest.java | 4 ++-- src/test/java/org/openmaptiles/layers/WaterNameTest.java | 2 +- 7 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/main/java/org/openmaptiles/layers/Park.java b/src/main/java/org/openmaptiles/layers/Park.java index e862021f..026f1f62 100644 --- a/src/main/java/org/openmaptiles/layers/Park.java +++ b/src/main/java/org/openmaptiles/layers/Park.java @@ -143,7 +143,7 @@ private int getMinZoomForArea(double area) { // sql filter: area > 70000*2^(20-zoom_level) // simplifies to: zoom_level > 20 - log(area / 70000) / log(2) int minzoom = (int) Math.floor(20 - Math.log(area / WORLD_AREA_FOR_70K_SQUARE_METERS) / LOG2); - minzoom = Math.min(14, Math.max(5, minzoom)); + minzoom = Math.clamp(minzoom, 5, 14); return minzoom; } diff --git a/src/main/java/org/openmaptiles/layers/Place.java b/src/main/java/org/openmaptiles/layers/Place.java index e9e58edb..ed1e5221 100644 --- a/src/main/java/org/openmaptiles/layers/Place.java +++ b/src/main/java/org/openmaptiles/layers/Place.java @@ -235,7 +235,7 @@ public void process(Tables.OsmCountryPoint element, FeatureCollector features) { rank = country.rank; } - rank = Math.max(1, Math.min(6, rank)); + rank = Math.clamp(rank, 1, 6); features.point(LAYER_NAME).setBufferPixels(BUFFER_SIZE) .putAttrs(names) @@ -261,7 +261,7 @@ public void process(Tables.OsmStatePoint element, FeatureCollector features) { if (nullOrEmpty(names.get(Fields.NAME_EN))) { names.put(Fields.NAME_EN, state.name); } - int rank = Math.min(6, Math.max(1, state.rank)); + int rank = Math.clamp(state.rank, 1, 6); features.point(LAYER_NAME).setBufferPixels(BUFFER_SIZE) .putAttrs(names) diff --git a/src/main/java/org/openmaptiles/layers/Poi.java b/src/main/java/org/openmaptiles/layers/Poi.java index 56020638..eb82874a 100644 --- a/src/main/java/org/openmaptiles/layers/Poi.java +++ b/src/main/java/org/openmaptiles/layers/Poi.java @@ -163,7 +163,7 @@ public static int uniAreaToMinZoom(double areaWorld) { // And to accommodate for some precision errors (observed for Z9-Z11) we do also `- 0.1e-10`. int result = (int) Math.floor(zoom - 0.1e-10) + 1; - return Math.min(14, Math.max(10, result)); + return Math.clamp(result, 10, 14); } @Override diff --git a/src/main/java/org/openmaptiles/util/Utils.java b/src/main/java/org/openmaptiles/util/Utils.java index 31936057..830e0329 100644 --- a/src/main/java/org/openmaptiles/util/Utils.java +++ b/src/main/java/org/openmaptiles/util/Utils.java @@ -114,6 +114,6 @@ public static int getMinZoomForLength(double length, double threshold) { * @return minzoom for a feature with given length and given threshold clipped to not exceed given minimum and maximum */ public static int getClippedMinZoomForLength(double length, double threshold, int min, int max) { - return Math.min(max, Math.max(min, getMinZoomForLength(length, threshold))); + return Math.clamp(getMinZoomForLength(length, threshold), min, max); } } diff --git a/src/test/java/org/openmaptiles/layers/PoiTest.java b/src/test/java/org/openmaptiles/layers/PoiTest.java index f4e10ced..5d95c368 100644 --- a/src/test/java/org/openmaptiles/layers/PoiTest.java +++ b/src/test/java/org/openmaptiles/layers/PoiTest.java @@ -447,7 +447,7 @@ private void createUniAreaForMinZoomTest(List testEntries, double sid )); testEntries.add(new TestEntry( feature, - Math.min(14, Math.max(10, expectedZoom)) + Math.clamp(expectedZoom, 10, 14) )); } diff --git a/src/test/java/org/openmaptiles/layers/TransportationTest.java b/src/test/java/org/openmaptiles/layers/TransportationTest.java index 7ae1db23..fdc4d2fd 100644 --- a/src/test/java/org/openmaptiles/layers/TransportationTest.java +++ b/src/test/java/org/openmaptiles/layers/TransportationTest.java @@ -2028,7 +2028,7 @@ private void createBrunnelForMinZoomTest(List testEntries, double len )); testEntries.add(new TestEntry( feature, - Math.min(12, Math.max(9, expectedZoom)) + Math.clamp(expectedZoom, 9, 12) )); } @@ -2075,7 +2075,7 @@ private void createFerryForMinZoomTest(List testEntries, double lengt )); testEntries.add(new TestEntry( feature, - Math.min(11, Math.max(4, expectedZoom)) + Math.clamp(expectedZoom, 4, 11) )); } diff --git a/src/test/java/org/openmaptiles/layers/WaterNameTest.java b/src/test/java/org/openmaptiles/layers/WaterNameTest.java index fee396b1..9f43c545 100644 --- a/src/test/java/org/openmaptiles/layers/WaterNameTest.java +++ b/src/test/java/org/openmaptiles/layers/WaterNameTest.java @@ -270,7 +270,7 @@ private void createAreaForMinZoomTest(List testEntries, double side, )); testEntries.add(new TestEntry( feature, - Math.min(14, Math.max(3, expectedZoom)) + Math.clamp(expectedZoom, 3, 14) )); } From 1f64f5f2c49ff4d663e0a12cf1631589ea3106f2 Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Thu, 16 Nov 2023 15:15:27 +0100 Subject: [PATCH 066/106] agg_stop now implemented --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 90ae11ff..37d2261e 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,6 @@ available options. ## Differences from OpenMapTiles - Road name abbreviations are not implemented yet in the `transportation_name` layer -- `agg_stop` tag not implemented yet in the `poi` layer - `brunnel` tag is excluded from `transportation_name` layer to avoid breaking apart long `transportation_name` lines, to revert this behavior set `--transportation-name-brunnel=true` - `rank` field on `mountain_peak` linestrings only has 3 levels (1: has wikipedia page and name, 2: has name, 3: no name From 0f8487e17c2f6057588cc60c00880d6ddef37423 Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Thu, 16 Nov 2023 15:22:22 +0100 Subject: [PATCH 067/106] fixed typo in the error message --- src/main/java/org/openmaptiles/layers/Poi.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/openmaptiles/layers/Poi.java b/src/main/java/org/openmaptiles/layers/Poi.java index eb82874a..d659b848 100644 --- a/src/main/java/org/openmaptiles/layers/Poi.java +++ b/src/main/java/org/openmaptiles/layers/Poi.java @@ -333,7 +333,7 @@ private Date: Thu, 16 Nov 2023 15:27:20 +0100 Subject: [PATCH 068/106] prepare IE and GB boundary geometry outside of synchronized{} --- .../openmaptiles/layers/Transportation.java | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/main/java/org/openmaptiles/layers/Transportation.java b/src/main/java/org/openmaptiles/layers/Transportation.java index 1a6d9411..550b42fb 100644 --- a/src/main/java/org/openmaptiles/layers/Transportation.java +++ b/src/main/java/org/openmaptiles/layers/Transportation.java @@ -261,22 +261,22 @@ public void processNaturalEarth(String table, SourceFeature feature, // multiple threads call this method concurrently, GB (or IE) polygon *should* only be found // once, but just to be safe synchronize updates to that field if (feature.hasTag("iso_a2", "GB")) { - synchronized (this) { - try { - Geometry boundary = feature.polygon().buffer(GeoUtils.metersToPixelAtEquator(0, 10_000) / 256d); + try { + Geometry boundary = feature.polygon().buffer(GeoUtils.metersToPixelAtEquator(0, 10_000) / 256d); + synchronized (this) { greatBritain = PreparedGeometryFactory.prepare(boundary); - } catch (GeometryException e) { - LOGGER.error("Failed to get Great Britain Polygon: " + e); } + } catch (GeometryException e) { + LOGGER.error("Failed to get Great Britain Polygon: " + e); } } else if (feature.hasTag("iso_a2", "IE")) { - synchronized (this) { - try { - Geometry boundary = feature.polygon().buffer(GeoUtils.metersToPixelAtEquator(0, 10_000) / 256d); + try { + Geometry boundary = feature.polygon().buffer(GeoUtils.metersToPixelAtEquator(0, 10_000) / 256d); + synchronized (this) { ireland = PreparedGeometryFactory.prepare(boundary); - } catch (GeometryException e) { - LOGGER.error("Failed to get Great Britain Polygon: " + e); } + } catch (GeometryException e) { + LOGGER.error("Failed to get Great Britain Polygon: " + e); } } } From 99704bfcb5884cc16f426b2a5795e7be39c87479 Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Thu, 16 Nov 2023 15:27:45 +0100 Subject: [PATCH 069/106] fixed typo in the error message --- src/main/java/org/openmaptiles/layers/Transportation.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/openmaptiles/layers/Transportation.java b/src/main/java/org/openmaptiles/layers/Transportation.java index 550b42fb..3fa25886 100644 --- a/src/main/java/org/openmaptiles/layers/Transportation.java +++ b/src/main/java/org/openmaptiles/layers/Transportation.java @@ -276,7 +276,7 @@ public void processNaturalEarth(String table, SourceFeature feature, ireland = PreparedGeometryFactory.prepare(boundary); } } catch (GeometryException e) { - LOGGER.error("Failed to get Great Britain Polygon: " + e); + LOGGER.error("Failed to get Ireland Polygon: " + e); } } } From eb46f8659e179af11dbdbeda2d96d39c95dd5f6d Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Thu, 16 Nov 2023 16:01:25 +0100 Subject: [PATCH 070/106] mvn spotless:apply --- .../org/openmaptiles/layers/HousenumberTest.java | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/test/java/org/openmaptiles/layers/HousenumberTest.java b/src/test/java/org/openmaptiles/layers/HousenumberTest.java index 7a5be879..5d211b08 100644 --- a/src/test/java/org/openmaptiles/layers/HousenumberTest.java +++ b/src/test/java/org/openmaptiles/layers/HousenumberTest.java @@ -34,14 +34,14 @@ void testHousenumber() { @ParameterizedTest @CsvSource({ - "1, 1", - "1;1a;2;2/b;20;3, 1–3", - "1;1a;2;2/b;20;3;, 1–3", - "1;2;20;3, 1–20", - "1;2;20;3;, 1–20", - ";, ;", - ";;, ;;", - "2712;935803935803, 2712–935803935803", + "1, 1", + "1;1a;2;2/b;20;3, 1–3", + "1;1a;2;2/b;20;3;, 1–3", + "1;2;20;3, 1–20", + "1;2;20;3;, 1–20", + ";, ;", + ";;, ;;", + "2712;935803935803, 2712–935803935803", }) void testDisplayHousenumber(String outlier, String expected) { assertEquals(expected, Housenumber.displayHousenumber(outlier)); From bfaa6763b117c66e3e5f058ff97b0085a9a219dc Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Thu, 16 Nov 2023 16:04:04 +0100 Subject: [PATCH 071/106] switch statements for IE and GB route networks simplified --- .../openmaptiles/layers/Transportation.java | 83 +++++++------------ 1 file changed, 30 insertions(+), 53 deletions(-) diff --git a/src/main/java/org/openmaptiles/layers/Transportation.java b/src/main/java/org/openmaptiles/layers/Transportation.java index 3fa25886..b9e133a5 100644 --- a/src/main/java/org/openmaptiles/layers/Transportation.java +++ b/src/main/java/org/openmaptiles/layers/Transportation.java @@ -355,28 +355,15 @@ List getRouteRelations(Tables.OsmHighwayLinestring element) { try { Geometry wayGeometry = element.source().worldGeometry(); if (greatBritain.intersects(wayGeometry)) { - Transportation.RouteNetwork networkType; - String network; - switch (element.highway()) { - case "motorway" -> { - networkType = Transportation.RouteNetwork.GB_MOTORWAY; - network = "omt-gb-motorway"; - } - case "trunk" -> { - networkType = RouteNetwork.GB_TRUNK; - network = "omt-gb-trunk"; - } - case "primary", "secondary" -> { - networkType = RouteNetwork.GB_PRIMARY; - network = "omt-gb-primary"; - } - default -> { - networkType = null; - network = null; - } - } - result.add(new RouteRelation(refMatcher.group(), network, networkType, (byte) -1, - 0)); + Transportation.RouteNetwork networkType = switch (element.highway()) { + case "motorway" -> Transportation.RouteNetwork.GB_MOTORWAY; + case "trunk" -> RouteNetwork.GB_TRUNK; + case "primary", "secondary" -> RouteNetwork.GB_PRIMARY; + default -> null; + }; + result.add(new RouteRelation(refMatcher.group(), + networkType == null ? null : networkType.network, + networkType, (byte) -1, 0)); } } catch (GeometryException e) { e.log(stats, "omt_transportation_name_gb_test", @@ -395,25 +382,13 @@ List getRouteRelations(Tables.OsmHighwayLinestring element) { try { Geometry wayGeometry = element.source().worldGeometry(); if (ireland.intersects(wayGeometry)) { - Transportation.RouteNetwork networkType; - String network; String highway = coalesce(element.highway(), ""); - switch (highway) { - case "motorway" -> { - networkType = Transportation.RouteNetwork.IE_MOTORWAY; - network = "omt-ie-motorway"; - } - case "trunk", "primary" -> { - networkType = RouteNetwork.IE_NATIONAL; - network = "omt-ie-national"; - } - default -> { - networkType = RouteNetwork.IE_REGIONAL; - network = "omt-ie-regional"; - } - } - result.add(new RouteRelation(refMatcher.group(), network, networkType, (byte) -1, - 0)); + Transportation.RouteNetwork networkType = switch (highway) { + case "motorway" -> Transportation.RouteNetwork.IE_MOTORWAY; + case "trunk", "primary" -> RouteNetwork.IE_NATIONAL; + default -> RouteNetwork.IE_REGIONAL; + }; + result.add(new RouteRelation(refMatcher.group(), networkType.network, networkType, (byte) -1, 0)); } } catch (GeometryException e) { e.log(stats, "omt_transportation_name_ie_test", @@ -681,23 +656,25 @@ public List postProcess(int zoom, List i enum RouteNetwork { - US_INTERSTATE("us-interstate"), - US_HIGHWAY("us-highway"), - US_STATE("us-state"), - CA_TRANSCANADA("ca-transcanada"), - CA_PROVINCIAL_ARTERIAL("ca-provincial-arterial"), - CA_PROVINCIAL("ca-provincial"), - GB_MOTORWAY("gb-motorway"), - GB_TRUNK("gb-trunk"), - GB_PRIMARY("gb-primary"), - IE_MOTORWAY("ie-motorway"), - IE_NATIONAL("ie-national"), - IE_REGIONAL("ie-regional"); + US_INTERSTATE("us-interstate", null), + US_HIGHWAY("us-highway", null), + US_STATE("us-state", null), + CA_TRANSCANADA("ca-transcanada", null), + CA_PROVINCIAL_ARTERIAL("ca-provincial-arterial", null), + CA_PROVINCIAL("ca-provincial", null), + GB_MOTORWAY("gb-motorway", "omt-gb-motorway"), + GB_TRUNK("gb-trunk", "omt-gb-trunk"), + GB_PRIMARY("gb-primary", "omt-gb-primary"), + IE_MOTORWAY("ie-motorway", "omt-ie-motorway"), + IE_NATIONAL("ie-national", "omt-ie-national"), + IE_REGIONAL("ie-regional", "omt-ie-regional"); final String name; + final String network; - RouteNetwork(String name) { + RouteNetwork(String name, String network) { this.name = name; + this.network = network; } } From 4374adfef9c08cb80c8ee70e1fc5b997c6ca21fa Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Thu, 16 Nov 2023 16:42:42 +0100 Subject: [PATCH 072/106] avoid RouteNetwork->String mapping, not needed for anyMatch() --- .../java/org/openmaptiles/layers/Transportation.java | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/openmaptiles/layers/Transportation.java b/src/main/java/org/openmaptiles/layers/Transportation.java index b9e133a5..f7733653 100644 --- a/src/main/java/org/openmaptiles/layers/Transportation.java +++ b/src/main/java/org/openmaptiles/layers/Transportation.java @@ -140,10 +140,10 @@ public class Transportation implements private static final Set ACCESS_NO_VALUES = Set.of( "private", "no" ); - private static final Set TRUNK_AS_MOTORWAY_BY_NETWORK = Set.of( - RouteNetwork.CA_TRANSCANADA.toString(), - RouteNetwork.CA_PROVINCIAL_ARTERIAL.toString(), - RouteNetwork.US_INTERSTATE.toString() + private static final Set TRUNK_AS_MOTORWAY_BY_NETWORK = Set.of( + RouteNetwork.CA_TRANSCANADA, + RouteNetwork.CA_PROVINCIAL_ARTERIAL, + RouteNetwork.US_INTERSTATE ); private static final Set CA_AB_PRIMARY_AS_ARTERIAL_BY_REF = Set.of( "2", "3", "4" @@ -497,7 +497,6 @@ int getMinzoom(Tables.OsmHighwayLinestring element, String highwayClass) { String clazz = routeRelations.stream() .map(RouteRelation::networkType) .filter(Objects::nonNull) - .map(Enum::toString) .anyMatch(TRUNK_AS_MOTORWAY_BY_NETWORK::contains) ? FieldValues.CLASS_MOTORWAY : FieldValues.CLASS_TRUNK; yield MINZOOMS.getOrDefault(clazz, Integer.MAX_VALUE); } From 371206cf3e63d2121f07a0628f29caa6e9be3788 Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Thu, 16 Nov 2023 17:22:30 +0100 Subject: [PATCH 073/106] fix: attr. brunnel optional based on size on Z4-Z11, attr. layer optional between Z9-Z11 --- src/main/java/org/openmaptiles/layers/Transportation.java | 7 ++++--- .../java/org/openmaptiles/layers/TransportationTest.java | 4 ++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/openmaptiles/layers/Transportation.java b/src/main/java/org/openmaptiles/layers/Transportation.java index f7733653..72e36201 100644 --- a/src/main/java/org/openmaptiles/layers/Transportation.java +++ b/src/main/java/org/openmaptiles/layers/Transportation.java @@ -427,6 +427,7 @@ public void process(Tables.OsmHighwayLinestring element, FeatureCollector featur String brunnelValue = brunnel(element.isBridge(), element.isTunnel(), element.isFord()); int brunnelMinzoom = brunnelValue != null ? getBrunnelMinzoom(element) : minzoom; + int layerMinzoom = Math.max(brunnelMinzoom, 9); if (minzoom > config.maxzoom()) { return; @@ -445,7 +446,7 @@ public void process(Tables.OsmHighwayLinestring element, FeatureCollector featur // z8+ .setAttrWithMinzoom(Fields.EXPRESSWAY, element.expressway() && !"motorway".equals(highway) ? 1 : null, 8) // z9+ - .setAttrWithMinzoom(Fields.LAYER, nullIfLong(element.layer(), 0), brunnelValue == null ? 9 : brunnelMinzoom) + .setAttrWithMinzoom(Fields.LAYER, nullIfLong(element.layer(), 0), layerMinzoom) .setAttrWithMinzoom(Fields.BICYCLE, nullIfEmpty(element.bicycle()), 9) .setAttrWithMinzoom(Fields.FOOT, nullIfEmpty(element.foot()), 9) .setAttrWithMinzoom(Fields.HORSE, nullIfEmpty(element.horse()), 9) @@ -512,11 +513,11 @@ int getMinzoom(Tables.OsmHighwayLinestring element, String highwayClass) { int getBrunnelMinzoom(Tables.OsmHighwayLinestring element) { try { - return Utils.getClippedMinZoomForLength(element.source().length(), 6, 9, 12); + return Utils.getClippedMinZoomForLength(element.source().length(), 6, 4, 12); } catch (GeometryException e) { e.log(stats, "omt_brunnel_minzoom", "Unable to calculate brunnel minzoom for " + element.source().id()); - // brunnel is optional (depends on feature size) for Z9-Z11, it is always present for Z12+, hence 12 as fallback + // brunnel is optional (depends on feature size) for Z4-Z11, it is always present for Z12+, hence 12 as fallback return 12; } } diff --git a/src/test/java/org/openmaptiles/layers/TransportationTest.java b/src/test/java/org/openmaptiles/layers/TransportationTest.java index fdc4d2fd..202f13ee 100644 --- a/src/test/java/org/openmaptiles/layers/TransportationTest.java +++ b/src/test/java/org/openmaptiles/layers/TransportationTest.java @@ -302,7 +302,7 @@ void testInterstateMotorway() { "bicycle", "", "foot", "", "horse", "", - "brunnel", "", + "brunnel", "bridge", "_minzoom", 4 ), Map.of( "_layer", "transportation_name", @@ -2028,7 +2028,7 @@ private void createBrunnelForMinZoomTest(List testEntries, double len )); testEntries.add(new TestEntry( feature, - Math.clamp(expectedZoom, 9, 12) + Math.clamp(expectedZoom, 4, 12) )); } From e21c74ae7059432df1d504ed23a05d009d939ea2 Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Thu, 16 Nov 2023 17:58:40 +0100 Subject: [PATCH 074/106] tolerance change in transportation reverted, added note to README as per why --- README.md | 1 + src/main/java/org/openmaptiles/layers/Transportation.java | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 37d2261e..f6f4977b 100644 --- a/README.md +++ b/README.md @@ -30,6 +30,7 @@ available options. lines, to revert this behavior set `--transportation-name-brunnel=true` - `rank` field on `mountain_peak` linestrings only has 3 levels (1: has wikipedia page and name, 2: has name, 3: no name or wikipedia page or name) +- some line and polygon tolerances are different, can be tweaked with `--simplify-tolerance` parameter ## Customizing diff --git a/src/main/java/org/openmaptiles/layers/Transportation.java b/src/main/java/org/openmaptiles/layers/Transportation.java index 72e36201..d96ee39c 100644 --- a/src/main/java/org/openmaptiles/layers/Transportation.java +++ b/src/main/java/org/openmaptiles/layers/Transportation.java @@ -633,6 +633,7 @@ public void process(Tables.OsmHighwayPolygon element, FeatureCollector features) @Override public List postProcess(int zoom, List items) { + double tolerance = config.tolerance(zoom); double minLength = coalesce(MIN_LENGTH.apply(zoom), 0).doubleValue(); // don't merge road segments with oneway tag @@ -645,8 +646,7 @@ public List postProcess(int zoom, List i } } - // "shipway_linestring_gen_z5: ... tolerance: ZRES6", etc. when recalculated from meters to pixels is always 0.5 - var merged = FeatureMerge.mergeLineStrings(items, minLength, 0.5, BUFFER_SIZE); + var merged = FeatureMerge.mergeLineStrings(items, minLength, tolerance, BUFFER_SIZE); for (var item : merged) { item.attrs().remove(LIMIT_MERGE_TAG); From f4994678e8ae9f04bec2fb1ed1acfab0f7c622a4 Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Thu, 16 Nov 2023 18:23:13 +0100 Subject: [PATCH 075/106] fix: monzoom for sea&co. is Z0-Z14 based on area, for the rest it is Z3-Z14 again based on area --- src/main/java/org/openmaptiles/layers/WaterName.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/openmaptiles/layers/WaterName.java b/src/main/java/org/openmaptiles/layers/WaterName.java index 29d2f5bf..470d2851 100644 --- a/src/main/java/org/openmaptiles/layers/WaterName.java +++ b/src/main/java/org/openmaptiles/layers/WaterName.java @@ -208,9 +208,9 @@ public void process(Tables.OsmWaterPolygon element, FeatureCollector features) { feature = features.pointOnSurface(LAYER_NAME); double area = element.source().area(); if (place != null && SEA_OR_OCEAN_PLACE.contains(place)) { - minzoom = 0; + minzoom = areaToMinZoom(area, 0); } else { - minzoom = areaToMinZoom(area); + minzoom = areaToMinZoom(area, 3); } } feature @@ -225,9 +225,9 @@ public void process(Tables.OsmWaterPolygon element, FeatureCollector features) { } } - public static int areaToMinZoom(double areaWorld) { + public static int areaToMinZoom(double areaWorld, int minLimit) { double oneSideWorld = Math.sqrt(areaWorld); // OMT does "feature area is 1/4 of tile area", which is same as "feature side is 1/2 of tile side" - return Utils.getClippedMinZoomForLength(oneSideWorld, 1, 3, 14); + return Utils.getClippedMinZoomForLength(oneSideWorld, 1, minLimit, 14); } } From caa41e124ed464f9595312611f1bff566080d150 Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Thu, 16 Nov 2023 18:35:11 +0100 Subject: [PATCH 076/106] clean-up: avoid doing area->side->area, do just area --- src/test/java/org/openmaptiles/layers/PoiTest.java | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/test/java/org/openmaptiles/layers/PoiTest.java b/src/test/java/org/openmaptiles/layers/PoiTest.java index 5d95c368..9e25237c 100644 --- a/src/test/java/org/openmaptiles/layers/PoiTest.java +++ b/src/test/java/org/openmaptiles/layers/PoiTest.java @@ -439,8 +439,7 @@ private record TestEntry( int expectedZoom ) {} - private void createUniAreaForMinZoomTest(List testEntries, double side, int expectedZoom, String name) { - double area = Math.pow(side, 2); + private void createUniAreaForMinZoomTest(List testEntries, double area, int expectedZoom, String name) { var feature = polygonFeatureWithArea(area, Map.of( "name", name, "amenity", "university" @@ -458,19 +457,18 @@ void testUniAreaToMinZoom() throws GeometryException { //final double PORTION_OF_TILE_SIDE = (256d / Math.sqrt(10)) / Math.pow(2d, 14d + 8d); // ... and then for some lower zoom: //double testAreaSide = PORTION_OF_TILE_SIDE * Math.pow(2, 14 - zoom); - // all this then simplified to `testAreaSide` calculation bellow + // all this then simplified to `testArea` calculation bellow - final double SQRT10 = Math.sqrt(10); final List testEntries = new ArrayList<>(); for (int zoom = 14; zoom >= 0; zoom--) { - double testAreaSide = Math.pow(2, -zoom) / SQRT10; + double testArea = Math.pow(4, -zoom) / 10; // slightly bellow the threshold - createUniAreaForMinZoomTest(testEntries, testAreaSide * 0.999, zoom + 1, "uni-"); + createUniAreaForMinZoomTest(testEntries, testArea * 0.999, zoom + 1, "uni-"); // precisely at the threshold - createUniAreaForMinZoomTest(testEntries, testAreaSide, zoom, "uni="); + createUniAreaForMinZoomTest(testEntries, testArea, zoom, "uni="); // slightly over the threshold - createUniAreaForMinZoomTest(testEntries, testAreaSide * 1.001, zoom, "uni+"); + createUniAreaForMinZoomTest(testEntries, testArea * 1.001, zoom, "uni+"); } for (var entry : testEntries) { From b9db64a3d277ce79f7bea662efc8e1be066f2002 Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Thu, 16 Nov 2023 20:55:40 +0100 Subject: [PATCH 077/106] regenerate-openmaptiles.sh 6c31841f4674f15e15afde346a060cf7c22e6cdd (to match content of OMT PR 1591) --- .../generated/OpenMapTilesSchema.java | 55 ++++++++++--------- .../org/openmaptiles/generated/Tables.java | 25 ++++----- 2 files changed, 39 insertions(+), 41 deletions(-) diff --git a/src/main/java/org/openmaptiles/generated/OpenMapTilesSchema.java b/src/main/java/org/openmaptiles/generated/OpenMapTilesSchema.java index 55ec4e54..3045438d 100644 --- a/src/main/java/org/openmaptiles/generated/OpenMapTilesSchema.java +++ b/src/main/java/org/openmaptiles/generated/OpenMapTilesSchema.java @@ -48,9 +48,9 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE import org.openmaptiles.Layer; /** - * All vector tile layer definitions, attributes, and allowed values generated from the - * OpenMapTiles vector tile schema - * master. + * All vector tile layer definitions, attributes, and allowed values generated from the OpenMapTiles + * vector tile schema 6c31841f4674f15e15afde346a060cf7c22e6cdd. */ @SuppressWarnings("unused") public class OpenMapTilesSchema { @@ -95,8 +95,8 @@ public static List createInstances(Translations translations, PlanetilerC * polygons to improve rendering performance. This however can lead to less rendering options in clients since these * boundaries show up. So you might not be able to use border styling for ocean water features. * - * Generated from - * water.yaml + * Generated from water.yaml */ public interface Water extends Layer { double BUFFER_SIZE = 4.0; @@ -194,8 +194,8 @@ final class FieldMappings { * there is also canal generated, starting z13 there is no generalization according to class * field applied. Waterways do not have a subclass field. * - * Generated from - * waterway.yaml + * Generated from waterway.yaml */ public interface Waterway extends Layer { double BUFFER_SIZE = 4.0; @@ -280,7 +280,7 @@ final class FieldMappings { * layer is to style wood (class=wood) and grass (class=grass) areas. * * Generated from landcover.yaml + * "https://github.com/openmaptiles/openmaptiles/blob/6c31841f4674f15e15afde346a060cf7c22e6cdd/layers/landcover/landcover.yaml">landcover.yaml */ public interface Landcover extends Layer { double BUFFER_SIZE = 4.0; @@ -436,8 +436,8 @@ final class FieldMappings { * Landuse is used to describe use of land by humans. At lower zoom levels this is from Natural Earth data for * residential (urban) areas and at higher zoom levels mostly OSM landuse tags. * - * Generated from - * landuse.yaml + * Generated from landuse.yaml */ public interface Landuse extends Layer { double BUFFER_SIZE = 4.0; @@ -533,7 +533,7 @@ final class FieldMappings { * Natural peaks * * Generated from mountain_peak.yaml + * "https://github.com/openmaptiles/openmaptiles/blob/6c31841f4674f15e15afde346a060cf7c22e6cdd/layers/mountain_peak/mountain_peak.yaml">mountain_peak.yaml */ public interface MountainPeak extends Layer { double BUFFER_SIZE = 64.0; @@ -611,8 +611,8 @@ final class FieldMappings { * Indigenous boundaries are not parks, but they are included in this layer for technical reasons related to data * processing. These boundaries represent areas with special legal and administrative status for indigenous peoples. * - * Generated from - * park.yaml + * Generated from park.yaml */ public interface Park extends Layer { double BUFFER_SIZE = 4.0; @@ -672,8 +672,8 @@ final class FieldMappings { * admin_level * but for most styles it makes sense to just style admin_level=2 and admin_level=4. * - * Generated from - * boundary.yaml + * Generated from boundary.yaml */ public interface Boundary extends Layer { double BUFFER_SIZE = 4.0; @@ -773,8 +773,8 @@ final class FieldMappings { * buildings are contained in the building layer but all other airport related polygons can be found * in the aeroway layer. * - * Generated from - * aeroway.yaml + * Generated from aeroway.yaml */ public interface Aeroway extends Layer { double BUFFER_SIZE = 4.0; @@ -834,7 +834,7 @@ final class FieldMappings { * features like plazas. * * Generated from transportation.yaml + * "https://github.com/openmaptiles/openmaptiles/blob/6c31841f4674f15e15afde346a060cf7c22e6cdd/layers/transportation/transportation.yaml">transportation.yaml */ public interface Transportation extends Layer { double BUFFER_SIZE = 4.0; @@ -1178,8 +1178,8 @@ final class FieldMappings { * (building= ). Only buildings with tag * location:underground are excluded. * - * Generated from - * building.yaml + * Generated from building.yaml */ public interface Building extends Layer { double BUFFER_SIZE = 4.0; @@ -1221,7 +1221,7 @@ final class FieldMappings { * from OSM water bodies. Only the most important lakes contain labels. * * Generated from water_name.yaml + * "https://github.com/openmaptiles/openmaptiles/blob/6c31841f4674f15e15afde346a060cf7c22e6cdd/layers/water_name/water_name.yaml">water_name.yaml */ public interface WaterName extends Layer { double BUFFER_SIZE = 256.0; @@ -1291,7 +1291,7 @@ final class FieldMappings { * while for other roads you should use name. * * Generated from transportation_name.yaml + * "https://github.com/openmaptiles/openmaptiles/blob/6c31841f4674f15e15afde346a060cf7c22e6cdd/layers/transportation_name/transportation_name.yaml">transportation_name.yaml */ public interface TransportationName extends Layer { double BUFFER_SIZE = 8.0; @@ -1520,8 +1520,8 @@ final class FieldMappings { * of the more important layers to create a beautiful map. We suggest you use different font styles and sizes to * create a text hierarchy. * - * Generated from - * place.yaml + * Generated from place.yaml */ public interface Place extends Layer { double BUFFER_SIZE = 256.0; @@ -1632,7 +1632,7 @@ final class FieldMappings { * tag are prioritized for preservation). * * Generated from housenumber.yaml + * "https://github.com/openmaptiles/openmaptiles/blob/6c31841f4674f15e15afde346a060cf7c22e6cdd/layers/housenumber/housenumber.yaml">housenumber.yaml */ public interface Housenumber extends Layer { double BUFFER_SIZE = 8.0; @@ -1664,7 +1664,8 @@ final class FieldMappings { * Points of interests containing a of a variety * of OpenStreetMap tags. Mostly contains amenities, sport, shop and tourist POIs. * - * Generated from poi.yaml + * Generated from poi.yaml */ public interface Poi extends Layer { double BUFFER_SIZE = 64.0; @@ -1883,7 +1884,7 @@ final class FieldMappings { * Aerodrome labels * * Generated from aerodrome_label.yaml + * "https://github.com/openmaptiles/openmaptiles/blob/6c31841f4674f15e15afde346a060cf7c22e6cdd/layers/aerodrome_label/aerodrome_label.yaml">aerodrome_label.yaml */ public interface AerodromeLabel extends Layer { double BUFFER_SIZE = 64.0; diff --git a/src/main/java/org/openmaptiles/generated/Tables.java b/src/main/java/org/openmaptiles/generated/Tables.java index 5effebba..31ccedca 100644 --- a/src/main/java/org/openmaptiles/generated/Tables.java +++ b/src/main/java/org/openmaptiles/generated/Tables.java @@ -50,8 +50,9 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE /** * OSM element parsers generated from the imposm3 table definitions - * in the OpenMapTiles vector tile - * schema. + * in the OpenMapTiles + * vector tile schema. * * These filter and parse the raw OSM key/value attribute pairs on tags into records with fields that match the columns * in the tables that imposm3 would generate. Layer implementations can "subscribe" to elements from each "table" but @@ -364,18 +365,16 @@ public interface Handler { public record OsmRailwayLinestring(@Override String railway, @Override String ref, @Override String network, @Override int zOrder, @Override long layer, @Override long level, @Override boolean indoor, @Override String name, @Override String nameEn, @Override String nameDe, @Override String shortName, @Override boolean isTunnel, - @Override boolean isBridge, @Override boolean isRamp, @Override boolean isFord, @Override int isOneway, - @Override boolean isArea, @Override String service, @Override String usage, @Override SourceFeature source) - implements Row, WithRailway, WithRef, WithNetwork, WithZOrder, WithLayer, WithLevel, WithIndoor, WithName, - WithNameEn, WithNameDe, WithShortName, WithIsTunnel, WithIsBridge, WithIsRamp, WithIsFord, WithIsOneway, WithIsArea, - WithService, WithUsage, WithSource { + @Override boolean isBridge, @Override boolean isRamp, @Override boolean isFord, @Override boolean isArea, + @Override String service, @Override String usage, @Override SourceFeature source) implements Row, WithRailway, + WithRef, WithNetwork, WithZOrder, WithLayer, WithLevel, WithIndoor, WithName, WithNameEn, WithNameDe, WithShortName, + WithIsTunnel, WithIsBridge, WithIsRamp, WithIsFord, WithIsArea, WithService, WithUsage, WithSource { public OsmRailwayLinestring(SourceFeature source, String mappingKey) { this(source.getString("railway"), source.getString("ref"), source.getString("network"), source.getWayZorder(), source.getLong("layer"), source.getLong("level"), source.getBoolean("indoor"), source.getString("name"), source.getString("name:en"), source.getString("name:de"), source.getString("short_name"), source.getBoolean("tunnel"), source.getBoolean("bridge"), source.getBoolean("ramp"), source.getBoolean("ford"), - source.getDirection("oneway"), source.getBoolean("area"), source.getString("service"), - source.getString("usage"), source); + source.getBoolean("area"), source.getString("service"), source.getString("usage"), source); } /** Imposm3 "mapping" to filter OSM elements that should appear in this "table". */ @@ -423,16 +422,14 @@ public interface Handler { public record OsmShipwayLinestring(@Override String shipway, @Override int zOrder, @Override long layer, @Override String name, @Override String nameEn, @Override String nameDe, @Override String shortName, @Override boolean isTunnel, @Override boolean isBridge, @Override boolean isRamp, @Override boolean isFord, - @Override int isOneway, @Override boolean isArea, @Override String service, @Override String usage, - @Override SourceFeature source) + @Override boolean isArea, @Override String service, @Override String usage, @Override SourceFeature source) implements Row, WithShipway, WithZOrder, WithLayer, WithName, WithNameEn, WithNameDe, WithShortName, WithIsTunnel, - WithIsBridge, WithIsRamp, WithIsFord, WithIsOneway, WithIsArea, WithService, WithUsage, WithSource { + WithIsBridge, WithIsRamp, WithIsFord, WithIsArea, WithService, WithUsage, WithSource { public OsmShipwayLinestring(SourceFeature source, String mappingKey) { this(source.getString("route"), source.getWayZorder(), source.getLong("layer"), source.getString("name"), source.getString("name:en"), source.getString("name:de"), source.getString("short_name"), source.getBoolean("tunnel"), source.getBoolean("bridge"), source.getBoolean("ramp"), source.getBoolean("ford"), - source.getDirection("oneway"), source.getBoolean("area"), source.getString("service"), - source.getString("usage"), source); + source.getBoolean("area"), source.getString("service"), source.getString("usage"), source); } /** Imposm3 "mapping" to filter OSM elements that should appear in this "table". */ From 9ab8f47e937dcc2bc7a8ae251fbd2fbf3d04d2c2 Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Thu, 16 Nov 2023 20:57:45 +0100 Subject: [PATCH 078/106] relevant process() functions adjusted to match changes in transportation/mapping.yaml --- src/main/java/org/openmaptiles/layers/Transportation.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/main/java/org/openmaptiles/layers/Transportation.java b/src/main/java/org/openmaptiles/layers/Transportation.java index 1a6d9411..0f7e2b43 100644 --- a/src/main/java/org/openmaptiles/layers/Transportation.java +++ b/src/main/java/org/openmaptiles/layers/Transportation.java @@ -585,7 +585,6 @@ public void process(Tables.OsmRailwayLinestring element, FeatureCollector featur .setAttr(Fields.CLASS, clazz) .setAttr(Fields.SUBCLASS, railway) .setAttr(Fields.SERVICE, service(service)) - .setAttr(Fields.ONEWAY, nullIfInt(element.isOneway(), 0)) .setAttr(Fields.RAMP, element.isRamp() ? 1L : null) .setAttrWithMinzoom(Fields.BRUNNEL, brunnel(element.isBridge(), element.isTunnel(), element.isFord()), 10) .setAttrWithMinzoom(Fields.LAYER, nullIfLong(element.layer(), 0), 9) @@ -616,7 +615,6 @@ public void process(Tables.OsmShipwayLinestring element, FeatureCollector featur .setAttr(Fields.CLASS, element.shipway()) // "ferry" // no subclass .setAttr(Fields.SERVICE, service(element.service())) - .setAttr(Fields.ONEWAY, nullIfInt(element.isOneway(), 0)) .setAttr(Fields.RAMP, element.isRamp() ? 1L : null) .setAttr(Fields.BRUNNEL, brunnel(element.isBridge(), element.isTunnel(), element.isFord())) .setAttr(Fields.LAYER, nullIfLong(element.layer(), 0)) From 32982da26e38c03a64676a34d0b692f3fa86381a Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Thu, 16 Nov 2023 21:01:43 +0100 Subject: [PATCH 079/106] regenerate-openmaptiles.sh master, instead of 6c31841f4674f15e15afde346a060cf7c22e6cdd (to match content of OMT PR 1591, in a cleaner way) --- .../generated/OpenMapTilesSchema.java | 55 +++++++++---------- .../org/openmaptiles/generated/Tables.java | 5 +- 2 files changed, 29 insertions(+), 31 deletions(-) diff --git a/src/main/java/org/openmaptiles/generated/OpenMapTilesSchema.java b/src/main/java/org/openmaptiles/generated/OpenMapTilesSchema.java index 3045438d..55ec4e54 100644 --- a/src/main/java/org/openmaptiles/generated/OpenMapTilesSchema.java +++ b/src/main/java/org/openmaptiles/generated/OpenMapTilesSchema.java @@ -48,9 +48,9 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE import org.openmaptiles.Layer; /** - * All vector tile layer definitions, attributes, and allowed values generated from the OpenMapTiles - * vector tile schema 6c31841f4674f15e15afde346a060cf7c22e6cdd. + * All vector tile layer definitions, attributes, and allowed values generated from the + * OpenMapTiles vector tile schema + * master. */ @SuppressWarnings("unused") public class OpenMapTilesSchema { @@ -95,8 +95,8 @@ public static List createInstances(Translations translations, PlanetilerC * polygons to improve rendering performance. This however can lead to less rendering options in clients since these * boundaries show up. So you might not be able to use border styling for ocean water features. * - * Generated from water.yaml + * Generated from + * water.yaml */ public interface Water extends Layer { double BUFFER_SIZE = 4.0; @@ -194,8 +194,8 @@ final class FieldMappings { * there is also canal generated, starting z13 there is no generalization according to class * field applied. Waterways do not have a subclass field. * - * Generated from waterway.yaml + * Generated from + * waterway.yaml */ public interface Waterway extends Layer { double BUFFER_SIZE = 4.0; @@ -280,7 +280,7 @@ final class FieldMappings { * layer is to style wood (class=wood) and grass (class=grass) areas. * * Generated from landcover.yaml + * "https://github.com/openmaptiles/openmaptiles/blob/master/layers/landcover/landcover.yaml">landcover.yaml */ public interface Landcover extends Layer { double BUFFER_SIZE = 4.0; @@ -436,8 +436,8 @@ final class FieldMappings { * Landuse is used to describe use of land by humans. At lower zoom levels this is from Natural Earth data for * residential (urban) areas and at higher zoom levels mostly OSM landuse tags. * - * Generated from landuse.yaml + * Generated from + * landuse.yaml */ public interface Landuse extends Layer { double BUFFER_SIZE = 4.0; @@ -533,7 +533,7 @@ final class FieldMappings { * Natural peaks * * Generated from mountain_peak.yaml + * "https://github.com/openmaptiles/openmaptiles/blob/master/layers/mountain_peak/mountain_peak.yaml">mountain_peak.yaml */ public interface MountainPeak extends Layer { double BUFFER_SIZE = 64.0; @@ -611,8 +611,8 @@ final class FieldMappings { * Indigenous boundaries are not parks, but they are included in this layer for technical reasons related to data * processing. These boundaries represent areas with special legal and administrative status for indigenous peoples. * - * Generated from park.yaml + * Generated from + * park.yaml */ public interface Park extends Layer { double BUFFER_SIZE = 4.0; @@ -672,8 +672,8 @@ final class FieldMappings { * admin_level * but for most styles it makes sense to just style admin_level=2 and admin_level=4. * - * Generated from boundary.yaml + * Generated from + * boundary.yaml */ public interface Boundary extends Layer { double BUFFER_SIZE = 4.0; @@ -773,8 +773,8 @@ final class FieldMappings { * buildings are contained in the building layer but all other airport related polygons can be found * in the aeroway layer. * - * Generated from aeroway.yaml + * Generated from + * aeroway.yaml */ public interface Aeroway extends Layer { double BUFFER_SIZE = 4.0; @@ -834,7 +834,7 @@ final class FieldMappings { * features like plazas. * * Generated from transportation.yaml + * "https://github.com/openmaptiles/openmaptiles/blob/master/layers/transportation/transportation.yaml">transportation.yaml */ public interface Transportation extends Layer { double BUFFER_SIZE = 4.0; @@ -1178,8 +1178,8 @@ final class FieldMappings { * (building= ). Only buildings with tag * location:underground are excluded. * - * Generated from building.yaml + * Generated from + * building.yaml */ public interface Building extends Layer { double BUFFER_SIZE = 4.0; @@ -1221,7 +1221,7 @@ final class FieldMappings { * from OSM water bodies. Only the most important lakes contain labels. * * Generated from water_name.yaml + * "https://github.com/openmaptiles/openmaptiles/blob/master/layers/water_name/water_name.yaml">water_name.yaml */ public interface WaterName extends Layer { double BUFFER_SIZE = 256.0; @@ -1291,7 +1291,7 @@ final class FieldMappings { * while for other roads you should use name. * * Generated from transportation_name.yaml + * "https://github.com/openmaptiles/openmaptiles/blob/master/layers/transportation_name/transportation_name.yaml">transportation_name.yaml */ public interface TransportationName extends Layer { double BUFFER_SIZE = 8.0; @@ -1520,8 +1520,8 @@ final class FieldMappings { * of the more important layers to create a beautiful map. We suggest you use different font styles and sizes to * create a text hierarchy. * - * Generated from place.yaml + * Generated from + * place.yaml */ public interface Place extends Layer { double BUFFER_SIZE = 256.0; @@ -1632,7 +1632,7 @@ final class FieldMappings { * tag are prioritized for preservation). * * Generated from housenumber.yaml + * "https://github.com/openmaptiles/openmaptiles/blob/master/layers/housenumber/housenumber.yaml">housenumber.yaml */ public interface Housenumber extends Layer { double BUFFER_SIZE = 8.0; @@ -1664,8 +1664,7 @@ final class FieldMappings { * Points of interests containing a of a variety * of OpenStreetMap tags. Mostly contains amenities, sport, shop and tourist POIs. * - * Generated from poi.yaml + * Generated from poi.yaml */ public interface Poi extends Layer { double BUFFER_SIZE = 64.0; @@ -1884,7 +1883,7 @@ final class FieldMappings { * Aerodrome labels * * Generated from aerodrome_label.yaml + * "https://github.com/openmaptiles/openmaptiles/blob/master/layers/aerodrome_label/aerodrome_label.yaml">aerodrome_label.yaml */ public interface AerodromeLabel extends Layer { double BUFFER_SIZE = 64.0; diff --git a/src/main/java/org/openmaptiles/generated/Tables.java b/src/main/java/org/openmaptiles/generated/Tables.java index 31ccedca..dfcf2478 100644 --- a/src/main/java/org/openmaptiles/generated/Tables.java +++ b/src/main/java/org/openmaptiles/generated/Tables.java @@ -50,9 +50,8 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE /** * OSM element parsers generated from the imposm3 table definitions - * in the OpenMapTiles - * vector tile schema. + * in the OpenMapTiles vector tile + * schema. * * These filter and parse the raw OSM key/value attribute pairs on tags into records with fields that match the columns * in the tables that imposm3 would generate. Layer implementations can "subscribe" to elements from each "table" but From 912538b497ec0c0bb33723710aae8392a9f93b7d Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Mon, 20 Nov 2023 12:16:41 +0100 Subject: [PATCH 080/106] introduce duplicate housenumber filtering (matching OMT PR 1391) --- .../org/openmaptiles/layers/Housenumber.java | 31 ++++- .../openmaptiles/layers/HousenumberTest.java | 110 ++++++++++++++++++ 2 files changed, 140 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/openmaptiles/layers/Housenumber.java b/src/main/java/org/openmaptiles/layers/Housenumber.java index d220e2cc..a0f1de69 100644 --- a/src/main/java/org/openmaptiles/layers/Housenumber.java +++ b/src/main/java/org/openmaptiles/layers/Housenumber.java @@ -44,13 +44,16 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE import com.onthegomap.planetiler.stats.Stats; import com.onthegomap.planetiler.util.Translations; import java.util.Arrays; +import java.util.Comparator; import java.util.List; +import java.util.Optional; import java.util.function.Predicate; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; import org.openmaptiles.generated.OpenMapTilesSchema; import org.openmaptiles.generated.Tables; +import org.openmaptiles.util.Utils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -70,6 +73,8 @@ public class Housenumber implements private static final String OSM_SEPARATOR = ";"; private static final String DISPLAY_SEPARATOR = "–"; private static final Pattern NO_CONVERSION_PATTERN = Pattern.compile("[^0-9;]"); + private static final String TEMP_PARTITION = "_partition"; + private static final String TEMP_HAS_NAME = "_has_name"; private final Stats stats; public Housenumber(Translations translations, PlanetilerConfig config, Stats stats) { @@ -121,15 +126,39 @@ public void process(Tables.OsmHousenumberPoint element, FeatureCollector feature housenumber = element.housenumber(); } + String partition = Utils.coalesce(element.street(), "") + .concat(Utils.coalesce(element.blockNumber(), "")) + .concat(housenumber); + Boolean hasName = element.hasName() == null ? Boolean.FALSE : !element.hasName().isEmpty(); + features.centroidIfConvex(LAYER_NAME) .setBufferPixels(BUFFER_SIZE) .setAttr(Fields.HOUSENUMBER, housenumber) + .setAttr(TEMP_PARTITION, partition) + .setAttr(TEMP_HAS_NAME, hasName) .setMinZoom(14); } @Override public List postProcess(int zoom, List list) throws GeometryException { + // remove duplicate house numbers, features without name tag are prioritized + var items = list.stream() + .collect(Collectors.groupingBy(f -> f.attrs().get(TEMP_PARTITION))) + .values().stream() + .map( + g -> g.stream().min(Comparator.comparing(i -> (Boolean) i.attrs().get(TEMP_HAS_NAME), Boolean::compare)) + ) + .filter(Optional::isPresent) + .map(Optional::get) + .toList(); + + // remove temporary attributes + for (var item : items) { + item.attrs().remove(TEMP_HAS_NAME); + item.attrs().remove(TEMP_PARTITION); + } + // reduces the size of some heavy z14 tiles with many repeated housenumber values by 60% or more - return FeatureMerge.mergeMultiPoint(list); + return FeatureMerge.mergeMultiPoint(items); } } diff --git a/src/test/java/org/openmaptiles/layers/HousenumberTest.java b/src/test/java/org/openmaptiles/layers/HousenumberTest.java index 5d211b08..2fa31291 100644 --- a/src/test/java/org/openmaptiles/layers/HousenumberTest.java +++ b/src/test/java/org/openmaptiles/layers/HousenumberTest.java @@ -2,8 +2,10 @@ import static org.junit.jupiter.api.Assertions.assertEquals; +import com.onthegomap.planetiler.geo.GeometryException; import java.util.List; import java.util.Map; +import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.CsvSource; @@ -46,4 +48,112 @@ void testHousenumber() { void testDisplayHousenumber(String outlier, String expected) { assertEquals(expected, Housenumber.displayHousenumber(outlier)); } + + @Test + void testTempAttrs() { + assertFeatures(14, List.of(Map.of( + "_has_name", Boolean.TRUE, + "_partition", "streetX765/6" + )), process(polygonFeature(Map.of( + "addr:housenumber", "765/6", + "addr:block_number", "X", + "addr:street", "street", + "name", "name" + )))); + } + + @Test + void testNonduplicateHousenumber() throws GeometryException { + var layerName = Housenumber.LAYER_NAME; + var hn1 = pointFeature( + layerName, + Map.of( + "housenumber", "764/2", + "_partition", "764/2" + ), + 1 + ); + var hn2 = pointFeature( + layerName, + Map.of( + "housenumber", "765/6", + "_partition", "765/6" + ), + 1 + ); + + Assertions.assertEquals( + 2, + profile.postProcessLayerFeatures(layerName, 14, List.of(hn1, hn2)).size() + ); + } + + @Test + void testNonduplicateStreet() throws GeometryException { + var layerName = Housenumber.LAYER_NAME; + var housenumber = "765/6"; + var hn1 = pointFeature( + layerName, + Map.of( + "housenumber", housenumber, + "_partition", "street 1" + housenumber + ), + 1 + ); + var hn2 = pointFeature( + layerName, + Map.of( + "housenumber", housenumber, + "_partition", "street 2" + housenumber + ), + 1 + ); + + var result = profile.postProcessLayerFeatures(layerName, 14, List.of(hn1, hn2)); + + Assertions.assertEquals( + 1, // same housenumber => two points merged into one multipoint + result.size() + ); + Assertions.assertEquals( + 5, // two point in multipoint => 5 commands + result.getFirst().geometry().commands().length); + } + + @Test + void testDuplicateHousenumber() throws GeometryException { + var layerName = Housenumber.LAYER_NAME; + var housenumber = "765/6"; + var hn1 = pointFeature( + layerName, + Map.of( + "housenumber", housenumber + " (no name)", + "_has_name", false, + "_partition", housenumber + ), + 1 + ); + var hn2 = pointFeature( + layerName, + Map.of( + "housenumber", housenumber + " (with name)", + "_has_name", true, + "_partition", housenumber + ), + 1 + ); + + var result = profile.postProcessLayerFeatures(layerName, 14, List.of(hn1, hn2)); + + Assertions.assertEquals(List.of( + pointFeature( + layerName, + Map.of("housenumber", "765/6 (no name)"), + 1 + ) + ), result); + Assertions.assertEquals( + 3, // only one point in multipoint => 3 commands + result.getFirst().geometry().commands().length); + } } From a0569fd1782e3c441bbc0ad26d316d6c6c42dc17 Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Mon, 20 Nov 2023 12:18:31 +0100 Subject: [PATCH 081/106] (less related) clean-up: use isEmpty() instead if size check --- src/main/java/org/openmaptiles/layers/Housenumber.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/openmaptiles/layers/Housenumber.java b/src/main/java/org/openmaptiles/layers/Housenumber.java index a0f1de69..aee9afc0 100644 --- a/src/main/java/org/openmaptiles/layers/Housenumber.java +++ b/src/main/java/org/openmaptiles/layers/Housenumber.java @@ -96,7 +96,7 @@ protected static String displayHousenumber(String housenumber) { .map(String::trim) .filter(Predicate.not(String::isEmpty)) .toList(); - if (numbers.size() <= 0) { + if (numbers.isEmpty()) { // not much to do with strange/invalid entries like "3;" or ";" etc. return housenumber; } From 5bab4cfba7a3f16936e31a0bb1c569a3d6717ea1 Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Mon, 20 Nov 2023 12:20:36 +0100 Subject: [PATCH 082/106] testContainsHousenumber UT adjusted, since duplicate housenumber filtering is reducing amount of house numbers --- src/test/java/org/openmaptiles/OpenMapTilesTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/org/openmaptiles/OpenMapTilesTest.java b/src/test/java/org/openmaptiles/OpenMapTilesTest.java index 44d428d3..187fccfd 100644 --- a/src/test/java/org/openmaptiles/OpenMapTilesTest.java +++ b/src/test/java/org/openmaptiles/OpenMapTilesTest.java @@ -137,7 +137,7 @@ void testContainsHousenumber() { assertFeatureNear(mbtiles, "housenumber", Map.of( "housenumber", "27" ), 7.42117, 43.73652, 14, 14); - assertNumFeatures("housenumber", Map.of(), 14, 274, Point.class); + assertNumFeatures("housenumber", Map.of(), 14, 231, Point.class); } @Test From dc8a179d9c6627b71ee4e01b133f7ce772d79ddc Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Mon, 20 Nov 2023 23:19:05 +0100 Subject: [PATCH 083/106] use combination of uic_ref, name, network and operator as key for agg_stop sets If we rely on only on `uic_ref` we group together also stations which are too far apart (even different cities). With this combo results seem OK, e.g. all grouped stations are within around 950m (1000 pixels at Z14) of each other (1000 being used in `PARTITION BY LabelGrid(...` in `layers/poi/poi.sql` in OpenMapTiles). --- src/main/java/org/openmaptiles/layers/Poi.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/openmaptiles/layers/Poi.java b/src/main/java/org/openmaptiles/layers/Poi.java index d659b848..061c88a8 100644 --- a/src/main/java/org/openmaptiles/layers/Poi.java +++ b/src/main/java/org/openmaptiles/layers/Poi.java @@ -175,8 +175,12 @@ public void release() { public void process(Tables.OsmPoiPoint element, FeatureCollector features) { if (element.uicRef() != null && AGG_STOP_SUBCLASS_ORDER.contains(element.subclass())) { // multiple threads may update this concurrently + String aggStopKey = element.uicRef() + .concat(coalesce(nullIfEmpty(element.name()), "")) + .concat(coalesce(nullIfEmpty(element.network()), "")) + .concat(coalesce(nullIfEmpty(element.operator()), "")); synchronized (this) { - aggStops.computeIfAbsent(element.uicRef(), key -> new ArrayList<>()).add(element); + aggStops.computeIfAbsent(aggStopKey, key -> new ArrayList<>()).add(element); } } else { setupPoiFeature(element, features.point(LAYER_NAME), null); From 60ee412b44824af6a3b40597195a792c27a21da7 Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Mon, 20 Nov 2023 23:40:59 +0100 Subject: [PATCH 084/106] agg_stop comparison made more explicit, since we want to match same exact one --- src/main/java/org/openmaptiles/layers/Poi.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/openmaptiles/layers/Poi.java b/src/main/java/org/openmaptiles/layers/Poi.java index 061c88a8..653b8874 100644 --- a/src/main/java/org/openmaptiles/layers/Poi.java +++ b/src/main/java/org/openmaptiles/layers/Poi.java @@ -235,7 +235,7 @@ public void finish(String sourceName, FeatureCollector.Factory featureCollectors processAggStop(topAggStops[0], featureCollectors, emit, 1); // and emit the rest aggStopSet.stream() - .filter(s -> !topAggStops[0].equals(s)) + .filter(s -> s != topAggStops[0]) .forEach(s -> processAggStop(s, featureCollectors, emit, null)); continue; } @@ -269,7 +269,7 @@ public void finish(String sourceName, FeatureCollector.Factory featureCollectors // emit the rest final var alreadyDone = nearest; aggStopSet.stream() - .filter(s -> !s.equals(alreadyDone)) + .filter(s -> s != alreadyDone) .forEach(s -> processAggStop(s, featureCollectors, emit, null)); } catch (GeometryException e) { e.log(stats, "agg_stop_geometry_1", From 68639154b125834b625b53f8c6aa8b0f6ee22a4a Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Mon, 20 Nov 2023 23:41:58 +0100 Subject: [PATCH 085/106] mvn spotless:apply --- src/main/java/org/openmaptiles/layers/Poi.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/openmaptiles/layers/Poi.java b/src/main/java/org/openmaptiles/layers/Poi.java index 653b8874..58a0dc16 100644 --- a/src/main/java/org/openmaptiles/layers/Poi.java +++ b/src/main/java/org/openmaptiles/layers/Poi.java @@ -176,9 +176,9 @@ public void process(Tables.OsmPoiPoint element, FeatureCollector features) { if (element.uicRef() != null && AGG_STOP_SUBCLASS_ORDER.contains(element.subclass())) { // multiple threads may update this concurrently String aggStopKey = element.uicRef() - .concat(coalesce(nullIfEmpty(element.name()), "")) - .concat(coalesce(nullIfEmpty(element.network()), "")) - .concat(coalesce(nullIfEmpty(element.operator()), "")); + .concat(coalesce(nullIfEmpty(element.name()), "")) + .concat(coalesce(nullIfEmpty(element.network()), "")) + .concat(coalesce(nullIfEmpty(element.operator()), "")); synchronized (this) { aggStops.computeIfAbsent(aggStopKey, key -> new ArrayList<>()).add(element); } From 23d9ba70c7351406a991717f4e1e5245f3f62ca7 Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Mon, 20 Nov 2023 23:49:59 +0100 Subject: [PATCH 086/106] name now important for agg_stop processing, hence name:es (ab)used for unit tests --- .../java/org/openmaptiles/layers/PoiTest.java | 40 +++++++++++-------- 1 file changed, 24 insertions(+), 16 deletions(-) diff --git a/src/test/java/org/openmaptiles/layers/PoiTest.java b/src/test/java/org/openmaptiles/layers/PoiTest.java index 9e25237c..9f65eee7 100644 --- a/src/test/java/org/openmaptiles/layers/PoiTest.java +++ b/src/test/java/org/openmaptiles/layers/PoiTest.java @@ -99,19 +99,21 @@ void testAggStopTwoWithSameSubclass() { var result = testAggStops(List.of( pointFeature(Map.of( "railway", "tram_stop", - "name", "station 1", + "name", "station", + "name:es", "test 1", "uic_ref", "1" )), pointFeature(Map.of( "railway", "tram_stop", - "name", "station 2", + "name", "station", + "name:es", "test 2", "uic_ref", "1" )) )); assertFeatures(14, List.of( Map.of( "_layer", "poi", - "name", "station 1", + "name:es", "test 1", "class", "railway", "subclass", "tram_stop", "agg_stop", 1, @@ -119,7 +121,7 @@ void testAggStopTwoWithSameSubclass() { ), Map.of( "_layer", "poi", - "name", "station 2", + "name:es", "test 2", "class", "railway", "subclass", "tram_stop", "agg_stop", "", @@ -133,24 +135,27 @@ void testAggStopThreeWithMixedSubclass() { var result = testAggStops(List.of( pointFeature(Map.of( "highway", "bus_stop", - "name", "station 1", + "name", "station", + "name:es", "test 1", "uic_ref", "1" )), pointFeature(Map.of( "highway", "bus_stop", - "name", "station 2", + "name", "station", + "name:es", "test 2", "uic_ref", "1" )), pointFeature(Map.of( "railway", "tram_stop", - "name", "station 3", + "name", "station", + "name:es", "test 3", "uic_ref", "1" )) )); assertFeatures(14, List.of( Map.of( "_layer", "poi", - "name", "station 3", + "name:es", "test 3", "class", "railway", "subclass", "tram_stop", "agg_stop", 1, @@ -158,7 +163,7 @@ void testAggStopThreeWithMixedSubclass() { ), Map.of( "_layer", "poi", - "name", "station 1", + "name:es", "test 1", "class", "bus", "subclass", "bus_stop", "agg_stop", "", @@ -166,7 +171,7 @@ void testAggStopThreeWithMixedSubclass() { ), Map.of( "_layer", "poi", - "name", "station 2", + "name:es", "test 2", "class", "bus", "subclass", "bus_stop", "agg_stop", "", @@ -180,24 +185,27 @@ void testAggStopThreeWithSameSubclass() { var result = testAggStops(List.of( SimpleFeature.create(newPoint(0, 0), Map.of( "highway", "bus_stop", - "name", "station 1", + "name", "station", + "name:es", "test 1", "uic_ref", "1" ), OpenMapTilesProfile.OSM_SOURCE, null, 0), SimpleFeature.create(newPoint(1, 0), Map.of( "highway", "bus_stop", - "name", "station 2", + "name", "station", + "name:es", "test 2", "uic_ref", "1" ), OpenMapTilesProfile.OSM_SOURCE, null, 1), SimpleFeature.create(newPoint(2, 0), Map.of( "highway", "bus_stop", - "name", "station 3", + "name", "station", + "name:es", "test 3", "uic_ref", "1" ), OpenMapTilesProfile.OSM_SOURCE, null, 2) )); assertFeatures(14, List.of( Map.of( "_layer", "poi", - "name", "station 2", + "name:es", "test 2", "class", "bus", "subclass", "bus_stop", "agg_stop", 1, @@ -205,7 +213,7 @@ void testAggStopThreeWithSameSubclass() { ), Map.of( "_layer", "poi", - "name", "station 1", + "name:es", "test 1", "class", "bus", "subclass", "bus_stop", "agg_stop", "", @@ -213,7 +221,7 @@ void testAggStopThreeWithSameSubclass() { ), Map.of( "_layer", "poi", - "name", "station 3", + "name:es", "test 3", "class", "bus", "subclass", "bus_stop", "agg_stop", "", From e11f9c6c3b9c015f1c69088c61a41f8d612e06e9 Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Tue, 21 Nov 2023 09:47:43 +0100 Subject: [PATCH 087/106] agg_stop: simplified processing of nearest station Results still same, only ordering is different: - previously: agg_stop=1 first - now: FIFO --- .../java/org/openmaptiles/layers/Poi.java | 35 ++++--------------- .../java/org/openmaptiles/layers/PoiTest.java | 24 ++++++------- 2 files changed, 19 insertions(+), 40 deletions(-) diff --git a/src/main/java/org/openmaptiles/layers/Poi.java b/src/main/java/org/openmaptiles/layers/Poi.java index 58a0dc16..f5605b5c 100644 --- a/src/main/java/org/openmaptiles/layers/Poi.java +++ b/src/main/java/org/openmaptiles/layers/Poi.java @@ -218,36 +218,25 @@ public void finish(String sourceName, FeatureCollector.Factory featureCollectors var timer = stats.startStage("agg_stop"); LOGGER.info("Processing {} agg_stop sets", aggStops.size()); - // possible TODO: run in parallel? for (var aggStopSet : aggStops.values()) { if (aggStopSet.size() == 1) { processAggStop(aggStopSet.get(0), featureCollectors, emit, 1); continue; } try { - // find first based on subclass + // find most important stops based on subclass var firstSubclass = aggStopSet.stream().min(BY_SUBCLASS).get().subclass(); var topAggStops = aggStopSet.stream().filter(s -> firstSubclass.equals(s.subclass())).toArray(Tables.OsmPoiPoint[]::new); - if (topAggStops.length <= 2) { - // one found => straightforward: flag it and emit - // two found => both will be same distance from centroid: pick first one, flag it and emit - processAggStop(topAggStops[0], featureCollectors, emit, 1); - // and emit the rest - aggStopSet.stream() - .filter(s -> s != topAggStops[0]) - .forEach(s -> processAggStop(s, featureCollectors, emit, null)); - continue; - } - // easy cases handled, now we need also centroid + // calculate the centroid and ... List aggStopPoints = new ArrayList<>(aggStopSet.size()); for (var aggStop : aggStopSet) { aggStopPoints.add(aggStop.source().worldGeometry().getCentroid()); } var aggStopCentroid = GeoUtils.combinePoints(aggStopPoints).getCentroid(); - // find nearest + // ... find one stop nearest to the centroid double minDistance = Double.MAX_VALUE; Tables.OsmPoiPoint nearest = null; for (var aggStop : topAggStops) { @@ -258,23 +247,13 @@ public void finish(String sourceName, FeatureCollector.Factory featureCollectors } } - // emit nearest - if (nearest != null) { - processAggStop(nearest, featureCollectors, emit, 1); - } else { - stats.dataError("agg_stop_nearest"); - LOGGER.warn("Failed to find nearest stop for agg_stop UIC ref. {}", aggStopSet.get(0).uicRef()); - } - - // emit the rest - final var alreadyDone = nearest; - aggStopSet.stream() - .filter(s -> s != alreadyDone) - .forEach(s -> processAggStop(s, featureCollectors, emit, null)); + final var nearestFinal = nearest; // final needed for lambda + aggStopSet + .forEach(s -> processAggStop(s, featureCollectors, emit, s == nearestFinal ? 1 : null)); } catch (GeometryException e) { e.log(stats, "agg_stop_geometry_1", "Error getting geometry for some of the stops with UIC ref. " + aggStopSet.get(0).uicRef() + " (agg_stop)"); - // we're not able to calculate agg_stop, so simply dump them as-is + // we're not able to calculate agg_stop, so simply dump the stops as they are aggStopSet .forEach(s -> processAggStop(s, featureCollectors, emit, null)); } diff --git a/src/test/java/org/openmaptiles/layers/PoiTest.java b/src/test/java/org/openmaptiles/layers/PoiTest.java index 9f65eee7..9eacb30a 100644 --- a/src/test/java/org/openmaptiles/layers/PoiTest.java +++ b/src/test/java/org/openmaptiles/layers/PoiTest.java @@ -153,14 +153,6 @@ void testAggStopThreeWithMixedSubclass() { )) )); assertFeatures(14, List.of( - Map.of( - "_layer", "poi", - "name:es", "test 3", - "class", "railway", - "subclass", "tram_stop", - "agg_stop", 1, - "_minzoom", 14 - ), Map.of( "_layer", "poi", "name:es", "test 1", @@ -176,6 +168,14 @@ void testAggStopThreeWithMixedSubclass() { "subclass", "bus_stop", "agg_stop", "", "_minzoom", 14 + ), + Map.of( + "_layer", "poi", + "name:es", "test 3", + "class", "railway", + "subclass", "tram_stop", + "agg_stop", 1, + "_minzoom", 14 ) ), result); } @@ -205,18 +205,18 @@ void testAggStopThreeWithSameSubclass() { assertFeatures(14, List.of( Map.of( "_layer", "poi", - "name:es", "test 2", + "name:es", "test 1", "class", "bus", "subclass", "bus_stop", - "agg_stop", 1, + "agg_stop", "", "_minzoom", 14 ), Map.of( "_layer", "poi", - "name:es", "test 1", + "name:es", "test 2", "class", "bus", "subclass", "bus_stop", - "agg_stop", "", + "agg_stop", 1, "_minzoom", 14 ), Map.of( From f5fe5fe77ea5246f331af6d796ddbbd4be3098cf Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Tue, 21 Nov 2023 09:55:46 +0100 Subject: [PATCH 088/106] agg_stop: forther code simplification --- src/main/java/org/openmaptiles/layers/Poi.java | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/main/java/org/openmaptiles/layers/Poi.java b/src/main/java/org/openmaptiles/layers/Poi.java index f5605b5c..a6cb4237 100644 --- a/src/main/java/org/openmaptiles/layers/Poi.java +++ b/src/main/java/org/openmaptiles/layers/Poi.java @@ -223,6 +223,8 @@ public void finish(String sourceName, FeatureCollector.Factory featureCollectors processAggStop(aggStopSet.get(0), featureCollectors, emit, 1); continue; } + + Tables.OsmPoiPoint nearest = null; try { // find most important stops based on subclass var firstSubclass = aggStopSet.stream().min(BY_SUBCLASS).get().subclass(); @@ -238,7 +240,6 @@ public void finish(String sourceName, FeatureCollector.Factory featureCollectors // ... find one stop nearest to the centroid double minDistance = Double.MAX_VALUE; - Tables.OsmPoiPoint nearest = null; for (var aggStop : topAggStops) { double distance = aggStopCentroid.distance(aggStop.source().worldGeometry()); if (distance < minDistance) { @@ -246,17 +247,17 @@ public void finish(String sourceName, FeatureCollector.Factory featureCollectors nearest = aggStop; } } - - final var nearestFinal = nearest; // final needed for lambda - aggStopSet - .forEach(s -> processAggStop(s, featureCollectors, emit, s == nearestFinal ? 1 : null)); } catch (GeometryException e) { e.log(stats, "agg_stop_geometry_1", "Error getting geometry for some of the stops with UIC ref. " + aggStopSet.get(0).uicRef() + " (agg_stop)"); // we're not able to calculate agg_stop, so simply dump the stops as they are - aggStopSet - .forEach(s -> processAggStop(s, featureCollectors, emit, null)); + nearest = null; } + + // now emit the stops + final Tables.OsmPoiPoint nearestFinal = nearest; // final needed for lambda + aggStopSet + .forEach(s -> processAggStop(s, featureCollectors, emit, s == nearestFinal ? 1 : null)); } timer.stop(); From f3905500689c9015b83d3213356989086672a36d Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Tue, 21 Nov 2023 11:50:48 +0100 Subject: [PATCH 089/106] fixed major typo introduced in previous merge --- src/test/java/org/openmaptiles/layers/TransportationTest.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/test/java/org/openmaptiles/layers/TransportationTest.java b/src/test/java/org/openmaptiles/layers/TransportationTest.java index 7de3afb0..6a98d1fb 100644 --- a/src/test/java/org/openmaptiles/layers/TransportationTest.java +++ b/src/test/java/org/openmaptiles/layers/TransportationTest.java @@ -2107,8 +2107,9 @@ void testGetFerryMinzoom() throws GeometryException { process(entry.feature); */ } + } - @Test + @Test void testIssue58() { // test subject: https://www.openstreetmap.org/way/222564359 // note: "name:es" used instead of "name:ar" since we've setup only "de" and "es" for unit tests From 3b363cb190ea75c1c188831d21b2f015004ffab2 Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Tue, 21 Nov 2023 18:05:33 +0100 Subject: [PATCH 090/106] setMinPixelSize() + setMinZoom() used instead of areaToMinZoom() --- .../org/openmaptiles/layers/WaterName.java | 75 ++++++++----------- .../openmaptiles/layers/WaterNameTest.java | 41 ++-------- 2 files changed, 39 insertions(+), 77 deletions(-) diff --git a/src/main/java/org/openmaptiles/layers/WaterName.java b/src/main/java/org/openmaptiles/layers/WaterName.java index 470d2851..54862510 100644 --- a/src/main/java/org/openmaptiles/layers/WaterName.java +++ b/src/main/java/org/openmaptiles/layers/WaterName.java @@ -167,6 +167,11 @@ public void process(Tables.OsmMarinePoint element, FeatureCollector features) { if ("ocean".equals(element.place())) { minZoom = 0; } else if (rank != null) { + // FIXME: While this looks like matching properly stuff in https://github.com/openmaptiles/openmaptiles/pull/1457/files#diff-201daa1c61c99073fe3280d440c9feca5ed2236b251ad454caa14cc203f952d1R74 , + // it includes not just https://www.openstreetmap.org/relation/13360255 but also https://www.openstreetmap.org/node/1385157299 (and some others). + // Hence check how that OpenMapTiles code works for "James Bay" and: + // a) if same as here then, fix there and then here + // b) if OK (while here NOK), fix only here minZoom = rank; } else if ("bay".equals(element.natural())) { minZoom = 13; @@ -185,49 +190,35 @@ public void process(Tables.OsmMarinePoint element, FeatureCollector features) { @Override public void process(Tables.OsmWaterPolygon element, FeatureCollector features) { if (nullIfEmpty(element.name()) != null) { - try { - Geometry centerlineGeometry = lakeCenterlines.get(element.source().id()); - FeatureCollector.Feature feature; - int minzoom = 9; - String place = element.place(); - String clazz; - if ("bay".equals(element.natural())) { - clazz = FieldValues.CLASS_BAY; - } else if ("sea".equals(place)) { - clazz = FieldValues.CLASS_SEA; - } else { - clazz = FieldValues.CLASS_LAKE; - minzoom = 3; - } - if (centerlineGeometry != null) { - // prefer lake centerline if it exists - feature = features.geometry(LAYER_NAME, centerlineGeometry) - .setMinPixelSizeBelowZoom(13, 6d * element.name().length()); - } else { - // otherwise just use a label point inside the lake - feature = features.pointOnSurface(LAYER_NAME); - double area = element.source().area(); - if (place != null && SEA_OR_OCEAN_PLACE.contains(place)) { - minzoom = areaToMinZoom(area, 0); - } else { - minzoom = areaToMinZoom(area, 3); - } - } - feature - .setAttr(Fields.CLASS, clazz) - .setBufferPixels(BUFFER_SIZE) - .putAttrs(OmtLanguageUtils.getNames(element.source().tags(), translations)) - .setAttr(Fields.INTERMITTENT, element.isIntermittent() ? 1 : 0) - .setMinZoom(minzoom); - } catch (GeometryException e) { - e.log(stats, "omt_water_polygon", "Unable to get geometry for water polygon " + element.source().id()); + Geometry centerlineGeometry = lakeCenterlines.get(element.source().id()); + FeatureCollector.Feature feature; + int minzoom = 9; + String place = element.place(); + String clazz; + if ("bay".equals(element.natural())) { + clazz = FieldValues.CLASS_BAY; + } else if ("sea".equals(place)) { + clazz = FieldValues.CLASS_SEA; + } else { + clazz = FieldValues.CLASS_LAKE; + minzoom = 3; } + if (centerlineGeometry != null) { + // prefer lake centerline if it exists + feature = features.geometry(LAYER_NAME, centerlineGeometry) + .setMinPixelSizeBelowZoom(13, 6d * element.name().length()); + } else { + // otherwise just use a label point inside the lake + feature = features.pointOnSurface(LAYER_NAME) + .setMinZoom(place != null && SEA_OR_OCEAN_PLACE.contains(place) ? 0 : 3) + .setMinPixelSize(128); // tiles are 256x256, so 128x128 is 1/4 of a tile + } + feature + .setAttr(Fields.CLASS, clazz) + .setBufferPixels(BUFFER_SIZE) + .putAttrs(OmtLanguageUtils.getNames(element.source().tags(), translations)) + .setAttr(Fields.INTERMITTENT, element.isIntermittent() ? 1 : 0) + .setMinZoom(minzoom); } } - - public static int areaToMinZoom(double areaWorld, int minLimit) { - double oneSideWorld = Math.sqrt(areaWorld); - // OMT does "feature area is 1/4 of tile area", which is same as "feature side is 1/2 of tile side" - return Utils.getClippedMinZoomForLength(oneSideWorld, 1, minLimit, 14); - } } diff --git a/src/test/java/org/openmaptiles/layers/WaterNameTest.java b/src/test/java/org/openmaptiles/layers/WaterNameTest.java index 9f43c545..2462014d 100644 --- a/src/test/java/org/openmaptiles/layers/WaterNameTest.java +++ b/src/test/java/org/openmaptiles/layers/WaterNameTest.java @@ -39,6 +39,7 @@ void testWaterNamePoint() { "water", "pond", "intermittent", "1" )))); + /* TODO: remove, since `setMinPixelSize()` is now used and this no longer makes sense, as minzoom is always 3 // 1/4 th of tile area is the threshold, 1/4 = 0.25 => area->side:0.25->0.5 => slightly bigger -> 0.51 double z11area = Math.pow(0.51d / Math.pow(2, 11), 2); assertFeatures(10, List.of(Map.of( @@ -46,15 +47,17 @@ void testWaterNamePoint() { ), Map.of( "_layer", "water_name", "_type", "point", - "_minzoom", 11, + "_minzoom", 3, "_maxzoom", 14 )), process(polygonFeatureWithArea(z11area, Map.of( "name", "waterway", "natural", "water", "water", "pond" )))); + */ } + /* TODO: remove, since `setMinPixelSize()` is now used and this no longer makes sense, as minzoom is always 3 // https://zelonewolf.github.io/openstreetmap-americana/#map=13/41.43989/-71.5716 @Test void testWordenPondNamePoint() { @@ -63,7 +66,7 @@ void testWordenPondNamePoint() { ), Map.of( "_layer", "water_name", "_type", "point", - "_minzoom", 13, + "_minzoom", 3, "_maxzoom", 14 )), process(polygonFeatureWithArea(4.930387948170328E-9, Map.of( "name", "waterway", @@ -71,6 +74,7 @@ void testWordenPondNamePoint() { "water", "pond" )))); } + */ @Test void testWaterNameLakeline() { @@ -273,37 +277,4 @@ private void createAreaForMinZoomTest(List testEntries, double side, Math.clamp(expectedZoom, 3, 14) )); } - - @Test - void testAreaToMinZoom() throws GeometryException { - // threshold is 1/4 of tile area, hence ... - // ... side is 1/2 tile side: from pixels to world coord, for say Z14 ... - //final double HALF_OF_TILE_SIDE = 128d / Math.pow(2d, 14d + 8d); - // ... and then for some lower zoom: - //double testAreaSide = HALF_OF_TILE_SIDE * Math.pow(2, 14 - zoom); - // all this then simplified to `testAreaSide` calculation bellow - - final List testEntries = new ArrayList<>(); - for (int zoom = 14; zoom >= 0; zoom--) { - double testAreaSide = Math.pow(2, -zoom - 1); - - // slightly bellow the threshold - createAreaForMinZoomTest(testEntries, testAreaSide * 0.999, zoom + 1, "waterway-"); - // precisely at the threshold - createAreaForMinZoomTest(testEntries, testAreaSide, zoom, "waterway="); - // slightly over the threshold - createAreaForMinZoomTest(testEntries, testAreaSide * 1.001, zoom, "waterway+"); - } - - for (var entry : testEntries) { - assertFeatures(10, List.of(Map.of( - "_layer", "water" - ), Map.of( - "_layer", "water_name", - "_type", "point", - "_minzoom", entry.expectedZoom, - "_maxzoom", 14 - )), process(entry.feature)); - } - } } From 360751ed48cbd9822ab4d689c545ae87f835cd2f Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Tue, 21 Nov 2023 18:06:59 +0100 Subject: [PATCH 091/106] clean-up: unused stuff removed --- .../openmaptiles/layers/WaterNameTest.java | 52 ------------------- 1 file changed, 52 deletions(-) diff --git a/src/test/java/org/openmaptiles/layers/WaterNameTest.java b/src/test/java/org/openmaptiles/layers/WaterNameTest.java index 2462014d..076acb55 100644 --- a/src/test/java/org/openmaptiles/layers/WaterNameTest.java +++ b/src/test/java/org/openmaptiles/layers/WaterNameTest.java @@ -39,42 +39,7 @@ void testWaterNamePoint() { "water", "pond", "intermittent", "1" )))); - /* TODO: remove, since `setMinPixelSize()` is now used and this no longer makes sense, as minzoom is always 3 - // 1/4 th of tile area is the threshold, 1/4 = 0.25 => area->side:0.25->0.5 => slightly bigger -> 0.51 - double z11area = Math.pow(0.51d / Math.pow(2, 11), 2); - assertFeatures(10, List.of(Map.of( - "_layer", "water" - ), Map.of( - "_layer", "water_name", - "_type", "point", - "_minzoom", 3, - "_maxzoom", 14 - )), process(polygonFeatureWithArea(z11area, Map.of( - "name", "waterway", - "natural", "water", - "water", "pond" - )))); - */ - } - - /* TODO: remove, since `setMinPixelSize()` is now used and this no longer makes sense, as minzoom is always 3 - // https://zelonewolf.github.io/openstreetmap-americana/#map=13/41.43989/-71.5716 - @Test - void testWordenPondNamePoint() { - assertFeatures(10, List.of(Map.of( - "_layer", "water" - ), Map.of( - "_layer", "water_name", - "_type", "point", - "_minzoom", 3, - "_maxzoom", 14 - )), process(polygonFeatureWithArea(4.930387948170328E-9, Map.of( - "name", "waterway", - "natural", "water", - "water", "pond" - )))); } - */ @Test void testWaterNameLakeline() { @@ -260,21 +225,4 @@ void testMarinePoint() { "place", "sea" )))); } - - private record TestEntry( - SourceFeature feature, - int expectedZoom - ) {} - - private void createAreaForMinZoomTest(List testEntries, double side, int expectedZoom, String name) { - double area = Math.pow(side, 2); - var feature = polygonFeatureWithArea(area, Map.of( - "name", name, - "natural", "water" - )); - testEntries.add(new TestEntry( - feature, - Math.clamp(expectedZoom, 3, 14) - )); - } } From 503eee8b61497cfa8e4cd875d66e78fedd409dc5 Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Tue, 21 Nov 2023 19:00:12 +0100 Subject: [PATCH 092/106] mvn spotless:apply --- src/main/java/org/openmaptiles/layers/WaterName.java | 3 +-- src/test/java/org/openmaptiles/layers/WaterNameTest.java | 3 --- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/src/main/java/org/openmaptiles/layers/WaterName.java b/src/main/java/org/openmaptiles/layers/WaterName.java index 54862510..cebaeffe 100644 --- a/src/main/java/org/openmaptiles/layers/WaterName.java +++ b/src/main/java/org/openmaptiles/layers/WaterName.java @@ -56,7 +56,6 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE import org.openmaptiles.generated.OpenMapTilesSchema; import org.openmaptiles.generated.Tables; import org.openmaptiles.util.OmtLanguageUtils; -import org.openmaptiles.util.Utils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -211,7 +210,7 @@ public void process(Tables.OsmWaterPolygon element, FeatureCollector features) { // otherwise just use a label point inside the lake feature = features.pointOnSurface(LAYER_NAME) .setMinZoom(place != null && SEA_OR_OCEAN_PLACE.contains(place) ? 0 : 3) - .setMinPixelSize(128); // tiles are 256x256, so 128x128 is 1/4 of a tile + .setMinPixelSize(128); // tiles are 256x256, so 128x128 is 1/4 of a tile } feature .setAttr(Fields.CLASS, clazz) diff --git a/src/test/java/org/openmaptiles/layers/WaterNameTest.java b/src/test/java/org/openmaptiles/layers/WaterNameTest.java index 076acb55..02b7c984 100644 --- a/src/test/java/org/openmaptiles/layers/WaterNameTest.java +++ b/src/test/java/org/openmaptiles/layers/WaterNameTest.java @@ -5,10 +5,7 @@ import com.onthegomap.planetiler.TestUtils; import com.onthegomap.planetiler.geo.GeoUtils; -import com.onthegomap.planetiler.geo.GeometryException; import com.onthegomap.planetiler.reader.SimpleFeature; -import com.onthegomap.planetiler.reader.SourceFeature; -import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; From 212c80c0f01b499066bbd57a9e8d39180eaa115b Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Tue, 21 Nov 2023 21:01:21 +0100 Subject: [PATCH 093/106] setAttrWithMinSize() used instead of getBrunnelMinzoom() getFerryMinzoom() kept since we'd like to replicate `sql_filter: ST_Length(...` from OMT --- .../openmaptiles/layers/Transportation.java | 32 +++++------- .../layers/TransportationName.java | 6 +-- .../layers/TransportationTest.java | 51 ------------------- 3 files changed, 15 insertions(+), 74 deletions(-) diff --git a/src/main/java/org/openmaptiles/layers/Transportation.java b/src/main/java/org/openmaptiles/layers/Transportation.java index 0e103910..78bc27ae 100644 --- a/src/main/java/org/openmaptiles/layers/Transportation.java +++ b/src/main/java/org/openmaptiles/layers/Transportation.java @@ -74,7 +74,6 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE import org.openmaptiles.OpenMapTilesProfile; import org.openmaptiles.generated.OpenMapTilesSchema; import org.openmaptiles.generated.Tables; -import org.openmaptiles.util.Utils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -166,6 +165,7 @@ public class Transportation implements private static final Set ONEWAY_VALUES = Set.of(-1, 1); private final Map MINZOOMS; private static final String LIMIT_MERGE_TAG = "__limit_merge"; + private static final double LOG2 = Math.log(2); private final AtomicBoolean loggedNoGb = new AtomicBoolean(false); private final AtomicBoolean loggedNoIreland = new AtomicBoolean(false); private final boolean z13Paths; @@ -425,10 +425,6 @@ public void process(Tables.OsmHighwayLinestring element, FeatureCollector featur } int minzoom = getMinzoom(element, highwayClass); - String brunnelValue = brunnel(element.isBridge(), element.isTunnel(), element.isFord()); - int brunnelMinzoom = brunnelValue != null ? getBrunnelMinzoom(element) : minzoom; - int layerMinzoom = Math.max(brunnelMinzoom, 9); - if (minzoom > config.maxzoom()) { return; } @@ -442,11 +438,11 @@ public void process(Tables.OsmHighwayLinestring element, FeatureCollector featur .setAttr(Fields.CLASS, highwayClass) .setAttr(Fields.SUBCLASS, highwaySubclass(highwayClass, element.publicTransport(), highway)) .setAttr(Fields.NETWORK, networkType != null ? networkType.name : null) - .setAttrWithMinzoom(Fields.BRUNNEL, brunnelValue, brunnelMinzoom) + .setAttrWithMinSize(Fields.BRUNNEL, brunnel(element.isBridge(), element.isTunnel(), element.isFord()), 4, 4, 12) // z8+ .setAttrWithMinzoom(Fields.EXPRESSWAY, element.expressway() && !"motorway".equals(highway) ? 1 : null, 8) // z9+ - .setAttrWithMinzoom(Fields.LAYER, nullIfLong(element.layer(), 0), layerMinzoom) + .setAttrWithMinSize(Fields.LAYER, nullIfLong(element.layer(), 0), 4, 9, 12) .setAttrWithMinzoom(Fields.BICYCLE, nullIfEmpty(element.bicycle()), 9) .setAttrWithMinzoom(Fields.FOOT, nullIfEmpty(element.foot()), 9) .setAttrWithMinzoom(Fields.HORSE, nullIfEmpty(element.horse()), 9) @@ -511,17 +507,6 @@ int getMinzoom(Tables.OsmHighwayLinestring element, String highwayClass) { return minzoom; } - int getBrunnelMinzoom(Tables.OsmHighwayLinestring element) { - try { - return Utils.getClippedMinZoomForLength(element.source().length(), 6, 4, 12); - } catch (GeometryException e) { - e.log(stats, "omt_brunnel_minzoom", - "Unable to calculate brunnel minzoom for " + element.source().id()); - // brunnel is optional (depends on feature size) for Z4-Z11, it is always present for Z12+, hence 12 as fallback - return 12; - } - } - private boolean isPierPolygon(Tables.OsmHighwayLinestring element) { if ("pier".equals(element.manMade())) { try { @@ -598,13 +583,20 @@ public void process(Tables.OsmShipwayLinestring element, FeatureCollector featur .setMinZoom(getFerryMinzoom(element)); } + /* + * Ferries are supposed to be included in Z4-Z10 depending on their length, for Z11+ always. This implements + * the equivalent of `sql_filter: ST_Length(geometry)>2*ZRES0` in OpenMapTiles to make sure that only longer ferries + * make it into lower zoom. That is needed since ferries are not tagged as highways based on importance, hence length + * is a substitute for importance. + */ int getFerryMinzoom(Tables.OsmShipwayLinestring element) { try { - return Utils.getClippedMinZoomForLength(element.source().length(), 3, 4, 11); + double zoom = -(Math.log(element.source().length()) / LOG2) - 3; + return Math.clamp((int) Math.floor(zoom - 0.1e-10) + 1, 4, 11); } catch (GeometryException e) { e.log(stats, "omt_ferry_minzoom", "Unable to calculate ferry minzoom for " + element.source().id()); - // ferries are supposed to be included in Z4-Z10 depending on their length (=this min. zoom calculation), for Z11+ always, hence 11 as fallback + // for Z11+ always, hence 11 as fallback return 11; } } diff --git a/src/main/java/org/openmaptiles/layers/TransportationName.java b/src/main/java/org/openmaptiles/layers/TransportationName.java index d9f0d2f1..4e8fefd8 100644 --- a/src/main/java/org/openmaptiles/layers/TransportationName.java +++ b/src/main/java/org/openmaptiles/layers/TransportationName.java @@ -279,9 +279,9 @@ public void process(Tables.OsmHighwayLinestring element, FeatureCollector featur } if (brunnel) { - String brunnelValue = brunnel(element.isBridge(), element.isTunnel(), element.isFord()); - int brunnelMinzoom = brunnelValue != null ? transportation.getBrunnelMinzoom(element) : minzoom; - feature.setAttrWithMinzoom(Fields.BRUNNEL, brunnelValue, brunnelMinzoom); + // from OMT: "Drop brunnel if length of way < 2% of tile width (less than 3 pixels)" + feature.setAttrWithMinSize(Fields.BRUNNEL, brunnel(element.isBridge(), element.isTunnel(), element.isFord()), + 3, 4, 12); } /* diff --git a/src/test/java/org/openmaptiles/layers/TransportationTest.java b/src/test/java/org/openmaptiles/layers/TransportationTest.java index 6a98d1fb..5bd13124 100644 --- a/src/test/java/org/openmaptiles/layers/TransportationTest.java +++ b/src/test/java/org/openmaptiles/layers/TransportationTest.java @@ -2020,54 +2020,6 @@ private record TestEntry( int expectedZoom ) {} - private void createBrunnelForMinZoomTest(List testEntries, double length, int expectedZoom, String name) { - var feature = lineFeatureWithLength(length, Map.of( - "name", name, - "bridge", "yes", - "highway", "motorway" - )); - testEntries.add(new TestEntry( - feature, - Math.clamp(expectedZoom, 4, 12) - )); - } - - @Test - void testGetBrunnelMinzoom() throws GeometryException { - final List testEntries = new ArrayList<>(); - for (int zoom = 14; zoom >= 0; zoom--) { - double testLength = Math.pow(2, -zoom - 6); - - // slightly bellow the threshold - createBrunnelForMinZoomTest(testEntries, testLength * 0.999, zoom + 1, "brunnel-"); - // precisely at the threshold - createBrunnelForMinZoomTest(testEntries, testLength, zoom, "brunnel="); - // slightly over the threshold - createBrunnelForMinZoomTest(testEntries, testLength * 1.001, zoom, "brunnel+"); - } - - for (var entry : testEntries) { - var result = process(entry.feature); - - assertFeatures(entry.expectedZoom, List.of(Map.of( - "_layer", "transportation", - "_type", "line", - "class", "motorway", - "brunnel", "bridge" - ), Map.of( - "_layer", "transportation_name", - "_type", "line" - )), result); - - assertFeatures(entry.expectedZoom - 1, List.of(Map.of( - "_layer", "transportation", - "brunnel", "" - ), Map.of( - "_layer", "transportation_name" - )), result); - } - } - private void createFerryForMinZoomTest(List testEntries, double length, int expectedZoom, String name) { var feature = lineFeatureWithLength(length, Map.of( "name", name, @@ -2103,9 +2055,6 @@ void testGetFerryMinzoom() throws GeometryException { "_layer", "transportation_name", "_type", "line" )), process(entry.feature)); - /* - process(entry.feature); - */ } } From 28b75a7fceff94ef25e44e07a75fdcdbd2a8dee0 Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Tue, 21 Nov 2023 21:22:09 +0100 Subject: [PATCH 094/106] getMinZoomForLength() no longer used, hence removed --- .../java/org/openmaptiles/layers/Poi.java | 5 --- .../java/org/openmaptiles/util/Utils.java | 40 ------------------- 2 files changed, 45 deletions(-) diff --git a/src/main/java/org/openmaptiles/layers/Poi.java b/src/main/java/org/openmaptiles/layers/Poi.java index a6cb4237..93052676 100644 --- a/src/main/java/org/openmaptiles/layers/Poi.java +++ b/src/main/java/org/openmaptiles/layers/Poi.java @@ -155,12 +155,7 @@ private int minzoom(String subclass, String mappingKey) { public static int uniAreaToMinZoom(double areaWorld) { double oneSideWorld = Math.sqrt(areaWorld); - // adjusted formula from `Utils.getMinZoomForLength()`, given that 1/10 does not match `1 / (2^)` double zoom = -(Math.log(oneSideWorld * SQRT10) / LOG2); - - // Say Z13.01 means bellow threshold, Z13.00 is exactly threshold, Z12.99 is over threshold, - // hence Z13.01 and Z13.00 will be rounded to Z14 and Z12.99 to Z13 (e.g. `floor() + 1`). - // And to accommodate for some precision errors (observed for Z9-Z11) we do also `- 0.1e-10`. int result = (int) Math.floor(zoom - 0.1e-10) + 1; return Math.clamp(result, 10, 14); diff --git a/src/main/java/org/openmaptiles/util/Utils.java b/src/main/java/org/openmaptiles/util/Utils.java index 830e0329..66e43071 100644 --- a/src/main/java/org/openmaptiles/util/Utils.java +++ b/src/main/java/org/openmaptiles/util/Utils.java @@ -76,44 +76,4 @@ public static String brunnel(boolean isBridge, boolean isTunnel) { public static String brunnel(boolean isBridge, boolean isTunnel, boolean isFord) { return isBridge ? "bridge" : isTunnel ? "tunnel" : isFord ? "ford" : null; } - - /** - * Calculate minzoom for a feature with given length based on threshold: if a feature's length is least - * {@code 1 / (2^threshold)} of a tile at certain zoom level, it will be shown at that and higher zoom levels, e.g. - * that particular zoom level is minzoom. - *

    - * {@code threshold} is calculated in such way to avoid calculating log(2) os it during the runtime. Use 1 for 1/2, 2 - * for 1/4, 3 for 1/8, etc. - * - * @param length length of the feature - * @param threshold threshold - * @return minzoom for a feature with given length and given threshold - */ - public static int getMinZoomForLength(double length, double threshold) { - // Say threshold is 1/8 (threshold variable = 8) of tile size, hence ... - // ... from pixels to world coord, for say Z14, the minimum length is: - // PORTION_OF_TILE_SIDE = (256d / 8) / Math.pow(2d, 14d + 8d); - // ... and then minimum length for some lower zoom: - // PORTION_OF_TILE_SIDE * Math.pow(2, 14 - zoom); - // all this then reversed and simplified to: - double zoom = -(Math.log(length) / LOG2) - threshold; - - // Say Z13.01 means bellow threshold, Z13.00 is exactly threshold, Z12.99 is over threshold, - // hence Z13.01 and Z13.00 will be rounded to Z14 and Z12.99 to Z13 (e.g. `floor() + 1`). - // And to accommodate for some precision errors (observed for Z9-Z11) we do also `- 0.1e-10`. - return (int) Math.floor(zoom - 0.1e-10) + 1; - } - - /** - * Same as {@link #getMinZoomForLength(double, double)} but with result within the given minimum and maximim. - * - * @param length length of the feature - * @param threshold threshold - * @param min clip the result to this value if lower - * @param max clip the result to this value if higher - * @return minzoom for a feature with given length and given threshold clipped to not exceed given minimum and maximum - */ - public static int getClippedMinZoomForLength(double length, double threshold, int min, int max) { - return Math.clamp(getMinZoomForLength(length, threshold), min, max); - } } From 612f700d287bdccc815331eeca79c377f0f253b0 Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Tue, 28 Nov 2023 15:29:02 +0100 Subject: [PATCH 095/106] clean-up: LOG2 not used, hence removed --- src/main/java/org/openmaptiles/util/Utils.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/main/java/org/openmaptiles/util/Utils.java b/src/main/java/org/openmaptiles/util/Utils.java index 66e43071..993ca090 100644 --- a/src/main/java/org/openmaptiles/util/Utils.java +++ b/src/main/java/org/openmaptiles/util/Utils.java @@ -8,8 +8,6 @@ */ public class Utils { - private static final double LOG2 = Math.log(2); - public static T coalesce(T a, T b) { return a != null ? a : b; } From d7560388d8486497133560a850ace3d102119840 Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Tue, 12 Dec 2023 10:50:02 +0100 Subject: [PATCH 096/106] added BY_TEMP_HAS_NAME comparator to avoid its repeated construction during run-time --- src/main/java/org/openmaptiles/layers/Housenumber.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/openmaptiles/layers/Housenumber.java b/src/main/java/org/openmaptiles/layers/Housenumber.java index aee9afc0..4d54481b 100644 --- a/src/main/java/org/openmaptiles/layers/Housenumber.java +++ b/src/main/java/org/openmaptiles/layers/Housenumber.java @@ -75,6 +75,8 @@ public class Housenumber implements private static final Pattern NO_CONVERSION_PATTERN = Pattern.compile("[^0-9;]"); private static final String TEMP_PARTITION = "_partition"; private static final String TEMP_HAS_NAME = "_has_name"; + private static final Comparator BY_TEMP_HAS_NAME = Comparator + .comparing(i -> (Boolean) i.attrs().get(TEMP_HAS_NAME), Boolean::compare); private final Stats stats; public Housenumber(Translations translations, PlanetilerConfig config, Stats stats) { @@ -146,7 +148,7 @@ public List postProcess(int zoom, List l .collect(Collectors.groupingBy(f -> f.attrs().get(TEMP_PARTITION))) .values().stream() .map( - g -> g.stream().min(Comparator.comparing(i -> (Boolean) i.attrs().get(TEMP_HAS_NAME), Boolean::compare)) + g -> g.stream().min(BY_TEMP_HAS_NAME) ) .filter(Optional::isPresent) .map(Optional::get) From 08ee4dad47bf539fea08b92fa5c9b3f86586f759 Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Tue, 12 Dec 2023 10:52:59 +0100 Subject: [PATCH 097/106] duplicate houcenumber processing simplified further --- src/main/java/org/openmaptiles/layers/Housenumber.java | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/openmaptiles/layers/Housenumber.java b/src/main/java/org/openmaptiles/layers/Housenumber.java index 4d54481b..2d53c42a 100644 --- a/src/main/java/org/openmaptiles/layers/Housenumber.java +++ b/src/main/java/org/openmaptiles/layers/Housenumber.java @@ -46,7 +46,6 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE import java.util.Arrays; import java.util.Comparator; import java.util.List; -import java.util.Optional; import java.util.function.Predicate; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -147,11 +146,9 @@ public List postProcess(int zoom, List l var items = list.stream() .collect(Collectors.groupingBy(f -> f.attrs().get(TEMP_PARTITION))) .values().stream() - .map( - g -> g.stream().min(BY_TEMP_HAS_NAME) + .flatMap( + g -> g.stream().min(BY_TEMP_HAS_NAME).stream() ) - .filter(Optional::isPresent) - .map(Optional::get) .toList(); // remove temporary attributes From c8c4b24089ed8bd4a6dec304760a1c41a99b3b0e Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Tue, 12 Dec 2023 10:55:25 +0100 Subject: [PATCH 098/106] clean-up: get(0) replaced with getFirst() --- src/main/java/org/openmaptiles/layers/Poi.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/openmaptiles/layers/Poi.java b/src/main/java/org/openmaptiles/layers/Poi.java index 93052676..e7e65240 100644 --- a/src/main/java/org/openmaptiles/layers/Poi.java +++ b/src/main/java/org/openmaptiles/layers/Poi.java @@ -215,7 +215,7 @@ public void finish(String sourceName, FeatureCollector.Factory featureCollectors for (var aggStopSet : aggStops.values()) { if (aggStopSet.size() == 1) { - processAggStop(aggStopSet.get(0), featureCollectors, emit, 1); + processAggStop(aggStopSet.getFirst(), featureCollectors, emit, 1); continue; } @@ -244,7 +244,8 @@ public void finish(String sourceName, FeatureCollector.Factory featureCollectors } } catch (GeometryException e) { e.log(stats, "agg_stop_geometry_1", - "Error getting geometry for some of the stops with UIC ref. " + aggStopSet.get(0).uicRef() + " (agg_stop)"); + "Error getting geometry for some of the stops with UIC ref. " + aggStopSet.getFirst().uicRef() + + " (agg_stop)"); // we're not able to calculate agg_stop, so simply dump the stops as they are nearest = null; } From 57c2547704733541d643c3b61c06280705e8066d Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Tue, 12 Dec 2023 11:06:22 +0100 Subject: [PATCH 099/106] clean-up: CPU-intensive prepare() moved out of synchronized block --- .../java/org/openmaptiles/layers/Transportation.java | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/openmaptiles/layers/Transportation.java b/src/main/java/org/openmaptiles/layers/Transportation.java index 78bc27ae..6cb8bd2d 100644 --- a/src/main/java/org/openmaptiles/layers/Transportation.java +++ b/src/main/java/org/openmaptiles/layers/Transportation.java @@ -262,18 +262,22 @@ public void processNaturalEarth(String table, SourceFeature feature, // once, but just to be safe synchronize updates to that field if (feature.hasTag("iso_a2", "GB")) { try { - Geometry boundary = feature.polygon().buffer(GeoUtils.metersToPixelAtEquator(0, 10_000) / 256d); + var prepared = PreparedGeometryFactory.prepare( + feature.polygon().buffer(GeoUtils.metersToPixelAtEquator(0, 10_000) / 256d) + ); synchronized (this) { - greatBritain = PreparedGeometryFactory.prepare(boundary); + greatBritain = prepared; } } catch (GeometryException e) { LOGGER.error("Failed to get Great Britain Polygon: " + e); } } else if (feature.hasTag("iso_a2", "IE")) { try { - Geometry boundary = feature.polygon().buffer(GeoUtils.metersToPixelAtEquator(0, 10_000) / 256d); + var prepared = PreparedGeometryFactory.prepare( + feature.polygon().buffer(GeoUtils.metersToPixelAtEquator(0, 10_000) / 256d) + ); synchronized (this) { - ireland = PreparedGeometryFactory.prepare(boundary); + ireland = prepared; } } catch (GeometryException e) { LOGGER.error("Failed to get Ireland Polygon: " + e); From 82bb68afc522c54245c38c713d927ddb75a6a03a Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Fri, 15 Dec 2023 09:13:34 +0100 Subject: [PATCH 100/106] regenerate-openmaptiles.sh 3cf77e2a542d8a369bb08bf2538cdde0b3effb2b (to match content of OMT PR 1423) --- .../generated/OpenMapTilesSchema.java | 71 +++++++++++-------- .../org/openmaptiles/generated/Tables.java | 25 +++++-- 2 files changed, 63 insertions(+), 33 deletions(-) diff --git a/src/main/java/org/openmaptiles/generated/OpenMapTilesSchema.java b/src/main/java/org/openmaptiles/generated/OpenMapTilesSchema.java index 55ec4e54..6018d0f7 100644 --- a/src/main/java/org/openmaptiles/generated/OpenMapTilesSchema.java +++ b/src/main/java/org/openmaptiles/generated/OpenMapTilesSchema.java @@ -48,9 +48,9 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE import org.openmaptiles.Layer; /** - * All vector tile layer definitions, attributes, and allowed values generated from the - * OpenMapTiles vector tile schema - * master. + * All vector tile layer definitions, attributes, and allowed values generated from the OpenMapTiles + * vector tile schema 3cf77e2a542d8a369bb08bf2538cdde0b3effb2b. */ @SuppressWarnings("unused") public class OpenMapTilesSchema { @@ -95,8 +95,8 @@ public static List createInstances(Translations translations, PlanetilerC * polygons to improve rendering performance. This however can lead to less rendering options in clients since these * boundaries show up. So you might not be able to use border styling for ocean water features. * - * Generated from - * water.yaml + * Generated from water.yaml */ public interface Water extends Layer { double BUFFER_SIZE = 4.0; @@ -194,8 +194,8 @@ final class FieldMappings { * there is also canal generated, starting z13 there is no generalization according to class * field applied. Waterways do not have a subclass field. * - * Generated from - * waterway.yaml + * Generated from waterway.yaml */ public interface Waterway extends Layer { double BUFFER_SIZE = 4.0; @@ -280,7 +280,7 @@ final class FieldMappings { * layer is to style wood (class=wood) and grass (class=grass) areas. * * Generated from landcover.yaml + * "https://github.com/openmaptiles/openmaptiles/blob/3cf77e2a542d8a369bb08bf2538cdde0b3effb2b/layers/landcover/landcover.yaml">landcover.yaml */ public interface Landcover extends Layer { double BUFFER_SIZE = 4.0; @@ -436,8 +436,8 @@ final class FieldMappings { * Landuse is used to describe use of land by humans. At lower zoom levels this is from Natural Earth data for * residential (urban) areas and at higher zoom levels mostly OSM landuse tags. * - * Generated from - * landuse.yaml + * Generated from landuse.yaml */ public interface Landuse extends Layer { double BUFFER_SIZE = 4.0; @@ -533,7 +533,7 @@ final class FieldMappings { * Natural peaks * * Generated from mountain_peak.yaml + * "https://github.com/openmaptiles/openmaptiles/blob/3cf77e2a542d8a369bb08bf2538cdde0b3effb2b/layers/mountain_peak/mountain_peak.yaml">mountain_peak.yaml */ public interface MountainPeak extends Layer { double BUFFER_SIZE = 64.0; @@ -611,8 +611,8 @@ final class FieldMappings { * Indigenous boundaries are not parks, but they are included in this layer for technical reasons related to data * processing. These boundaries represent areas with special legal and administrative status for indigenous peoples. * - * Generated from - * park.yaml + * Generated from park.yaml */ public interface Park extends Layer { double BUFFER_SIZE = 4.0; @@ -672,8 +672,8 @@ final class FieldMappings { * admin_level * but for most styles it makes sense to just style admin_level=2 and admin_level=4. * - * Generated from - * boundary.yaml + * Generated from boundary.yaml */ public interface Boundary extends Layer { double BUFFER_SIZE = 4.0; @@ -773,8 +773,8 @@ final class FieldMappings { * buildings are contained in the building layer but all other airport related polygons can be found * in the aeroway layer. * - * Generated from - * aeroway.yaml + * Generated from aeroway.yaml */ public interface Aeroway extends Layer { double BUFFER_SIZE = 4.0; @@ -834,7 +834,7 @@ final class FieldMappings { * features like plazas. * * Generated from transportation.yaml + * "https://github.com/openmaptiles/openmaptiles/blob/3cf77e2a542d8a369bb08bf2538cdde0b3effb2b/layers/transportation/transportation.yaml">transportation.yaml */ public interface Transportation extends Layer { double BUFFER_SIZE = 4.0; @@ -1178,8 +1178,8 @@ final class FieldMappings { * (building= ). Only buildings with tag * location:underground are excluded. * - * Generated from - * building.yaml + * Generated from building.yaml */ public interface Building extends Layer { double BUFFER_SIZE = 4.0; @@ -1221,7 +1221,7 @@ final class FieldMappings { * from OSM water bodies. Only the most important lakes contain labels. * * Generated from water_name.yaml + * "https://github.com/openmaptiles/openmaptiles/blob/3cf77e2a542d8a369bb08bf2538cdde0b3effb2b/layers/water_name/water_name.yaml">water_name.yaml */ public interface WaterName extends Layer { double BUFFER_SIZE = 256.0; @@ -1291,7 +1291,7 @@ final class FieldMappings { * while for other roads you should use name. * * Generated from transportation_name.yaml + * "https://github.com/openmaptiles/openmaptiles/blob/3cf77e2a542d8a369bb08bf2538cdde0b3effb2b/layers/transportation_name/transportation_name.yaml">transportation_name.yaml */ public interface TransportationName extends Layer { double BUFFER_SIZE = 8.0; @@ -1520,8 +1520,8 @@ final class FieldMappings { * of the more important layers to create a beautiful map. We suggest you use different font styles and sizes to * create a text hierarchy. * - * Generated from - * place.yaml + * Generated from place.yaml */ public interface Place extends Layer { double BUFFER_SIZE = 256.0; @@ -1632,7 +1632,7 @@ final class FieldMappings { * tag are prioritized for preservation). * * Generated from housenumber.yaml + * "https://github.com/openmaptiles/openmaptiles/blob/3cf77e2a542d8a369bb08bf2538cdde0b3effb2b/layers/housenumber/housenumber.yaml">housenumber.yaml */ public interface Housenumber extends Layer { double BUFFER_SIZE = 8.0; @@ -1664,7 +1664,8 @@ final class FieldMappings { * Points of interests containing a of a variety * of OpenStreetMap tags. Mostly contains amenities, sport, shop and tourist POIs. * - * Generated from poi.yaml + * Generated from poi.yaml */ public interface Poi extends Layer { double BUFFER_SIZE = 64.0; @@ -1693,6 +1694,7 @@ final class Fields { * allowed values: *

      *
    • shop + *
    • office *
    • town_hall *
    • golf *
    • fast_food @@ -1788,6 +1790,7 @@ final class Fields { /** Attribute values for map elements in the poi layer. */ final class FieldValues { public static final String CLASS_SHOP = "shop"; + public static final String CLASS_OFFICE = "office"; public static final String CLASS_TOWN_HALL = "town_hall"; public static final String CLASS_GOLF = "golf"; public static final String CLASS_FAST_FOOD = "fast_food"; @@ -1821,8 +1824,8 @@ final class FieldValues { public static final String CLASS_SWIMMING = "swimming"; public static final String CLASS_CASTLE = "castle"; public static final String CLASS_ATM = "atm"; - public static final Set CLASS_VALUES = Set.of("shop", "town_hall", "golf", "fast_food", "park", "bus", - "railway", "aerialway", "entrance", "campsite", "laundry", "grocery", "library", "college", "lodging", + public static final Set CLASS_VALUES = Set.of("shop", "office", "town_hall", "golf", "fast_food", "park", + "bus", "railway", "aerialway", "entrance", "campsite", "laundry", "grocery", "library", "college", "lodging", "ice_cream", "post", "cafe", "school", "alcohol_shop", "bar", "harbor", "car", "hospital", "cemetery", "attraction", "beer", "music", "stadium", "art_gallery", "clothing_store", "swimming", "castle", "atm"); } @@ -1837,6 +1840,16 @@ final class FieldMappings { "locksmith", "lamps", "mall", "massage", "motorcycle", "mobile_phone", "newsagent", "optician", "outdoor", "paint", "perfumery", "perfume", "pet", "photo", "second_hand", "shoes", "sports", "stationery", "tailor", "tattoo", "ticket", "tobacco", "toys", "travel_agency", "watches", "weapons", "wholesale")), + MultiExpression.entry("office", + matchAny("subclass", "accountant", "advertising_agency", "architect", "association", "bail_bond_agent", + "charity", "company", "construction_company", "consulting", "cooperative", "courier", "coworking", + "diplomatic", "educational_institution", "employment_agency", "energy_supplier", "engineer", "estate_agent", + "financial", "financial_advisor", "forestry", "foundation", "geodesist", "government", "graphic_design", + "guide", "harbour_master", "health_insurance", "insurance", "interior_design", "it", "lawyer", "logistics", + "marketing", "moving_company", "newspaper", "ngo", "notary", "physician", "political_party", + "private_investigator", "property_management", "publisher", "quango", "religion", "research", "security", + "surveyor", "tax_advisor", "taxi", "telecommunication", "therapist", "translator", "travel_agent", + "tutoring", "union", "university", "water_utility", "web_design", "wedding_planner")), MultiExpression.entry("town_hall", matchAny("subclass", "townhall", "public_building", "courthouse", "community_centre")), MultiExpression.entry("golf", matchAny("subclass", "golf", "golf_course", "miniature_golf")), @@ -1883,7 +1896,7 @@ final class FieldMappings { * Aerodrome labels * * Generated from aerodrome_label.yaml + * "https://github.com/openmaptiles/openmaptiles/blob/3cf77e2a542d8a369bb08bf2538cdde0b3effb2b/layers/aerodrome_label/aerodrome_label.yaml">aerodrome_label.yaml */ public interface AerodromeLabel extends Layer { double BUFFER_SIZE = 64.0; diff --git a/src/main/java/org/openmaptiles/generated/Tables.java b/src/main/java/org/openmaptiles/generated/Tables.java index dfcf2478..dbbe732d 100644 --- a/src/main/java/org/openmaptiles/generated/Tables.java +++ b/src/main/java/org/openmaptiles/generated/Tables.java @@ -50,8 +50,9 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE /** * OSM element parsers generated from the imposm3 table definitions - * in the OpenMapTiles vector tile - * schema. + * in the OpenMapTiles + * vector tile schema. * * These filter and parse the raw OSM key/value attribute pairs on tags into records with fields that match the columns * in the tables that imposm3 would generate. Layer implementations can "subscribe" to elements from each "table" but @@ -725,7 +726,15 @@ public OsmPoiPoint(SourceFeature source, String mappingKey) { matchAny("leisure", "dog_park", "escape_game", "garden", "golf_course", "ice_rink", "hackerspace", "marina", "miniature_golf", "park", "pitch", "playground", "sports_centre", "stadium", "swimming_area", "swimming_pool", "water_park"), - matchAny("office", "diplomatic"), + matchAny("office", "accountant", "advertising_agency", "architect", "association", "bail_bond_agent", "charity", + "company", "construction_company", "consulting", "cooperative", "courier", "coworking", "diplomatic", + "educational_institution", "employment_agency", "energy_supplier", "engineer", "estate_agent", "financial", + "financial_advisor", "forestry", "foundation", "geodesist", "government", "graphic_design", "guide", + "harbour_master", "health_insurance", "insurance", "interior_design", "it", "lawyer", "logistics", "marketing", + "moving_company", "newspaper", "ngo", "notary", "physician", "political_party", "private_investigator", + "property_management", "publisher", "quango", "religion", "research", "security", "surveyor", "tax_advisor", + "taxi", "telecommunication", "therapist", "translator", "travel_agent", "tutoring", "union", "university", + "water_utility", "web_design", "wedding_planner"), matchAny("railway", "halt", "station", "subway_entrance", "train_station_entrance", "tram_stop"), matchAny("shop", "accessories", "alcohol", "antiques", "art", "bag", "bakery", "beauty", "bed", "beverages", "bicycle", "books", "boutique", "butcher", "camera", "car", "car_repair", "car_parts", "carpet", "charity", @@ -795,7 +804,15 @@ public OsmPoiPolygon(SourceFeature source, String mappingKey) { matchAny("leisure", "dog_park", "escape_game", "garden", "golf_course", "ice_rink", "hackerspace", "marina", "miniature_golf", "park", "pitch", "playground", "sports_centre", "stadium", "swimming_area", "swimming_pool", "water_park"), - matchAny("office", "diplomatic"), + matchAny("office", "accountant", "advertising_agency", "architect", "association", "bail_bond_agent", "charity", + "company", "construction_company", "consulting", "cooperative", "courier", "coworking", "diplomatic", + "educational_institution", "employment_agency", "energy_supplier", "engineer", "estate_agent", "financial", + "financial_advisor", "forestry", "foundation", "geodesist", "government", "graphic_design", "guide", + "harbour_master", "health_insurance", "insurance", "interior_design", "it", "lawyer", "logistics", "marketing", + "moving_company", "newspaper", "ngo", "notary", "physician", "political_party", "private_investigator", + "property_management", "publisher", "quango", "religion", "research", "security", "surveyor", "tax_advisor", + "taxi", "telecommunication", "therapist", "translator", "travel_agent", "tutoring", "union", "university", + "water_utility", "web_design", "wedding_planner"), matchAny("railway", "halt", "station", "subway_entrance", "train_station_entrance", "tram_stop"), matchAny("shop", "accessories", "alcohol", "antiques", "art", "bag", "bakery", "beauty", "bed", "beverages", "bicycle", "books", "boutique", "butcher", "camera", "car", "car_repair", "car_parts", "carpet", "charity", From 7eed2acf4f1c4a37df66ed8b3aef1249dd7f9235 Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Fri, 15 Dec 2023 09:16:18 +0100 Subject: [PATCH 101/106] unit test adjusted for POI office class changes --- src/test/java/org/openmaptiles/layers/PoiTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/org/openmaptiles/layers/PoiTest.java b/src/test/java/org/openmaptiles/layers/PoiTest.java index 9eacb30a..8f63613f 100644 --- a/src/test/java/org/openmaptiles/layers/PoiTest.java +++ b/src/test/java/org/openmaptiles/layers/PoiTest.java @@ -352,7 +352,7 @@ void testGridRank() throws GeometryException { void testEmbassy() { assertFeatures(7, List.of(Map.of( "_layer", "poi", - "class", "diplomatic", + "class", "office", "subclass", "diplomatic", "name", "The Embassy" )), process(pointFeature(Map.of( From 60be59b63f7ee4aec027a7ab7d0b107814a37df7 Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Mon, 18 Dec 2023 21:46:06 +0100 Subject: [PATCH 102/106] regenerate-openmaptiles.sh master (to match content of OMT PR 1544) --- .../generated/OpenMapTilesSchema.java | 62 ++++++++++--------- .../org/openmaptiles/generated/Tables.java | 31 +++++----- 2 files changed, 48 insertions(+), 45 deletions(-) diff --git a/src/main/java/org/openmaptiles/generated/OpenMapTilesSchema.java b/src/main/java/org/openmaptiles/generated/OpenMapTilesSchema.java index 6018d0f7..03014fce 100644 --- a/src/main/java/org/openmaptiles/generated/OpenMapTilesSchema.java +++ b/src/main/java/org/openmaptiles/generated/OpenMapTilesSchema.java @@ -48,9 +48,9 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE import org.openmaptiles.Layer; /** - * All vector tile layer definitions, attributes, and allowed values generated from the OpenMapTiles - * vector tile schema 3cf77e2a542d8a369bb08bf2538cdde0b3effb2b. + * All vector tile layer definitions, attributes, and allowed values generated from the + * OpenMapTiles vector tile schema + * master. */ @SuppressWarnings("unused") public class OpenMapTilesSchema { @@ -95,8 +95,8 @@ public static List createInstances(Translations translations, PlanetilerC * polygons to improve rendering performance. This however can lead to less rendering options in clients since these * boundaries show up. So you might not be able to use border styling for ocean water features. * - * Generated from water.yaml + * Generated from + * water.yaml */ public interface Water extends Layer { double BUFFER_SIZE = 4.0; @@ -194,8 +194,8 @@ final class FieldMappings { * there is also canal generated, starting z13 there is no generalization according to class * field applied. Waterways do not have a subclass field. * - * Generated from waterway.yaml + * Generated from + * waterway.yaml */ public interface Waterway extends Layer { double BUFFER_SIZE = 4.0; @@ -280,7 +280,7 @@ final class FieldMappings { * layer is to style wood (class=wood) and grass (class=grass) areas. * * Generated from landcover.yaml + * "https://github.com/openmaptiles/openmaptiles/blob/master/layers/landcover/landcover.yaml">landcover.yaml */ public interface Landcover extends Layer { double BUFFER_SIZE = 4.0; @@ -436,8 +436,8 @@ final class FieldMappings { * Landuse is used to describe use of land by humans. At lower zoom levels this is from Natural Earth data for * residential (urban) areas and at higher zoom levels mostly OSM landuse tags. * - * Generated from landuse.yaml + * Generated from + * landuse.yaml */ public interface Landuse extends Layer { double BUFFER_SIZE = 4.0; @@ -533,7 +533,7 @@ final class FieldMappings { * Natural peaks * * Generated from mountain_peak.yaml + * "https://github.com/openmaptiles/openmaptiles/blob/master/layers/mountain_peak/mountain_peak.yaml">mountain_peak.yaml */ public interface MountainPeak extends Layer { double BUFFER_SIZE = 64.0; @@ -611,8 +611,8 @@ final class FieldMappings { * Indigenous boundaries are not parks, but they are included in this layer for technical reasons related to data * processing. These boundaries represent areas with special legal and administrative status for indigenous peoples. * - * Generated from park.yaml + * Generated from + * park.yaml */ public interface Park extends Layer { double BUFFER_SIZE = 4.0; @@ -672,8 +672,8 @@ final class FieldMappings { * admin_level * but for most styles it makes sense to just style admin_level=2 and admin_level=4. * - * Generated from boundary.yaml + * Generated from + * boundary.yaml */ public interface Boundary extends Layer { double BUFFER_SIZE = 4.0; @@ -773,8 +773,8 @@ final class FieldMappings { * buildings are contained in the building layer but all other airport related polygons can be found * in the aeroway layer. * - * Generated from aeroway.yaml + * Generated from + * aeroway.yaml */ public interface Aeroway extends Layer { double BUFFER_SIZE = 4.0; @@ -834,7 +834,7 @@ final class FieldMappings { * features like plazas. * * Generated from transportation.yaml + * "https://github.com/openmaptiles/openmaptiles/blob/master/layers/transportation/transportation.yaml">transportation.yaml */ public interface Transportation extends Layer { double BUFFER_SIZE = 4.0; @@ -1178,8 +1178,8 @@ final class FieldMappings { * (building= ). Only buildings with tag * location:underground are excluded. * - * Generated from building.yaml + * Generated from + * building.yaml */ public interface Building extends Layer { double BUFFER_SIZE = 4.0; @@ -1221,7 +1221,7 @@ final class FieldMappings { * from OSM water bodies. Only the most important lakes contain labels. * * Generated from water_name.yaml + * "https://github.com/openmaptiles/openmaptiles/blob/master/layers/water_name/water_name.yaml">water_name.yaml */ public interface WaterName extends Layer { double BUFFER_SIZE = 256.0; @@ -1291,7 +1291,7 @@ final class FieldMappings { * while for other roads you should use name. * * Generated from transportation_name.yaml + * "https://github.com/openmaptiles/openmaptiles/blob/master/layers/transportation_name/transportation_name.yaml">transportation_name.yaml */ public interface TransportationName extends Layer { double BUFFER_SIZE = 8.0; @@ -1520,8 +1520,8 @@ final class FieldMappings { * of the more important layers to create a beautiful map. We suggest you use different font styles and sizes to * create a text hierarchy. * - * Generated from place.yaml + * Generated from + * place.yaml */ public interface Place extends Layer { double BUFFER_SIZE = 256.0; @@ -1632,7 +1632,7 @@ final class FieldMappings { * tag are prioritized for preservation). * * Generated from housenumber.yaml + * "https://github.com/openmaptiles/openmaptiles/blob/master/layers/housenumber/housenumber.yaml">housenumber.yaml */ public interface Housenumber extends Layer { double BUFFER_SIZE = 8.0; @@ -1664,8 +1664,7 @@ final class FieldMappings { * Points of interests containing a of a variety * of OpenStreetMap tags. Mostly contains amenities, sport, shop and tourist POIs. * - * Generated from poi.yaml + * Generated from poi.yaml */ public interface Poi extends Layer { double BUFFER_SIZE = 64.0; @@ -1728,6 +1727,7 @@ final class Fields { *
    • swimming *
    • castle *
    • atm + *
    • fuel *
    */ public static final String CLASS = "class"; @@ -1824,10 +1824,11 @@ final class FieldValues { public static final String CLASS_SWIMMING = "swimming"; public static final String CLASS_CASTLE = "castle"; public static final String CLASS_ATM = "atm"; + public static final String CLASS_FUEL = "fuel"; public static final Set CLASS_VALUES = Set.of("shop", "office", "town_hall", "golf", "fast_food", "park", "bus", "railway", "aerialway", "entrance", "campsite", "laundry", "grocery", "library", "college", "lodging", "ice_cream", "post", "cafe", "school", "alcohol_shop", "bar", "harbor", "car", "hospital", "cemetery", - "attraction", "beer", "music", "stadium", "art_gallery", "clothing_store", "swimming", "castle", "atm"); + "attraction", "beer", "music", "stadium", "art_gallery", "clothing_store", "swimming", "castle", "atm", "fuel"); } /** Complex mappings to generate attribute values from OSM element tags in the poi layer. */ final class FieldMappings { @@ -1889,14 +1890,15 @@ final class FieldMappings { MultiExpression.entry("clothing_store", matchAny("subclass", "bag", "clothes")), MultiExpression.entry("swimming", matchAny("subclass", "swimming_area", "swimming")), MultiExpression.entry("castle", matchAny("subclass", "castle", "ruins")), - MultiExpression.entry("atm", matchAny("subclass", "atm")))); + MultiExpression.entry("atm", matchAny("subclass", "atm")), + MultiExpression.entry("fuel", matchAny("subclass", "fuel", "charging_station")))); } } /** * Aerodrome labels * * Generated from aerodrome_label.yaml + * "https://github.com/openmaptiles/openmaptiles/blob/master/layers/aerodrome_label/aerodrome_label.yaml">aerodrome_label.yaml */ public interface AerodromeLabel extends Layer { double BUFFER_SIZE = 64.0; diff --git a/src/main/java/org/openmaptiles/generated/Tables.java b/src/main/java/org/openmaptiles/generated/Tables.java index dbbe732d..1d2458ed 100644 --- a/src/main/java/org/openmaptiles/generated/Tables.java +++ b/src/main/java/org/openmaptiles/generated/Tables.java @@ -50,9 +50,8 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE /** * OSM element parsers generated from the imposm3 table definitions - * in the OpenMapTiles - * vector tile schema. + * in the OpenMapTiles vector tile + * schema. * * These filter and parse the raw OSM key/value attribute pairs on tags into records with fields that match the columns * in the tables that imposm3 would generate. Layer implementations can "subscribe" to elements from each "table" but @@ -712,12 +711,13 @@ public OsmPoiPoint(SourceFeature source, String mappingKey) { /** Imposm3 "mapping" to filter OSM elements that should appear in this "table". */ public static final Expression MAPPING = and(or(matchAny("aerialway", "station"), matchAny("amenity", "arts_centre", "atm", "bank", "bar", "bbq", "bicycle_parking", "bicycle_rental", "biergarten", - "bus_station", "cafe", "cinema", "clinic", "college", "community_centre", "courthouse", "dentist", "doctors", - "drinking_water", "fast_food", "ferry_terminal", "fire_station", "food_court", "fuel", "grave_yard", "hospital", - "ice_cream", "kindergarten", "library", "marketplace", "motorcycle_parking", "nightclub", "nursing_home", - "parking", "pharmacy", "place_of_worship", "police", "parcel_locker", "post_box", "post_office", "prison", - "pub", "public_building", "recycling", "restaurant", "school", "shelter", "swimming_pool", "taxi", "telephone", - "theatre", "toilets", "townhall", "university", "veterinary", "waste_basket"), + "bus_station", "cafe", "charging_station", "cinema", "clinic", "college", "community_centre", "courthouse", + "dentist", "doctors", "drinking_water", "fast_food", "ferry_terminal", "fire_station", "food_court", "fuel", + "grave_yard", "hospital", "ice_cream", "kindergarten", "library", "marketplace", "motorcycle_parking", + "nightclub", "nursing_home", "parking", "pharmacy", "place_of_worship", "police", "parcel_locker", "post_box", + "post_office", "prison", "pub", "public_building", "recycling", "restaurant", "school", "shelter", + "swimming_pool", "taxi", "telephone", "theatre", "toilets", "townhall", "university", "veterinary", + "waste_basket"), matchAny("barrier", "bollard", "border_control", "cycle_barrier", "gate", "lift_gate", "sally_port", "stile", "toll_booth"), matchAny("building", "dormitory"), matchAny("highway", "bus_stop"), @@ -790,12 +790,13 @@ public OsmPoiPolygon(SourceFeature source, String mappingKey) { /** Imposm3 "mapping" to filter OSM elements that should appear in this "table". */ public static final Expression MAPPING = and(or(matchAny("aerialway", "station"), matchAny("amenity", "arts_centre", "atm", "bank", "bar", "bbq", "bicycle_parking", "bicycle_rental", "biergarten", - "bus_station", "cafe", "cinema", "clinic", "college", "community_centre", "courthouse", "dentist", "doctors", - "drinking_water", "fast_food", "ferry_terminal", "fire_station", "food_court", "fuel", "grave_yard", "hospital", - "ice_cream", "kindergarten", "library", "marketplace", "motorcycle_parking", "nightclub", "nursing_home", - "parking", "pharmacy", "place_of_worship", "police", "parcel_locker", "post_box", "post_office", "prison", - "pub", "public_building", "recycling", "restaurant", "school", "shelter", "swimming_pool", "taxi", "telephone", - "theatre", "toilets", "townhall", "university", "veterinary", "waste_basket"), + "bus_station", "cafe", "charging_station", "cinema", "clinic", "college", "community_centre", "courthouse", + "dentist", "doctors", "drinking_water", "fast_food", "ferry_terminal", "fire_station", "food_court", "fuel", + "grave_yard", "hospital", "ice_cream", "kindergarten", "library", "marketplace", "motorcycle_parking", + "nightclub", "nursing_home", "parking", "pharmacy", "place_of_worship", "police", "parcel_locker", "post_box", + "post_office", "prison", "pub", "public_building", "recycling", "restaurant", "school", "shelter", + "swimming_pool", "taxi", "telephone", "theatre", "toilets", "townhall", "university", "veterinary", + "waste_basket"), matchAny("barrier", "bollard", "border_control", "cycle_barrier", "gate", "lift_gate", "sally_port", "stile", "toll_booth"), matchAny("building", "dormitory"), matchAny("highway", "bus_stop"), From a0b7443b10c1351459d0f5d9bcd496ebea0f3da2 Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Mon, 18 Dec 2023 22:16:40 +0100 Subject: [PATCH 103/106] added charging_station implementation matching OMT PR 1544 --- .../java/org/openmaptiles/layers/Poi.java | 3 ++- .../java/org/openmaptiles/layers/PoiTest.java | 23 +++++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/openmaptiles/layers/Poi.java b/src/main/java/org/openmaptiles/layers/Poi.java index e7e65240..63d4775c 100644 --- a/src/main/java/org/openmaptiles/layers/Poi.java +++ b/src/main/java/org/openmaptiles/layers/Poi.java @@ -122,6 +122,7 @@ public class Poi implements ); private static final Comparator BY_SUBCLASS = Comparator .comparingInt(s -> AGG_STOP_SUBCLASS_ORDER.indexOf(s.subclass())); + private static final Set BRAND_OPERATOR_REF_SUBCLASSES = Set.of("charging_station", "parcel_locker"); private static final double LOG2 = Math.log(2); private static final double SQRT10 = Math.sqrt(10); private final MultiExpression.Index classMapping; @@ -286,7 +287,7 @@ private > expected = List.of(Map.of( + "_layer", "poi", + "class", "fuel", + "subclass", "charging_station", + "name", "Some Charging Station Operator" + )); + assertFeatures(14, expected, process(pointFeature(Map.of( + "amenity", "charging_station", + "brand", "Some Charging Station Operator" + )))); + assertFeatures(14, expected, process(pointFeature(Map.of( + "amenity", "charging_station", + "operator", "Some Charging Station Operator" + )))); + assertFeatures(14, expected, process(pointFeature(Map.of( + "amenity", "charging_station", + "operator", "Some Charging Station", + "ref", "Operator" + )))); + } + private record TestEntry( SourceFeature feature, int expectedZoom From 69393d0b8552f3cc86c8eccd4bc233ef6ed346db Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Tue, 19 Dec 2023 11:29:44 +0100 Subject: [PATCH 104/106] use setMinPixelSizeBelowZoom() instead of uniAreaToMinZoom() --- .../java/org/openmaptiles/layers/Poi.java | 23 ++------- .../java/org/openmaptiles/layers/PoiTest.java | 49 ------------------- 2 files changed, 4 insertions(+), 68 deletions(-) diff --git a/src/main/java/org/openmaptiles/layers/Poi.java b/src/main/java/org/openmaptiles/layers/Poi.java index 63d4775c..084f5835 100644 --- a/src/main/java/org/openmaptiles/layers/Poi.java +++ b/src/main/java/org/openmaptiles/layers/Poi.java @@ -123,8 +123,6 @@ public class Poi implements private static final Comparator BY_SUBCLASS = Comparator .comparingInt(s -> AGG_STOP_SUBCLASS_ORDER.indexOf(s.subclass())); private static final Set BRAND_OPERATOR_REF_SUBCLASSES = Set.of("charging_station", "parcel_locker"); - private static final double LOG2 = Math.log(2); - private static final double SQRT10 = Math.sqrt(10); private final MultiExpression.Index classMapping; private final Translations translations; private final Stats stats; @@ -154,14 +152,6 @@ private int minzoom(String subclass, String mappingKey) { return lowZoom ? 12 : 14; } - public static int uniAreaToMinZoom(double areaWorld) { - double oneSideWorld = Math.sqrt(areaWorld); - double zoom = -(Math.log(oneSideWorld * SQRT10) / LOG2); - int result = (int) Math.floor(zoom - 0.1e-10) + 1; - - return Math.clamp(result, 10, 14); - } - @Override public void release() { aggStops.clear(); @@ -308,16 +298,11 @@ private testEntries, double area, int expectedZoom, String name) { - var feature = polygonFeatureWithArea(area, Map.of( - "name", name, - "amenity", "university" - )); - testEntries.add(new TestEntry( - feature, - Math.clamp(expectedZoom, 10, 14) - )); - } - - @Test - void testUniAreaToMinZoom() throws GeometryException { - // threshold is 1/10 of tile area, hence ... - // ... side is 1/sqrt(10) tile side: from pixels to world coord, for say Z14 ... - //final double PORTION_OF_TILE_SIDE = (256d / Math.sqrt(10)) / Math.pow(2d, 14d + 8d); - // ... and then for some lower zoom: - //double testAreaSide = PORTION_OF_TILE_SIDE * Math.pow(2, 14 - zoom); - // all this then simplified to `testArea` calculation bellow - - final List testEntries = new ArrayList<>(); - for (int zoom = 14; zoom >= 0; zoom--) { - double testArea = Math.pow(4, -zoom) / 10; - - // slightly bellow the threshold - createUniAreaForMinZoomTest(testEntries, testArea * 0.999, zoom + 1, "uni-"); - // precisely at the threshold - createUniAreaForMinZoomTest(testEntries, testArea, zoom, "uni="); - // slightly over the threshold - createUniAreaForMinZoomTest(testEntries, testArea * 1.001, zoom, "uni+"); - } - - for (var entry : testEntries) { - assertFeatures(14, List.of(Map.of( - "_layer", "landuse", - "class", "university" - ), Map.of( - "_layer", "poi", - "_type", "point", - "_minzoom", entry.expectedZoom, - "_maxzoom", 14 - )), process(entry.feature)); - } - } } From bdea32592a21830cc489c8e5354784fdab76e387 Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Wed, 20 Dec 2023 11:49:02 +0100 Subject: [PATCH 105/106] use setMinPixelSizeBelowZoom() instead of getFerryMinzoom() --- .../openmaptiles/layers/Transportation.java | 22 +--------- .../layers/TransportationTest.java | 43 ------------------- 2 files changed, 2 insertions(+), 63 deletions(-) diff --git a/src/main/java/org/openmaptiles/layers/Transportation.java b/src/main/java/org/openmaptiles/layers/Transportation.java index 6cb8bd2d..4aa898ef 100644 --- a/src/main/java/org/openmaptiles/layers/Transportation.java +++ b/src/main/java/org/openmaptiles/layers/Transportation.java @@ -165,7 +165,6 @@ public class Transportation implements private static final Set ONEWAY_VALUES = Set.of(-1, 1); private final Map MINZOOMS; private static final String LIMIT_MERGE_TAG = "__limit_merge"; - private static final double LOG2 = Math.log(2); private final AtomicBoolean loggedNoGb = new AtomicBoolean(false); private final AtomicBoolean loggedNoIreland = new AtomicBoolean(false); private final boolean z13Paths; @@ -584,25 +583,8 @@ public void process(Tables.OsmShipwayLinestring element, FeatureCollector featur .setAttr(Fields.LAYER, nullIfLong(element.layer(), 0)) .setSortKey(element.zOrder()) .setMinPixelSize(0) // merge during post-processing, then limit by size - .setMinZoom(getFerryMinzoom(element)); - } - - /* - * Ferries are supposed to be included in Z4-Z10 depending on their length, for Z11+ always. This implements - * the equivalent of `sql_filter: ST_Length(geometry)>2*ZRES0` in OpenMapTiles to make sure that only longer ferries - * make it into lower zoom. That is needed since ferries are not tagged as highways based on importance, hence length - * is a substitute for importance. - */ - int getFerryMinzoom(Tables.OsmShipwayLinestring element) { - try { - double zoom = -(Math.log(element.source().length()) / LOG2) - 3; - return Math.clamp((int) Math.floor(zoom - 0.1e-10) + 1, 4, 11); - } catch (GeometryException e) { - e.log(stats, "omt_ferry_minzoom", - "Unable to calculate ferry minzoom for " + element.source().id()); - // for Z11+ always, hence 11 as fallback - return 11; - } + .setMinZoom(4) + .setMinPixelSizeBelowZoom(10, 32); // `sql_filter: ST_Length(...)` used in OpenMapTiles translates to 32px } @Override diff --git a/src/test/java/org/openmaptiles/layers/TransportationTest.java b/src/test/java/org/openmaptiles/layers/TransportationTest.java index 5bd13124..d2a6992b 100644 --- a/src/test/java/org/openmaptiles/layers/TransportationTest.java +++ b/src/test/java/org/openmaptiles/layers/TransportationTest.java @@ -2015,49 +2015,6 @@ void testGrade1TracktypePath() { )))); } - private record TestEntry( - SourceFeature feature, - int expectedZoom - ) {} - - private void createFerryForMinZoomTest(List testEntries, double length, int expectedZoom, String name) { - var feature = lineFeatureWithLength(length, Map.of( - "name", name, - "route", "ferry" - )); - testEntries.add(new TestEntry( - feature, - Math.clamp(expectedZoom, 4, 11) - )); - } - - @Test - void testGetFerryMinzoom() throws GeometryException { - final List testEntries = new ArrayList<>(); - for (int zoom = 14; zoom >= 0; zoom--) { - double testLength = Math.pow(2, -zoom - 3); - - // slightly bellow the threshold - createFerryForMinZoomTest(testEntries, testLength * 0.999, zoom + 1, "ferry-"); - // precisely at the threshold - createFerryForMinZoomTest(testEntries, testLength, zoom, "ferry="); - // slightly over the threshold - createFerryForMinZoomTest(testEntries, testLength * 1.001, zoom, "ferry+"); - } - - for (var entry : testEntries) { - assertFeatures(14, List.of(Map.of( - "_layer", "transportation", - "class", "ferry", - "_minzoom", entry.expectedZoom, - "_maxzoom", 14 - ), Map.of( - "_layer", "transportation_name", - "_type", "line" - )), process(entry.feature)); - } - } - @Test void testIssue58() { // test subject: https://www.openstreetmap.org/way/222564359 From 6aa4e88b8efb605dc659be59a3ce61b0c775d353 Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Fri, 22 Dec 2023 09:59:01 +0100 Subject: [PATCH 106/106] fixed unit test, to match recent tweaks --- src/test/java/org/openmaptiles/layers/TransportationTest.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/test/java/org/openmaptiles/layers/TransportationTest.java b/src/test/java/org/openmaptiles/layers/TransportationTest.java index d2a6992b..fe32a98f 100644 --- a/src/test/java/org/openmaptiles/layers/TransportationTest.java +++ b/src/test/java/org/openmaptiles/layers/TransportationTest.java @@ -1809,8 +1809,9 @@ void testFerry() { "_layer", "transportation", "class", "ferry", - "_minzoom", 5, + "_minzoom", 4, "_maxzoom", 14, + "_minpixelsize", 32d, "_type", "line" ), Map.of( "_layer", "transportation_name",