From 0bb61f730620c4d8bc4703103b4cd5d10481aca1 Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Wed, 5 Jul 2023 13:37:21 +0200 Subject: [PATCH 001/211] 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/211] 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/211] 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/211] 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/211] 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/211] 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/211] 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/211] 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/211] 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/211] 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/211] 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/211] 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/211] 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/211] 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/211] 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/211] 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/211] 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/211] 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/211] 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/211] 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/211] 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/211] 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/211] 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/211] 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/211] 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/211] 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/211] 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/211] 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/211] 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/211] 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/211] 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/211] 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/211] 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/211] 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/211] 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/211] 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/211] 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/211] 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/211] 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/211] 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/211] 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/211] 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/211] 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/211] 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/211] 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/211] 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/211] 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/211] 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/211] 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/211] 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/211] 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/211] 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/211] 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/211] 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/211] 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/211] 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/211] 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/211] 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/211] 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/211] 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/211] 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/211] 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/211] 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/211] 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/211] 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/211] 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/211] 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/211] 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/211] 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/211] 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/211] 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/211] 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/211] 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/211] 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/211] 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/211] 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/211] 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/211] 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/211] 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/211] 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/211] (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/211] 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/211] 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/211] 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/211] 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/211] 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/211] 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/211] 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/211] 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/211] 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/211] 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/211] 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/211] 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/211] 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 8fb67075289f3028d31761dcc9564fc597adab36 Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Tue, 28 Nov 2023 11:57:09 +0100 Subject: [PATCH 095/211] MIN_LENGTH value halved, to partially counter the effect of Planetiler applying the limit before merging --- .../openmaptiles/layers/TransportationName.java | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/openmaptiles/layers/TransportationName.java b/src/main/java/org/openmaptiles/layers/TransportationName.java index 4e8fefd8..3e78f8ed 100644 --- a/src/main/java/org/openmaptiles/layers/TransportationName.java +++ b/src/main/java/org/openmaptiles/layers/TransportationName.java @@ -106,13 +106,18 @@ public class TransportationName implements private static final String LINK_TEMP_KEY = "__islink"; private static final String RELATION_ID_TEMP_KEY = "__relid"; + /* + * note: OpenMapTiles (OMT) is applying length limit *after* line merge, Planetiler *before* merge. Hence, + * Planetiler produces fewer features with same limits. To counter that, limits here are 1/2 of those used in OMT. + * Result is not 100% same as from OMT but is closer. + */ private static final ZoomFunction.MeterToPixelThresholds MIN_LENGTH = ZoomFunction.meterThresholds() - .put(6, 20_000) - .put(7, 20_000) - .put(8, 14_000) - .put(9, 8_000) - .put(10, 4_000) - .put(11, 2_000); + .put(6, 10_000) + .put(7, 10_000) + .put(8, 7_000) + .put(9, 4_000) + .put(10, 2_000) + .put(11, 1_000); private final boolean brunnel; private final boolean sizeForShield; private final boolean limitMerge; From 612f700d287bdccc815331eeca79c377f0f253b0 Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Tue, 28 Nov 2023 15:29:02 +0100 Subject: [PATCH 096/211] 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 c3239ada947c5ed9b5bab4f18a380d79cbfabd55 Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Mon, 11 Dec 2023 18:07:50 +0100 Subject: [PATCH 097/211] Revert "MIN_LENGTH value halved, to partially counter the effect of Planetiler applying the limit before merging" This reverts commit 8fb67075289f3028d31761dcc9564fc597adab36. --- .../openmaptiles/layers/TransportationName.java | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/src/main/java/org/openmaptiles/layers/TransportationName.java b/src/main/java/org/openmaptiles/layers/TransportationName.java index 3e78f8ed..4e8fefd8 100644 --- a/src/main/java/org/openmaptiles/layers/TransportationName.java +++ b/src/main/java/org/openmaptiles/layers/TransportationName.java @@ -106,18 +106,13 @@ public class TransportationName implements private static final String LINK_TEMP_KEY = "__islink"; private static final String RELATION_ID_TEMP_KEY = "__relid"; - /* - * note: OpenMapTiles (OMT) is applying length limit *after* line merge, Planetiler *before* merge. Hence, - * Planetiler produces fewer features with same limits. To counter that, limits here are 1/2 of those used in OMT. - * Result is not 100% same as from OMT but is closer. - */ private static final ZoomFunction.MeterToPixelThresholds MIN_LENGTH = ZoomFunction.meterThresholds() - .put(6, 10_000) - .put(7, 10_000) - .put(8, 7_000) - .put(9, 4_000) - .put(10, 2_000) - .put(11, 1_000); + .put(6, 20_000) + .put(7, 20_000) + .put(8, 14_000) + .put(9, 8_000) + .put(10, 4_000) + .put(11, 2_000); private final boolean brunnel; private final boolean sizeForShield; private final boolean limitMerge; From 8d268414bd0e81f9af4af92bbae3fdaa52de0ed1 Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Mon, 11 Dec 2023 18:37:37 +0100 Subject: [PATCH 098/211] use 256px as buffer pixel override instead of MIN_LENGTH Make sure we have enough room (=whole next tile) when checking MIN_LENGTH, to avoid pieces of otherwise "long enough" lines to be excluded (thus creating "holes" or missing ends) in some tiles just because length in that particular tile is bellow limit. Given that MIN_LENGTH values translated to pixels are quite big (compared to {@code BUFFER_SIZE}), such missing pieces would be quite noticeable. This improves items mainly in Z12-Z13. --- .../org/openmaptiles/layers/TransportationName.java | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/openmaptiles/layers/TransportationName.java b/src/main/java/org/openmaptiles/layers/TransportationName.java index 4e8fefd8..28ff92a6 100644 --- a/src/main/java/org/openmaptiles/layers/TransportationName.java +++ b/src/main/java/org/openmaptiles/layers/TransportationName.java @@ -113,6 +113,11 @@ public class TransportationName implements .put(9, 8_000) .put(10, 4_000) .put(11, 2_000); + private static final ZoomFunction BUFFER_PIXEL_OVERRIDES = + ZoomFunction.fromMaxZoomThresholds(Map.of( + 13, 256, + 6, 256 + )); private final boolean brunnel; private final boolean sizeForShield; private final boolean limitMerge; @@ -255,7 +260,7 @@ public void process(Tables.OsmHighwayLinestring element, FeatureCollector featur FeatureCollector.Feature feature = features.line(LAYER_NAME) .setBufferPixels(BUFFER_SIZE) - .setBufferPixelOverrides(MIN_LENGTH) + .setBufferPixelOverrides(BUFFER_PIXEL_OVERRIDES) // TODO abbreviate road names - can't port osml10n because it is AGPL .putAttrs(OmtLanguageUtils.getNames(element.source().tags(), translations)) .setAttr(Fields.REF, ref) @@ -308,7 +313,7 @@ public void process(Tables.OsmAerialwayLinestring element, FeatureCollector feat if (!nullOrEmpty(element.name())) { features.line(LAYER_NAME) .setBufferPixels(BUFFER_SIZE) - .setBufferPixelOverrides(MIN_LENGTH) + .setBufferPixelOverrides(BUFFER_PIXEL_OVERRIDES) .putAttrs(OmtLanguageUtils.getNames(element.source().tags(), translations)) .setAttr(Fields.CLASS, "aerialway") .setAttr(Fields.SUBCLASS, element.aerialway()) @@ -323,7 +328,7 @@ public void process(Tables.OsmShipwayLinestring element, FeatureCollector featur if (!nullOrEmpty(element.name())) { features.line(LAYER_NAME) .setBufferPixels(BUFFER_SIZE) - .setBufferPixelOverrides(MIN_LENGTH) + .setBufferPixelOverrides(BUFFER_PIXEL_OVERRIDES) .putAttrs(OmtLanguageUtils.getNames(element.source().tags(), translations)) .setAttr(Fields.CLASS, element.shipway()) .setMinPixelSize(0) From d7560388d8486497133560a850ace3d102119840 Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Tue, 12 Dec 2023 10:50:02 +0100 Subject: [PATCH 099/211] 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 100/211] 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 101/211] 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 102/211] 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 103/211] 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 104/211] 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 105/211] 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 106/211] 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 107/211] 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 108/211] 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 109/211] 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", From ae2c25ef368f82dd45dba45ce26d983df33e473c Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Mon, 8 Jan 2024 17:58:24 +0100 Subject: [PATCH 110/211] regenerate-openmaptiles.sh master (to match content of OMT PR 1605) --- .../generated/OpenMapTilesSchema.java | 100 ++++++++++++++---- 1 file changed, 78 insertions(+), 22 deletions(-) diff --git a/src/main/java/org/openmaptiles/generated/OpenMapTilesSchema.java b/src/main/java/org/openmaptiles/generated/OpenMapTilesSchema.java index 03014fce..8f3f959a 100644 --- a/src/main/java/org/openmaptiles/generated/OpenMapTilesSchema.java +++ b/src/main/java/org/openmaptiles/generated/OpenMapTilesSchema.java @@ -209,13 +209,20 @@ default String name() { /** Attribute names for map elements in the waterway layer. */ final class Fields { /** - * The OSM name value of the waterway. The - * name field may be empty for NaturalEarth data or at lower zoom levels. + * The OSM name value of the waterway. + * Language-specific values are in name:xx. The name field may be empty for NaturalEarth + * data or at lower zoom levels. */ public static final String NAME = "name"; - /** English name name:en if available, otherwise name. */ + /** + * English name name:en if available, otherwise name. This is deprecated and will be + * removed in a future release in favor of name:en. + */ public static final String NAME_EN = "name_en"; - /** German name name:de if available, otherwise name or name:en. */ + /** + * German name name:de if available, otherwise name or name:en. This is + * deprecated and will be removed in a future release in favor of name:de. + */ public static final String NAME_DE = "name_de"; /** @@ -546,9 +553,15 @@ default String name() { /** Attribute names for map elements in the mountain_peak layer. */ final class Fields { - /** The OSM name value of the peak. */ + /** + * The OSM name value of the peak. + * Language-specific values are in name:xx. + */ public static final String NAME = "name"; - /** English name name:en if available, otherwise name. */ + /** + * English name name:en if available, otherwise name. This is deprecated and will be + * removed in a future release in favor of name:en. + */ public static final String NAME_EN = "name_en"; /** German name name:de if available, otherwise name or name:en. */ public static final String NAME_DE = "name_de"; @@ -640,14 +653,17 @@ final class Fields { public static final String CLASS = "class"; /** * The OSM name value of the park (point - * features only). + * features only). Language-specific values are in name:xx. */ public static final String NAME = "name"; - /** English name name:en if available, otherwise name (point features only). */ + /** + * English name name:en if available, otherwise name (point features only). This is + * deprecated and will be removed in a future release in favor of name:en. + */ public static final String NAME_EN = "name_en"; /** * German name name:de if available, otherwise name or name:en (point - * features only). + * features only). This is deprecated and will be removed in a future release in favor of name:de. */ public static final String NAME_DE = "name_de"; /** Rank of the park within one tile, starting at 1 that is the most important park (point features only). */ @@ -1236,11 +1252,18 @@ default String name() { final class Fields { /** * The OSM name value of the water body. + * Language-specific values are in name:xx. */ public static final String NAME = "name"; - /** English name name:en if available, otherwise name. */ + /** + * English name name:en if available, otherwise name. This is deprecated and will be + * removed in a future release in favor of name:en. + */ public static final String NAME_EN = "name_en"; - /** German name name:de if available, otherwise name or name:en. */ + /** + * German name name:de if available, otherwise name or name:en. This is + * deprecated and will be removed in a future release in favor of name:de. + */ public static final String NAME_DE = "name_de"; /** @@ -1309,9 +1332,15 @@ final class Fields { * of the highway. */ public static final String NAME = "name"; - /** English name name:en if available, otherwise name. */ + /** + * English name name:en if available, otherwise name. This is deprecated and will be + * removed in a future release in favor of name:en. + */ public static final String NAME_EN = "name_en"; - /** German name name:de if available, otherwise name or name:en. */ + /** + * German name name:de if available, otherwise name or name:en. This is + * deprecated and will be removed in a future release in favor of name:de. + */ public static final String NAME_DE = "name_de"; /** * The OSM ref tag of the motorway or its @@ -1534,11 +1563,20 @@ default String name() { /** Attribute names for map elements in the place layer. */ final class Fields { - /** The OSM name value of the POI. */ + /** + * The OSM name value of the place. + * Language-specific values are in name:xx. + */ public static final String NAME = "name"; - /** English name name:en if available, otherwise name. */ + /** + * English name name:en if available, otherwise name. This is deprecated and will be + * removed in a future release in favor of name:en. + */ public static final String NAME_EN = "name_en"; - /** German name name:de if available, otherwise name or name:en. */ + /** + * German name name:de if available, otherwise name or name:en. This is + * deprecated and will be removed in a future release in favor of name:de. + */ public static final String NAME_DE = "name_de"; /** @@ -1677,11 +1715,20 @@ default String name() { /** Attribute names for map elements in the poi layer. */ final class Fields { - /** The OSM name value of the POI. */ + /** + * The OSM name value of the POI. + * Language-specific values are in name:xx. + */ public static final String NAME = "name"; - /** English name name:en if available, otherwise name. */ + /** + * English name name:en if available, otherwise name. This is deprecated and will be + * removed in a future release in favor of name:en. + */ public static final String NAME_EN = "name_en"; - /** German name name:de if available, otherwise name or name:en. */ + /** + * German name name:de if available, otherwise name or name:en. This is + * deprecated and will be removed in a future release in favor of name:de. + */ public static final String NAME_DE = "name_de"; /** @@ -1911,11 +1958,20 @@ default String name() { /** Attribute names for map elements in the aerodrome_label layer. */ final class Fields { - /** The OSM name value of the aerodrome. */ + /** + * The OSM name value of the aerodrome. + * Language-specific values are in name:xx. + */ public static final String NAME = "name"; - /** English name name:en if available, otherwise name. */ + /** + * English name name:en if available, otherwise name. This is deprecated and will be + * removed in a future release in favor of name:en. + */ public static final String NAME_EN = "name_en"; - /** German name name:de if available, otherwise name or name:en. */ + /** + * German name name:de if available, otherwise name or name:en. This is + * deprecated and will be removed in a future release in favor of name:de. + */ public static final String NAME_DE = "name_de"; /** From 802046f8c35cbc48d28f98c8fd5e8e6f76837088 Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Wed, 10 Jan 2024 11:30:25 +0100 Subject: [PATCH 111/211] Fix university office/amenity collision (to match OSM PR 1607) --- src/main/java/org/openmaptiles/layers/Poi.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/main/java/org/openmaptiles/layers/Poi.java b/src/main/java/org/openmaptiles/layers/Poi.java index 084f5835..134e775d 100644 --- a/src/main/java/org/openmaptiles/layers/Poi.java +++ b/src/main/java/org/openmaptiles/layers/Poi.java @@ -139,6 +139,11 @@ static int poiClassRank(String clazz) { } private String poiClass(String subclass, String mappingKey) { + // Special case subclass collision between office=university and amenity=university + if ("amenity".equals(mappingKey) && "university".equals(subclass)) { + return FieldValues.CLASS_COLLEGE; + } + subclass = coalesce(subclass, ""); return classMapping.getOrElse(Map.of( "subclass", subclass, From 0e9f9f99cb5c01f46c73d6f96fd50263c6daee52 Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Wed, 10 Jan 2024 15:09:41 +0100 Subject: [PATCH 112/211] Remove expressway from ramps --- src/main/java/org/openmaptiles/layers/Transportation.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/openmaptiles/layers/Transportation.java b/src/main/java/org/openmaptiles/layers/Transportation.java index 4aa898ef..8e06e24e 100644 --- a/src/main/java/org/openmaptiles/layers/Transportation.java +++ b/src/main/java/org/openmaptiles/layers/Transportation.java @@ -436,6 +436,8 @@ public void process(Tables.OsmHighwayLinestring element, FeatureCollector featur Integer rampAboveZ12 = (highwayRamp || element.isRamp()) ? 1 : null; Integer rampBelowZ12 = highwayRamp ? 1 : null; + boolean expressway = element.expressway() && !"motorway".equals(highway) && !(element.isRamp() || highwayRamp); + FeatureCollector.Feature feature = features.line(LAYER_NAME).setBufferPixels(BUFFER_SIZE) // main attributes at all zoom levels (used for grouping <= z8) .setAttr(Fields.CLASS, highwayClass) @@ -443,7 +445,7 @@ public void process(Tables.OsmHighwayLinestring element, FeatureCollector featur .setAttr(Fields.NETWORK, networkType != null ? networkType.name : null) .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) + .setAttrWithMinzoom(Fields.EXPRESSWAY, expressway ? 1 : null, 8) // z9+ .setAttrWithMinSize(Fields.LAYER, nullIfLong(element.layer(), 0), 4, 9, 12) .setAttrWithMinzoom(Fields.BICYCLE, nullIfEmpty(element.bicycle()), 9) From 7d28732bee9fc8d6b51b9bc84b386d3fca38ef7e Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Tue, 23 Jan 2024 22:09:46 +0100 Subject: [PATCH 113/211] OsmMarinePoint+ne_10m_geography_marine_polys join via name limited to 50km distance --- .../org/openmaptiles/layers/WaterName.java | 72 +++++++++++++------ 1 file changed, 51 insertions(+), 21 deletions(-) diff --git a/src/main/java/org/openmaptiles/layers/WaterName.java b/src/main/java/org/openmaptiles/layers/WaterName.java index cebaeffe..9bbd0127 100644 --- a/src/main/java/org/openmaptiles/layers/WaterName.java +++ b/src/main/java/org/openmaptiles/layers/WaterName.java @@ -83,11 +83,13 @@ public class WaterName implements private static final Logger LOGGER = LoggerFactory.getLogger(WaterName.class); private static final Set SEA_OR_OCEAN_PLACE = Set.of("sea", "ocean"); + private static final double IMPORTANT_MARINE_REGIONS_JOIN_DISTANCE = + GeoUtils.metersToPixelAtEquator(0, 50_000) / 256d; private final Translations translations; // need to synchronize updates from multiple threads private final LongObjectMap lakeCenterlines = Hppc.newLongObjectHashMap(); // may be updated concurrently by multiple threads - private final ConcurrentSkipListMap importantMarinePoints = new ConcurrentSkipListMap<>(); + private ConcurrentSkipListMap importantMarinePoints = new ConcurrentSkipListMap<>(); private final Stats stats; public WaterName(Translations translations, PlanetilerConfig config, Stats stats) { @@ -98,7 +100,7 @@ public WaterName(Translations translations, PlanetilerConfig config, Stats stats @Override public void release() { lakeCenterlines.release(); - importantMarinePoints.clear(); + importantMarinePoints = null; } @Override @@ -133,11 +135,49 @@ public void processNaturalEarth(String table, SourceFeature feature, FeatureColl Integer scalerank = Parse.parseIntOrNull(feature.getTag("scalerank")); if (name != null && scalerank != null) { name = name.replaceAll("\\s+", " ").trim().toLowerCase(); - importantMarinePoints.put(name, scalerank); + try { + importantMarinePoints.put(name, new NaturalEarthRegion(feature.worldGeometry(), scalerank)); + } catch (GeometryException e) { + e.log(stats, "ne_marine_polys", + "Error getting geometry for natural earth feature " + table + " " + feature.getTag("ogc_fid")); + } } } } + private NaturalEarthRegion getImportantMarineRegion(Tables.OsmMarinePoint element) { + var source = element.source(); + String name = element.name().toLowerCase(); + NaturalEarthRegion result = importantMarinePoints.get(name); + if (result == null) { + result = importantMarinePoints.get(source.getString("name:en", "").toLowerCase()); + } + if (result == null) { + result = importantMarinePoints.get(source.getString("name:es", "").toLowerCase()); + } + if (result == null) { + Map.Entry next = importantMarinePoints.ceilingEntry(name); + if (next != null && next.getKey().startsWith(name)) { + result = next.getValue(); + } + } + + if (result == null) { + return null; + } + try { + double distance = result.geometry.distance(source.worldGeometry()); + if (distance <= IMPORTANT_MARINE_REGIONS_JOIN_DISTANCE) { + return result; + } + } catch (GeometryException e) { + e.log(stats, "osm_marine_point", + "Error getting geometry for OSM marine point " + element.source().id()); + } + + return null; + } + @Override public void process(Tables.OsmMarinePoint element, FeatureCollector features) { if (!element.name().isBlank()) { @@ -148,29 +188,14 @@ public void process(Tables.OsmMarinePoint element, FeatureCollector features) { 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")); - String name = element.name().toLowerCase(); - Integer nerank; - if ((nerank = importantMarinePoints.get(name)) != null) { - rank = nerank; - } else if ((nerank = importantMarinePoints.get(source.getString("name:en", "").toLowerCase())) != null) { - rank = nerank; - } else if ((nerank = importantMarinePoints.get(source.getString("name:es", "").toLowerCase())) != null) { - rank = nerank; - } else { - Map.Entry next = importantMarinePoints.ceilingEntry(name); - if (next != null && next.getKey().startsWith(name)) { - rank = next.getValue(); - } + NaturalEarthRegion neRegion = getImportantMarineRegion(element); + if (neRegion != null) { + rank = neRegion.scalerank; } int minZoom; 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; @@ -220,4 +245,9 @@ public void process(Tables.OsmWaterPolygon element, FeatureCollector features) { .setMinZoom(minzoom); } } + + private record NaturalEarthRegion( + Geometry geometry, + int scalerank + ) {} } From da1b7bad96bd260b84e6e2d53656f65f5ceca789 Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Thu, 25 Jan 2024 14:11:15 +0100 Subject: [PATCH 114/211] regenerate-openmaptiles.sh master (to match content of OMT PR 1604) --- .../generated/OpenMapTilesSchema.java | 30 ++++++++++++------- .../org/openmaptiles/generated/Tables.java | 30 +++++++++++++++++-- 2 files changed, 47 insertions(+), 13 deletions(-) diff --git a/src/main/java/org/openmaptiles/generated/OpenMapTilesSchema.java b/src/main/java/org/openmaptiles/generated/OpenMapTilesSchema.java index 8f3f959a..82127e82 100644 --- a/src/main/java/org/openmaptiles/generated/OpenMapTilesSchema.java +++ b/src/main/java/org/openmaptiles/generated/OpenMapTilesSchema.java @@ -619,10 +619,6 @@ final class FieldMappings { * 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 @@ -640,15 +636,14 @@ default String name() { final class Fields { /** * 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 + * layer. 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. The class for boundary=aboriginal_lands is aboriginal_lands. + * similarly assigned. */ public static final String CLASS = "class"; /** @@ -679,7 +674,7 @@ final class FieldMappings { } } /** - * Contains administrative boundaries as linestrings. Until z4 + * Contains administrative boundaries as linestrings and aboriginal lands as polygons. Until z4 * Natural Earth data is used after which OSM boundaries * (boundary=administrative) are @@ -702,6 +697,15 @@ default String name() { /** Attribute names for map elements in the boundary layer. */ final class Fields { + /** + * Use the class to differentiate between different kinds of boundaries. The class for + * boundary=aboriginal_lands is aboriginal_lands. + */ + public static final String CLASS = "class"; + /** + * The OSM name value (area features only). + */ + public static final String NAME = "name"; /** * OSM admin_level * indicating the level of importance of this boundary. The admin_level corresponds to the lowest @@ -1599,7 +1603,8 @@ final class Fields { * Original value of the place tag. * Distinguish between continents, countries, states, islands and places like settlements or smaller entities. Use * class to separately style the different places and build a text hierarchy according to their - * importance. + * importance. For places derived from boundaries, the original value of the + * boundary tag. *

    * allowed values: *

      @@ -1617,6 +1622,7 @@ final class Fields { *
    • "neighbourhood" *
    • "isolated_dwelling" *
    • "island" + *
    • "aboriginal_lands" *
    */ public static final String CLASS = "class"; @@ -1655,8 +1661,10 @@ final class FieldValues { 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", "borough", "suburb", "quarter", "neighbourhood", "isolated_dwelling", "island"); + public static final String CLASS_ABORIGINAL_LANDS = "aboriginal_lands"; + public static final Set CLASS_VALUES = + Set.of("continent", "country", "state", "province", "city", "town", "village", "hamlet", "borough", "suburb", + "quarter", "neighbourhood", "isolated_dwelling", "island", "aboriginal_lands"); } /** Complex mappings to generate attribute values from OSM element tags in the place layer. */ final class FieldMappings { diff --git a/src/main/java/org/openmaptiles/generated/Tables.java b/src/main/java/org/openmaptiles/generated/Tables.java index 1d2458ed..cde3c7e5 100644 --- a/src/main/java/org/openmaptiles/generated/Tables.java +++ b/src/main/java/org/openmaptiles/generated/Tables.java @@ -248,8 +248,9 @@ 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", "aboriginal_lands")), matchType("polygon")); + public static final Expression MAPPING = + and(or(matchAny("leisure", "nature_reserve"), matchAny("boundary", "national_park", "protected_area")), + matchType("polygon")); /** * Interface for layer implementations to extend to subscribe to OSM elements filtered and parsed as @@ -259,6 +260,25 @@ public interface Handler { void process(OsmParkPolygon element, FeatureCollector features); } } + /** An OSM element that would appear in the {@code osm_boundary_polygon} table generated by imposm3. */ + public record OsmBoundaryPolygon(@Override String name, @Override String boundary, @Override SourceFeature source) + implements Row, WithName, WithBoundary, WithSource { + public OsmBoundaryPolygon(SourceFeature source, String mappingKey) { + this(source.getString("name"), source.getString("boundary"), source); + } + + /** Imposm3 "mapping" to filter OSM elements that should appear in this "table". */ + public static final Expression MAPPING = + and(matchAny("boundary", "aboriginal_lands"), matchAny("type", "boundary"), matchType("polygon")); + + /** + * Interface for layer implementations to extend to subscribe to OSM elements filtered and parsed as + * {@link OsmBoundaryPolygon}. + */ + public interface Handler { + void process(OsmBoundaryPolygon element, FeatureCollector features); + } + } /** An OSM element that would appear in the {@code osm_aeroway_polygon} table generated by imposm3. */ public record OsmAerowayPolygon(@Override String ref, @Override String aeroway, @Override SourceFeature source) implements Row, WithRef, WithAeroway, WithSource { @@ -1331,6 +1351,8 @@ public interface WithZOrder { OsmMountainLinestring.MAPPING), MultiExpression.entry(new RowClassAndConstructor(OsmParkPolygon.class, OsmParkPolygon::new), OsmParkPolygon.MAPPING), + MultiExpression.entry(new RowClassAndConstructor(OsmBoundaryPolygon.class, OsmBoundaryPolygon::new), + OsmBoundaryPolygon.MAPPING), MultiExpression.entry(new RowClassAndConstructor(OsmAerowayPolygon.class, OsmAerowayPolygon::new), OsmAerowayPolygon.MAPPING), MultiExpression.entry(new RowClassAndConstructor(OsmAerowayLinestring.class, OsmAerowayLinestring::new), @@ -1406,6 +1428,10 @@ public static Map, List>> generateDis result.computeIfAbsent(OsmParkPolygon.class, cls -> new ArrayList<>()) .add(new RowHandlerAndClass<>(typedHandler.getClass(), typedHandler::process)); } + if (handler instanceof OsmBoundaryPolygon.Handler typedHandler) { + result.computeIfAbsent(OsmBoundaryPolygon.class, cls -> new ArrayList<>()) + .add(new RowHandlerAndClass<>(typedHandler.getClass(), typedHandler::process)); + } if (handler instanceof OsmAerowayPolygon.Handler typedHandler) { result.computeIfAbsent(OsmAerowayPolygon.class, cls -> new ArrayList<>()) .add(new RowHandlerAndClass<>(typedHandler.getClass(), typedHandler::process)); From 2299a696c340a67b4210b35d1af7122719de9da3 Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Thu, 25 Jan 2024 15:30:57 +0100 Subject: [PATCH 115/211] Revert "Add aboriginal lands (as per OMT PR 1489)" This reverts commit 899a0c5718734d5a746cdd84df2e1308614954c0. --- .../java/org/openmaptiles/layers/Park.java | 21 +++++++--------- .../org/openmaptiles/layers/ParkTest.java | 24 ------------------- 2 files changed, 8 insertions(+), 37 deletions(-) diff --git a/src/main/java/org/openmaptiles/layers/Park.java b/src/main/java/org/openmaptiles/layers/Park.java index 026f1f62..648df0f3 100644 --- a/src/main/java/org/openmaptiles/layers/Park.java +++ b/src/main/java/org/openmaptiles/layers/Park.java @@ -91,20 +91,15 @@ public Park(Translations translations, PlanetilerConfig config, Stats stats) { @Override public void process(Tables.OsmParkPolygon element, FeatureCollector features) { - 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 protectionTitle = element.protectionTitle(); + if (protectionTitle != null) { + protectionTitle = protectionTitle.replace(' ', '_').toLowerCase(Locale.ROOT); } + 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 2383a136..a393b211 100644 --- a/src/test/java/org/openmaptiles/layers/ParkTest.java +++ b/src/test/java/org/openmaptiles/layers/ParkTest.java @@ -45,30 +45,6 @@ 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 ab5d1b5c5fea182877c7e5f25bc0a6729efe20c6 Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Fri, 26 Jan 2024 16:23:24 +0100 Subject: [PATCH 116/211] added handling of aboriginal_lands/OsmBoundaryPolygon into Boundary and Place layers --- .../org/openmaptiles/layers/Boundary.java | 14 ++++++ .../java/org/openmaptiles/layers/Place.java | 26 +++++++++-- .../org/openmaptiles/layers/BoundaryTest.java | 22 ++++++++++ .../org/openmaptiles/layers/PlaceTest.java | 43 +++++++++++++++++++ 4 files changed, 102 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/openmaptiles/layers/Boundary.java b/src/main/java/org/openmaptiles/layers/Boundary.java index 83f7f62e..99a29977 100644 --- a/src/main/java/org/openmaptiles/layers/Boundary.java +++ b/src/main/java/org/openmaptiles/layers/Boundary.java @@ -79,6 +79,8 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE import org.locationtech.jts.operation.polygonize.Polygonizer; 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; @@ -95,6 +97,7 @@ public class Boundary implements OpenMapTilesProfile.NaturalEarthProcessor, OpenMapTilesProfile.OsmRelationPreprocessor, OpenMapTilesProfile.OsmAllProcessor, + Tables.OsmBoundaryPolygon.Handler, OpenMapTilesProfile.FeaturePostProcessor, OpenMapTilesProfile.FinishHandler { @@ -127,6 +130,7 @@ public class Boundary implements private final Map> regionGeometries = new HashMap<>(); private final Map> boundariesToMerge = new HashMap<>(); private final PlanetilerConfig config; + private final Translations translations; public Boundary(Translations translations, PlanetilerConfig config, Stats stats) { this.config = config; @@ -141,6 +145,7 @@ public Boundary(Translations translations, PlanetilerConfig config, Stats stats) false ); this.stats = stats; + this.translations = translations; } private static boolean isDisputed(Map tags) { @@ -306,6 +311,15 @@ public void processAllOsm(SourceFeature feature, FeatureCollector features) { } } + @Override + public void process(Tables.OsmBoundaryPolygon element, FeatureCollector features) { + features.polygon(LAYER_NAME).setBufferPixels(BUFFER_SIZE) + .putAttrs(OmtLanguageUtils.getNames(element.source().tags(), translations)) + .setAttr(OpenMapTilesSchema.Boundary.Fields.CLASS, element.boundary()) + .setMinPixelSizeBelowZoom(13, 4) // for Z4: `sql_filter: area>power(ZRES3,2)`, etc. + .setMinZoom(4); + } + @Override public void finish(String sourceName, FeatureCollector.Factory featureCollectors, Consumer emit) { diff --git a/src/main/java/org/openmaptiles/layers/Place.java b/src/main/java/org/openmaptiles/layers/Place.java index ed1e5221..f3545a93 100644 --- a/src/main/java/org/openmaptiles/layers/Place.java +++ b/src/main/java/org/openmaptiles/layers/Place.java @@ -88,6 +88,7 @@ public class Place implements Tables.OsmIslandPoint.Handler, Tables.OsmIslandPolygon.Handler, Tables.OsmCityPoint.Handler, + Tables.OsmBoundaryPolygon.Handler, OpenMapTilesProfile.FeaturePostProcessor { /* @@ -96,8 +97,10 @@ public class Place implements * and minimum zoom level to use for those points. */ - private static final TreeMap ISLAND_AREA_RANKS = new TreeMap<>(Map.of( - Double.MAX_VALUE, 3, + private static final TreeMap AREA_RANKS = new TreeMap<>(Map.of( + Double.MAX_VALUE, 1, + squareMetersToWorldArea(640_000_000), 2, + squareMetersToWorldArea(160_000_000), 3, squareMetersToWorldArea(40_000_000), 4, squareMetersToWorldArea(15_000_000), 5, squareMetersToWorldArea(1_000_000), 6 @@ -282,7 +285,7 @@ public void process(Tables.OsmStatePoint element, FeatureCollector features) { public void process(Tables.OsmIslandPolygon element, FeatureCollector features) { try { double area = element.source().area(); - int rank = ISLAND_AREA_RANKS.ceilingEntry(area).getValue(); + int rank = AREA_RANKS.ceilingEntry(area).getValue(); int minzoom = rank <= 3 ? 8 : rank <= 4 ? 9 : 10; features.pointOnSurface(LAYER_NAME).setBufferPixels(BUFFER_SIZE) @@ -369,6 +372,23 @@ public void process(Tables.OsmCityPoint element, FeatureCollector features) { } } + @Override + public void process(Tables.OsmBoundaryPolygon element, FeatureCollector features) { + try { + int rank = AREA_RANKS.ceilingEntry(element.source().area()).getValue(); + int minzoom = rank <= 4 ? rank + 5 : 10; + + features.centroid(LAYER_NAME).setBufferPixels(BUFFER_SIZE) + .putAttrs(OmtLanguageUtils.getNames(element.source().tags(), translations)) + .setAttr(OpenMapTilesSchema.Boundary.Fields.CLASS, element.boundary()) + .setAttr(Fields.RANK, rank) + .setMinZoom(minzoom); + } catch (GeometryException e) { + e.log(stats, "omt_boundary_poly", + "Unable to get point for OSM boundary polygon " + element.source().id()); + } + } + @Override public List postProcess(int zoom, List items) { // infer the rank field from ordering of the place labels with each label grid square diff --git a/src/test/java/org/openmaptiles/layers/BoundaryTest.java b/src/test/java/org/openmaptiles/layers/BoundaryTest.java index 931371b8..95e56d09 100644 --- a/src/test/java/org/openmaptiles/layers/BoundaryTest.java +++ b/src/test/java/org/openmaptiles/layers/BoundaryTest.java @@ -714,4 +714,26 @@ void testOsmBoundariesOnly() { assertFeatures(14, List.of(Map.of("_minzoom", 1)), processOsmOnly(lineFeatureWithRelation(profile.preprocessOsmRelation(relation), Map.of()))); } + + @Test + void testIndigenousLand() { + assertFeatures(0, List.of(Map.of( + "_layer", "boundary", + "class", "aboriginal_lands", + "name", "Seminole Nation", + "name_en", "Seminole Nation", + "name:latin", "Seminole Nation", + + "_type", "polygon", + "_minzoom", 4 + ), Map.of( + "_layer", "place" + )), process(polygonFeatureWithArea(1, + Map.of( + "type", "boundary", + "boundary", "aboriginal_lands", + "name", "Seminole Nation", + "name:en", "Seminole Nation" + )))); + } } diff --git a/src/test/java/org/openmaptiles/layers/PlaceTest.java b/src/test/java/org/openmaptiles/layers/PlaceTest.java index d12a6e46..384537e1 100644 --- a/src/test/java/org/openmaptiles/layers/PlaceTest.java +++ b/src/test/java/org/openmaptiles/layers/PlaceTest.java @@ -287,6 +287,49 @@ void testIslandPolygon() { )))); } + @Test + void testIndigenousLand() { + assertFeatures(0, List.of(Map.of( + "_layer", "place", + "class", "aboriginal_lands", + "name", "Seminole Nation", + "name_en", "Seminole Nation", + "name:latin", "Seminole Nation", + "rank", 1, + + "_type", "point", + "_minzoom", 6 + ), Map.of( + "_layer", "boundary" + )), process(polygonFeatureWithArea(1, + Map.of( + "type", "boundary", + "boundary", "aboriginal_lands", + "name", "Seminole Nation", + "name:en", "Seminole Nation" + )))); + + double rank2area = Math.pow(GeoUtils.metersToPixelAtEquator(0, Math.sqrt(640_000_000 - 1)) / 256d, 2); + + assertFeatures(0, List.of(Map.of( + "_layer", "place", + "class", "aboriginal_lands", + "name", "Seminole Nation", + "rank", 2, + + "_type", "point", + "_minzoom", 7 + ), Map.of( + "_layer", "boundary" + )), process(polygonFeatureWithArea(rank2area, + Map.of( + "type", "boundary", + "boundary", "aboriginal_lands", + "name", "Seminole Nation", + "name:en", "Seminole Nation" + )))); + } + @Test void testPlaceSortKeyRanking() { int[] sortKeys = new int[]{ From 2cf1f7f014dc9f2b6e1902d7b212009c2c11ca22 Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Fri, 26 Jan 2024 16:24:18 +0100 Subject: [PATCH 117/211] big islands can now get rank lower than 3 --- src/test/java/org/openmaptiles/layers/PlaceTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/org/openmaptiles/layers/PlaceTest.java b/src/test/java/org/openmaptiles/layers/PlaceTest.java index 384537e1..02873f69 100644 --- a/src/test/java/org/openmaptiles/layers/PlaceTest.java +++ b/src/test/java/org/openmaptiles/layers/PlaceTest.java @@ -258,7 +258,7 @@ void testIslandPolygon() { "name", "Nantucket", "name_en", "Nantucket", "name:latin", "Nantucket", - "rank", 3, + "rank", 1, "_type", "point", "_minzoom", 8 From fff48f796feb8e2d2d2d21b1e958fbc68cb290ae Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Sat, 27 Jan 2024 15:37:49 +0100 Subject: [PATCH 118/211] Add e-road and a-road for transportation z4 --- .../openmaptiles/layers/Transportation.java | 14 +++-- .../layers/TransportationTest.java | 54 +++++++++++++++++++ 2 files changed, 65 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/openmaptiles/layers/Transportation.java b/src/main/java/org/openmaptiles/layers/Transportation.java index 8e06e24e..282bfc3f 100644 --- a/src/main/java/org/openmaptiles/layers/Transportation.java +++ b/src/main/java/org/openmaptiles/layers/Transportation.java @@ -142,7 +142,9 @@ public class Transportation implements private static final Set TRUNK_AS_MOTORWAY_BY_NETWORK = Set.of( RouteNetwork.CA_TRANSCANADA, RouteNetwork.CA_PROVINCIAL_ARTERIAL, - RouteNetwork.US_INTERSTATE + RouteNetwork.US_INTERSTATE, + RouteNetwork.E_ROAD, + RouteNetwork.A_ROAD ); private static final Set CA_AB_PRIMARY_AS_ARTERIAL_BY_REF = Set.of( "2", "3", "4" @@ -291,7 +293,11 @@ public List preprocessOsmRelation(OsmElement.Relation relation) String network = relation.getString("network"); String ref = relation.getString("ref"); - if ("US:I".equals(network)) { + if ("e-road".equals(network)) { + networkType = RouteNetwork.E_ROAD; + } else if ("AsianHighway".equals(network)) { + networkType = RouteNetwork.A_ROAD; + } else if ("US:I".equals(network)) { networkType = RouteNetwork.US_INTERSTATE; } else if ("US:US".equals(network)) { networkType = RouteNetwork.US_HIGHWAY; @@ -645,7 +651,9 @@ enum RouteNetwork { 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"); + IE_REGIONAL("ie-regional", "omt-ie-regional"), + E_ROAD("e-road", null), + A_ROAD("a-road", null); final String name; final String network; diff --git a/src/test/java/org/openmaptiles/layers/TransportationTest.java b/src/test/java/org/openmaptiles/layers/TransportationTest.java index fe32a98f..7baf1483 100644 --- a/src/test/java/org/openmaptiles/layers/TransportationTest.java +++ b/src/test/java/org/openmaptiles/layers/TransportationTest.java @@ -2041,4 +2041,58 @@ void testIssue58() { "name:en", "Ayalon South" )), result); } + + @Test + void testARoad() { + var rel = new OsmElement.Relation(1); + rel.setTag("type", "route"); + rel.setTag("route", "road"); + rel.setTag("network", "AsianHighway"); + rel.setTag("ref", "AH11"); + + FeatureCollector features = process(lineFeatureWithRelation( + profile.preprocessOsmRelation(rel), + Map.of( + "highway", "trunk" + ))); + + assertFeatures(13, List.of(Map.of( + "_layer", "transportation", + "class", "trunk", + "network", "a-road", + "_minzoom", 4 + ), Map.of( + "_layer", "transportation_name", + "class", "trunk", + "ref", "AH11", + "network", "a-road" + )), features); + } + + @Test + void testERoad() { + var rel = new OsmElement.Relation(1); + rel.setTag("type", "route"); + rel.setTag("route", "road"); + rel.setTag("network", "e-road"); + rel.setTag("ref", "E 50"); + + FeatureCollector features = process(lineFeatureWithRelation( + profile.preprocessOsmRelation(rel), + Map.of( + "highway", "motorway" + ))); + + assertFeatures(13, List.of(Map.of( + "_layer", "transportation", + "class", "motorway", + "network", "e-road", + "_minzoom", 4 + ), Map.of( + "_layer", "transportation_name", + "class", "motorway", + "ref", "E 50", + "network", "e-road" + )), features); + } } From 3ad70b065bf124619d79f3e8fc6f5a93d3b74d64 Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Sat, 27 Jan 2024 15:41:44 +0100 Subject: [PATCH 119/211] testPolishHighwayIssue165() adjusted: e-road now takes precendence --- src/test/java/org/openmaptiles/layers/TransportationTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/java/org/openmaptiles/layers/TransportationTest.java b/src/test/java/org/openmaptiles/layers/TransportationTest.java index 7baf1483..1b01f087 100644 --- a/src/test/java/org/openmaptiles/layers/TransportationTest.java +++ b/src/test/java/org/openmaptiles/layers/TransportationTest.java @@ -519,8 +519,8 @@ void testPolishHighwayIssue165() { "_layer", "transportation_name", "class", "trunk", "name", "", - "ref", "S7", - "ref_length", 2, + "ref", "E 28", + "ref_length", 4, "route_1", "e-road=E 28", "route_2", "e-road=E 77" )), rendered); From bc12b25734caa1db358d0e9d0318c8da1a6a54c6 Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Mon, 12 Feb 2024 17:26:53 +0100 Subject: [PATCH 120/211] regenerate-openmaptiles.sh master (to match content of OMT PR 1627) --- .../generated/OpenMapTilesSchema.java | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/src/main/java/org/openmaptiles/generated/OpenMapTilesSchema.java b/src/main/java/org/openmaptiles/generated/OpenMapTilesSchema.java index 82127e82..722d3eae 100644 --- a/src/main/java/org/openmaptiles/generated/OpenMapTilesSchema.java +++ b/src/main/java/org/openmaptiles/generated/OpenMapTilesSchema.java @@ -1887,15 +1887,14 @@ final class FieldValues { } /** Complex mappings to generate attribute values from OSM element tags in the poi layer. */ final class FieldMappings { - public static final MultiExpression Class = MultiExpression.of(List.of( - MultiExpression.entry("shop", - matchAny("subclass", "accessories", "antiques", "beauty", "bed", "boutique", "camera", "carpet", "charity", - "chemist", "coffee", "computer", "convenience", "copyshop", "cosmetics", "garden_centre", "doityourself", - "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", - "paint", "perfumery", "perfume", "pet", "photo", "second_hand", "shoes", "sports", "stationery", "tailor", - "tattoo", "ticket", "tobacco", "toys", "travel_agency", "watches", "weapons", "wholesale")), + public static final MultiExpression Class = MultiExpression.of(List.of(MultiExpression.entry("shop", + matchAny("subclass", "accessories", "antiques", "beauty", "bed", "boutique", "camera", "carpet", "charity", + "chemist", "coffee", "computer", "convenience", "confectionery", "copyshop", "cosmetics", "garden_centre", + "doityourself", "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", "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", @@ -1927,7 +1926,7 @@ final class FieldMappings { MultiExpression.entry("lodging", matchAny("subclass", "hotel", "motel", "bed_and_breakfast", "guest_house", "hostel", "chalet", "alpine_hut", "dormitory")), - MultiExpression.entry("ice_cream", matchAny("subclass", "chocolate", "confectionery")), + MultiExpression.entry("ice_cream", matchAny("subclass", "chocolate")), MultiExpression.entry("post", matchAny("subclass", "post_box", "post_office", "parcel_locker")), MultiExpression.entry("cafe", matchAny("subclass", "cafe")), MultiExpression.entry("school", matchAny("subclass", "school", "kindergarten")), From 197e87b939796cb9813cf0202ad5b83e3c56a0a3 Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Tue, 20 Feb 2024 14:52:28 +0100 Subject: [PATCH 121/211] regenerate-openmaptiles.sh fff7110aeb61882abfafe22d1618fbe6181d96cb (to match content of OMT PR 1620) --- .../generated/OpenMapTilesSchema.java | 134 +++++++++++------- .../org/openmaptiles/generated/Tables.java | 82 +++++------ 2 files changed, 124 insertions(+), 92 deletions(-) diff --git a/src/main/java/org/openmaptiles/generated/OpenMapTilesSchema.java b/src/main/java/org/openmaptiles/generated/OpenMapTilesSchema.java index 722d3eae..d86ad220 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 fff7110aeb61882abfafe22d1618fbe6181d96cb. */ @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; @@ -287,7 +287,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/fff7110aeb61882abfafe22d1618fbe6181d96cb/layers/landcover/landcover.yaml">landcover.yaml */ public interface Landcover extends Layer { double BUFFER_SIZE = 4.0; @@ -443,8 +443,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; @@ -540,7 +540,7 @@ final class FieldMappings { * Natural peaks * * Generated from mountain_peak.yaml + * "https://github.com/openmaptiles/openmaptiles/blob/fff7110aeb61882abfafe22d1618fbe6181d96cb/layers/mountain_peak/mountain_peak.yaml">mountain_peak.yaml */ public interface MountainPeak extends Layer { double BUFFER_SIZE = 64.0; @@ -620,8 +620,8 @@ final class FieldMappings { * "https://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; @@ -683,8 +683,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; @@ -793,8 +793,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; @@ -854,7 +854,7 @@ final class FieldMappings { * features like plazas. * * Generated from transportation.yaml + * "https://github.com/openmaptiles/openmaptiles/blob/fff7110aeb61882abfafe22d1618fbe6181d96cb/layers/transportation/transportation.yaml">transportation.yaml */ public interface Transportation extends Layer { double BUFFER_SIZE = 4.0; @@ -1198,8 +1198,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; @@ -1241,7 +1241,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/fff7110aeb61882abfafe22d1618fbe6181d96cb/layers/water_name/water_name.yaml">water_name.yaml */ public interface WaterName extends Layer { double BUFFER_SIZE = 256.0; @@ -1318,7 +1318,7 @@ final class FieldMappings { * while for other roads you should use name. * * Generated from transportation_name.yaml + * "https://github.com/openmaptiles/openmaptiles/blob/fff7110aeb61882abfafe22d1618fbe6181d96cb/layers/transportation_name/transportation_name.yaml">transportation_name.yaml */ public interface TransportationName extends Layer { double BUFFER_SIZE = 8.0; @@ -1465,18 +1465,54 @@ final class Fields { * */ public static final String INDOOR = "indoor"; - /** 1st route concurrency. */ - public static final String ROUTE_1 = "route_1"; - /** 2nd route concurrency. */ - public static final String ROUTE_2 = "route_2"; - /** 3rd route concurrency. */ - public static final String ROUTE_3 = "route_3"; - /** 4th route concurrency. */ - public static final String ROUTE_4 = "route_4"; - /** 5th route concurrency. */ - public static final String ROUTE_5 = "route_5"; - /** 6th route concurrency. */ - public static final String ROUTE_6 = "route_6"; + /** 1st route concurrency network. */ + public static final String ROUTE_1_NETWORK = "route_1_network"; + /** 1st route concurrency ref. */ + public static final String ROUTE_1_REF = "route_1_ref"; + /** 1st route concurrency name. */ + public static final String ROUTE_1_NAME = "route_1_name"; + /** 1st route concurrency colour. */ + public static final String ROUTE_1_COLOUR = "route_1_colour"; + /** 2nd route concurrency network. */ + public static final String ROUTE_2_NETWORK = "route_2_network"; + /** 2nd route concurrency ref. */ + public static final String ROUTE_2_REF = "route_2_ref"; + /** 2nd route concurrency name. */ + public static final String ROUTE_2_NAME = "route_2_name"; + /** 2nd route concurrency colour. */ + public static final String ROUTE_2_COLOUR = "route_2_colour"; + /** 3rd route concurrency network. */ + public static final String ROUTE_3_NETWORK = "route_3_network"; + /** 3rd route concurrency ref. */ + public static final String ROUTE_3_REF = "route_3_ref"; + /** 3rd route concurrency name. */ + public static final String ROUTE_3_NAME = "route_3_name"; + /** 3rd route concurrency colour. */ + public static final String ROUTE_3_COLOUR = "route_3_colour"; + /** 4th route concurrency network. */ + public static final String ROUTE_4_NETWORK = "route_4_network"; + /** 4th route concurrency ref. */ + public static final String ROUTE_4_REF = "route_4_ref"; + /** 4th route concurrency name. */ + public static final String ROUTE_4_NAME = "route_4_name"; + /** 4th route concurrency colour. */ + public static final String ROUTE_4_COLOUR = "route_4_colour"; + /** 5th route concurrency network. */ + public static final String ROUTE_5_NETWORK = "route_5_network"; + /** 5th route concurrency ref. */ + public static final String ROUTE_5_REF = "route_5_ref"; + /** 5th route concurrency name. */ + public static final String ROUTE_5_NAME = "route_5_name"; + /** 5th route concurrency colour. */ + public static final String ROUTE_5_COLOUR = "route_5_colour"; + /** 6th route concurrency network. */ + public static final String ROUTE_6_NETWORK = "route_6_network"; + /** 6th route concurrency ref. */ + public static final String ROUTE_6_REF = "route_6_ref"; + /** 6th route concurrency name. */ + public static final String ROUTE_6_NAME = "route_6_name"; + /** 6th route concurrency colour. */ + public static final String ROUTE_6_COLOUR = "route_6_colour"; } /** Attribute values for map elements in the transportation_name layer. */ final class FieldValues { @@ -1553,8 +1589,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; @@ -1678,7 +1714,7 @@ final class FieldMappings { * tag are prioritized for preservation). * * Generated from housenumber.yaml + * "https://github.com/openmaptiles/openmaptiles/blob/fff7110aeb61882abfafe22d1618fbe6181d96cb/layers/housenumber/housenumber.yaml">housenumber.yaml */ public interface Housenumber extends Layer { double BUFFER_SIZE = 8.0; @@ -1710,7 +1746,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; @@ -1887,14 +1924,15 @@ final class FieldValues { } /** Complex mappings to generate attribute values from OSM element tags in the poi layer. */ final class FieldMappings { - public static final MultiExpression Class = MultiExpression.of(List.of(MultiExpression.entry("shop", - matchAny("subclass", "accessories", "antiques", "beauty", "bed", "boutique", "camera", "carpet", "charity", - "chemist", "coffee", "computer", "convenience", "confectionery", "copyshop", "cosmetics", "garden_centre", - "doityourself", "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", "paint", "perfumery", "perfume", "pet", "photo", "second_hand", "shoes", "sports", "stationery", - "tailor", "tattoo", "ticket", "tobacco", "toys", "travel_agency", "watches", "weapons", "wholesale")), + public static final MultiExpression Class = MultiExpression.of(List.of( + MultiExpression.entry("shop", + matchAny("subclass", "accessories", "antiques", "beauty", "bed", "boutique", "camera", "carpet", "charity", + "chemist", "coffee", "computer", "convenience", "copyshop", "cosmetics", "garden_centre", "doityourself", + "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", + "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", @@ -1926,7 +1964,7 @@ final class FieldMappings { MultiExpression.entry("lodging", matchAny("subclass", "hotel", "motel", "bed_and_breakfast", "guest_house", "hostel", "chalet", "alpine_hut", "dormitory")), - MultiExpression.entry("ice_cream", matchAny("subclass", "chocolate")), + MultiExpression.entry("ice_cream", matchAny("subclass", "chocolate", "confectionery")), MultiExpression.entry("post", matchAny("subclass", "post_box", "post_office", "parcel_locker")), MultiExpression.entry("cafe", matchAny("subclass", "cafe")), MultiExpression.entry("school", matchAny("subclass", "school", "kindergarten")), @@ -1952,7 +1990,7 @@ final class FieldMappings { * Aerodrome labels * * Generated from aerodrome_label.yaml + * "https://github.com/openmaptiles/openmaptiles/blob/fff7110aeb61882abfafe22d1618fbe6181d96cb/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 cde3c7e5..c3da1810 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 @@ -340,24 +341,23 @@ public interface Handler { 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) + @Override String nameEn, @Override String nameDe, @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 { + WithLevel, WithIndoor, WithName, WithNameEn, WithNameDe, 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("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("name:de"), 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); @@ -383,17 +383,17 @@ public interface Handler { /** An OSM element that would appear in the {@code osm_railway_linestring} table generated by imposm3. */ 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 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 { + @Override String nameEn, @Override String nameDe, @Override boolean isTunnel, @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, 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.getBoolean("area"), source.getString("service"), source.getString("usage"), source); + source.getString("name:en"), source.getString("name:de"), source.getBoolean("tunnel"), + source.getBoolean("bridge"), source.getBoolean("ramp"), source.getBoolean("ford"), source.getBoolean("area"), + source.getString("service"), source.getString("usage"), source); } /** Imposm3 "mapping" to filter OSM elements that should appear in this "table". */ @@ -411,16 +411,15 @@ public interface Handler { } /** An OSM element that would appear in the {@code osm_aerialway_linestring} table generated by imposm3. */ public record OsmAerialwayLinestring(@Override String aerialway, @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) - implements Row, WithAerialway, WithZOrder, WithLayer, WithName, WithNameEn, WithNameDe, WithShortName, WithIsTunnel, - WithIsBridge, WithIsRamp, WithIsFord, WithIsOneway, WithIsArea, WithService, WithUsage, WithSource { + @Override String name, @Override String nameEn, @Override String nameDe, @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, WithAerialway, WithZOrder, WithLayer, WithName, WithNameEn, WithNameDe, WithIsTunnel, WithIsBridge, + WithIsRamp, WithIsFord, WithIsOneway, WithIsArea, WithService, WithUsage, WithSource { public OsmAerialwayLinestring(SourceFeature source, String mappingKey) { this(source.getString("aerialway"), 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.getString("name:en"), source.getString("name:de"), 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); } @@ -439,16 +438,16 @@ public interface Handler { } /** An OSM element that would appear in the {@code osm_shipway_linestring} table generated by imposm3. */ 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 boolean isArea, @Override String service, @Override String usage, @Override SourceFeature source) - implements Row, WithShipway, WithZOrder, WithLayer, WithName, WithNameEn, WithNameDe, WithShortName, WithIsTunnel, - WithIsBridge, WithIsRamp, WithIsFord, WithIsArea, WithService, WithUsage, WithSource { + @Override String name, @Override String nameEn, @Override String nameDe, @Override boolean isTunnel, + @Override boolean isBridge, @Override boolean isRamp, @Override boolean isFord, @Override boolean isArea, + @Override String service, @Override String usage, @Override SourceFeature source) + implements Row, WithShipway, WithZOrder, WithLayer, WithName, WithNameEn, WithNameDe, WithIsTunnel, 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.getBoolean("area"), source.getString("service"), source.getString("usage"), source); + source.getString("name:en"), source.getString("name:de"), source.getBoolean("tunnel"), + source.getBoolean("bridge"), source.getBoolean("ramp"), source.getBoolean("ford"), source.getBoolean("area"), + source.getString("service"), source.getString("usage"), source); } /** Imposm3 "mapping" to filter OSM elements that should appear in this "table". */ @@ -1256,11 +1255,6 @@ public interface WithShipway { String shipway(); } - /** Rows with a String shortName attribute. */ - public interface WithShortName { - String shortName(); - } - /** Rows with a SourceFeature source attribute. */ public interface WithSource { SourceFeature source(); From 9de78fcacdb54f6979f405c0c6ad7a087f6e0685 Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Tue, 20 Feb 2024 17:04:21 +0100 Subject: [PATCH 122/211] Expanded road route attributes (to match OMT PR 1620) --- .../openmaptiles/layers/Transportation.java | 17 +- .../layers/TransportationName.java | 14 +- .../layers/TransportationTest.java | 176 ++++++++++++------ 3 files changed, 145 insertions(+), 62 deletions(-) diff --git a/src/main/java/org/openmaptiles/layers/Transportation.java b/src/main/java/org/openmaptiles/layers/Transportation.java index 282bfc3f..8257641d 100644 --- a/src/main/java/org/openmaptiles/layers/Transportation.java +++ b/src/main/java/org/openmaptiles/layers/Transportation.java @@ -291,7 +291,10 @@ public List preprocessOsmRelation(OsmElement.Relation relation) if (relation.hasTag("route", "road", "hiking")) { RouteNetwork networkType = null; String network = relation.getString("network"); - String ref = relation.getString("ref"); + String ref = nullIfEmpty(relation.getString("ref")); + String name = nullIfEmpty(relation.getString("name")); + String colour = coalesce( + nullIfEmpty(relation.getString("colour")), nullIfEmpty(relation.getString("ref:colour"))); if ("e-road".equals(network)) { networkType = RouteNetwork.E_ROAD; @@ -334,7 +337,8 @@ public List preprocessOsmRelation(OsmElement.Relation relation) }; if (network != null || rank < 3) { - return List.of(new RouteRelation(coalesce(ref, ""), network, networkType, (byte) rank, relation.id())); + return List + .of(new RouteRelation(coalesce(ref, ""), network, name, colour, networkType, (byte) rank, relation.id())); } } return null; @@ -372,6 +376,7 @@ List getRouteRelations(Tables.OsmHighwayLinestring element) { }; result.add(new RouteRelation(refMatcher.group(), networkType == null ? null : networkType.network, + null, null, networkType, (byte) -1, 0)); } } catch (GeometryException e) { @@ -397,7 +402,9 @@ List getRouteRelations(Tables.OsmHighwayLinestring element) { case "trunk", "primary" -> RouteNetwork.IE_NATIONAL; default -> RouteNetwork.IE_REGIONAL; }; - result.add(new RouteRelation(refMatcher.group(), networkType.network, networkType, (byte) -1, 0)); + result.add(new RouteRelation(refMatcher.group(), + networkType.network, null, null, + networkType, (byte) -1, 0)); } } catch (GeometryException e) { e.log(stats, "omt_transportation_name_ie_test", @@ -668,6 +675,8 @@ enum RouteNetwork { record RouteRelation( String ref, String network, + String name, + String colour, RouteNetwork networkType, byte rank, @Override long id @@ -679,6 +688,8 @@ public long estimateMemoryUsageBytes() { MemoryEstimator.estimateSize(rank) + POINTER_BYTES + estimateSize(ref) + POINTER_BYTES + estimateSize(network) + + POINTER_BYTES + estimateSize(name) + + POINTER_BYTES + estimateSize(colour) + POINTER_BYTES + // networkType MemoryEstimator.estimateSizeLong(id); } diff --git a/src/main/java/org/openmaptiles/layers/TransportationName.java b/src/main/java/org/openmaptiles/layers/TransportationName.java index 28ff92a6..f5561f78 100644 --- a/src/main/java/org/openmaptiles/layers/TransportationName.java +++ b/src/main/java/org/openmaptiles/layers/TransportationName.java @@ -274,12 +274,16 @@ public void process(Tables.OsmHighwayLinestring element, FeatureCollector featur .setSortKey(element.zOrder()) .setMinZoom(minzoom); - // populate route_1, route_2, ... route_n tags and remove duplicates - Set routes = new HashSet<>(); + // populate route_1_, route_2_, ... route_n_ tags and remove duplicates + Set routes = new HashSet<>(); for (var route : relations) { - String routeString = route.network() + "=" + coalesce(route.ref(), ""); - if (routes.add(routeString)) { - feature.setAttr("route_" + routes.size(), routeString); + if (routes.add(route.id())) { + String keyPrefix = "route_" + routes.size() + "_"; + + feature.setAttr(keyPrefix + "network", route.network()); + feature.setAttr(keyPrefix + "ref", route.ref()); + feature.setAttr(keyPrefix + "name", route.name()); + feature.setAttr(keyPrefix + "colour", route.colour()); } } diff --git a/src/test/java/org/openmaptiles/layers/TransportationTest.java b/src/test/java/org/openmaptiles/layers/TransportationTest.java index 1b01f087..2210aa65 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 java.util.Map.entry; import static org.junit.jupiter.api.Assertions.assertFalse; import com.onthegomap.planetiler.FeatureCollector; @@ -280,17 +281,18 @@ void testInterstateMotorway() { "horse", "no", "brunnel", "bridge", "_minzoom", 4 - ), Map.of( - "_layer", "transportation_name", - "class", "motorway", - "name", "Massachusetts Turnpike", - "name_en", "Massachusetts Turnpike", - "ref", "90", - "ref_length", 2, - "network", "us-interstate", - "route_1", "US:I=90", - "brunnel", "", - "_minzoom", 6 + ), Map.ofEntries( + entry("_layer", "transportation_name"), + entry("class", "motorway"), + entry("name", "Massachusetts Turnpike"), + entry("name_en", "Massachusetts Turnpike"), + entry("ref", "90"), + entry("ref_length", 2), + entry("network", "us-interstate"), + entry("route_1_network", "US:I"), + entry("route_1_ref", "90"), + entry("brunnel", ""), + entry("_minzoom", 6) )), features); assertFeatures(8, List.of(Map.of( @@ -304,17 +306,18 @@ void testInterstateMotorway() { "horse", "", "brunnel", "bridge", "_minzoom", 4 - ), Map.of( - "_layer", "transportation_name", - "class", "motorway", - "name", "Massachusetts Turnpike", - "name_en", "Massachusetts Turnpike", - "ref", "90", - "ref_length", 2, - "network", "us-interstate", - "route_1", "US:I=90", - "brunnel", "", - "_minzoom", 6 + ), Map.ofEntries( + entry("_layer", "transportation_name"), + entry("class", "motorway"), + entry("name", "Massachusetts Turnpike"), + entry("name_en", "Massachusetts Turnpike"), + entry("ref", "90"), + entry("ref_length", 2), + entry("network", "us-interstate"), + entry("route_1_network", "US:I"), + entry("route_1_ref", "90"), + entry("brunnel", ""), + entry("_minzoom", 6) )), features); } @@ -326,7 +329,7 @@ void testDuplicateRoute() { rel1.setTag("network", "US:OK"); rel1.setTag("ref", "104"); rel1.setTag("direction", "north"); - var rel2 = new OsmElement.Relation(2); + var rel2 = new OsmElement.Relation(1); rel2.setTag("type", "route"); rel2.setTag("route", "road"); rel2.setTag("network", "US:OK"); @@ -357,8 +360,10 @@ void testDuplicateRoute() { "ref", "104", "ref_length", 3, "network", "us-state", - "route_1", "US:OK=104", - "route_2", "", + "route_1_network", "US:OK", + "route_1_ref", "104", + "route_2_network", "", + "route_2_ref", "", "_minzoom", 8 )), features); } @@ -372,7 +377,7 @@ void testRouteWithoutNetworkType() { rel1.setTag("ref", "NJTP"); rel1.setTag("name", "New Jersey Turnpike (mainline)"); - var rel2 = new OsmElement.Relation(1); + var rel2 = new OsmElement.Relation(2); rel2.setTag("type", "route"); rel2.setTag("route", "road"); rel2.setTag("network", "US:I"); @@ -400,8 +405,55 @@ void testRouteWithoutNetworkType() { "name", "New Jersey Turnpike", "ref", "95", "ref_length", 2, - "route_1", "US:I=95", - "route_2", "US:NJ:NJTP=NJTP", + "route_1_network", "US:I", + "route_1_ref", "95", + "route_2_network", "US:NJ:NJTP", + "route_2_ref", "NJTP", + "_minzoom", 6 + )), rendered); + } + + @Test + void testRouteWithColour() { + var rel1 = new OsmElement.Relation(1); + rel1.setTag("type", "route"); + rel1.setTag("route", "road"); + rel1.setTag("network", "US:NJ:NJTP"); + rel1.setTag("ref", "NJTP"); + rel1.setTag("name", "New Jersey Turnpike (mainline)"); + rel1.setTag("colour", "#FFFFFF"); + rel1.setTag("ref:colour", "#888888"); + + var rel2 = new OsmElement.Relation(2); + rel2.setTag("type", "route"); + rel2.setTag("route", "road"); + rel2.setTag("network", "US:I"); + rel2.setTag("ref", "95"); + rel2.setTag("name", "I 95 (NJ)"); + rel2.setTag("ref:colour", "#000000"); + + FeatureCollector rendered = process(lineFeatureWithRelation( + Stream.concat( + profile.preprocessOsmRelation(rel1).stream(), + profile.preprocessOsmRelation(rel2).stream() + ).toList(), + Map.of( + "highway", "motorway", + "name", "New Jersey Turnpike", + "ref", "I 95;NJTP" + ))); + + assertFeatures(13, List.of(mapOf( + "_layer", "transportation", + "class", "motorway", + "_minzoom", 4 + ), Map.of( + "_layer", "transportation_name", + "class", "motorway", + "route_1_network", "US:I", + "route_1_colour", "#000000", + "route_2_network", "US:NJ:NJTP", + "route_2_colour", "#FFFFFF", "_minzoom", 6 )), rendered); } @@ -411,7 +463,7 @@ void testSegmentWithManyRoutes() { List relations = new ArrayList<>(); for (int route = 1; route <= 16; route++) { int num = (route + 1) / 2; // to make dups - var rel = new OsmElement.Relation(route); + var rel = new OsmElement.Relation(num); rel.setTag("type", "route"); rel.setTag("route", "road"); rel.setTag("network", "US:I"); @@ -434,15 +486,24 @@ void testSegmentWithManyRoutes() { "_minzoom", 4 ), mapOf( "_layer", "transportation_name", - "route_1", "US:I=1", - "route_2", "US:I=2", - "route_3", "US:I=3", - "route_4", "US:I=4", - "route_5", "US:I=5", - "route_6", "US:I=6", - "route_7", "US:I=7", - "route_8", "US:I=8", - "route_9", "" + "route_1_network", "US:I", + "route_1_ref", "1", + "route_2_network", "US:I", + "route_2_ref", "2", + "route_3_network", "US:I", + "route_3_ref", "3", + "route_4_network", "US:I", + "route_4_ref", "4", + "route_5_network", "US:I", + "route_5_ref", "5", + "route_6_network", "US:I", + "route_6_ref", "6", + "route_7_network", "US:I", + "route_7_ref", "7", + "route_8_network", "US:I", + "route_8_ref", "8", + "route_9_network", "", + "route_9_ref", "" )), rendered); } @@ -521,8 +582,10 @@ void testPolishHighwayIssue165() { "name", "", "ref", "E 28", "ref_length", 4, - "route_1", "e-road=E 28", - "route_2", "e-road=E 77" + "route_1_network", "e-road", + "route_1_ref", "E 28", + "route_2_network", "e-road", + "route_2_ref", "E 77" )), rendered); } @@ -601,7 +664,8 @@ void testInterstateMotorwayWithoutWayInfo() { "ref_length", 2, "network", "us-interstate", "brunnel", "", - "route_1", "US:I=90", + "route_1_network", "US:I", + "route_1_ref", "90", "_minzoom", 6 )), features); } @@ -839,17 +903,19 @@ void testUSAndStateHighway() { "ramp", "", "network", "us-highway", "_minzoom", 7 - ), Map.of( - "_layer", "transportation_name", - "class", "primary", - "name", "Memorial Drive", - "name_en", "Memorial Drive", - "ref", "3", - "ref_length", 1, - "network", "us-highway", - "route_1", "US:US=3", - "route_2", "US:MA=2", - "_minzoom", 12 + ), Map.ofEntries( + entry("_layer", "transportation_name"), + entry("class", "primary"), + entry("name", "Memorial Drive"), + entry("name_en", "Memorial Drive"), + entry("ref", "3"), + entry("ref_length", 1), + entry("network", "us-highway"), + entry("route_1_network", "US:US"), + entry("route_1_ref", "3"), + entry("route_2_network", "US:MA"), + entry("route_2_ref", "2"), + entry("_minzoom", 12) )), process(lineFeatureWithRelation( Stream.concat( profile.preprocessOsmRelation(relUS).stream(), @@ -869,8 +935,10 @@ void testUSAndStateHighway() { ), Map.of( "_layer", "transportation_name", "class", "primary", - "route_1", "US:US=3", - "route_2", "US:MA=2", + "route_1_network", "US:US", + "route_1_ref", "3", + "route_2_network", "US:MA", + "route_2_ref", "2", "ref", "3", "network", "us-highway" )), process(lineFeatureWithRelation( From 27f670615555be8e855cb6f7bfe15cb9508f8674 Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Tue, 20 Feb 2024 17:22:05 +0100 Subject: [PATCH 123/211] unrelated clean-up: use getFirst() --- 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 8257641d..e0ac379b 100644 --- a/src/main/java/org/openmaptiles/layers/Transportation.java +++ b/src/main/java/org/openmaptiles/layers/Transportation.java @@ -419,7 +419,7 @@ List getRouteRelations(Tables.OsmHighwayLinestring element) { RouteRelation getRouteRelation(Tables.OsmHighwayLinestring element) { List all = getRouteRelations(element); - return all.isEmpty() ? null : all.get(0); + return all.isEmpty() ? null : all.getFirst(); } @Override From 088eaa6259072330256287a1d1f3eba199938c48 Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Tue, 20 Feb 2024 19:58:14 +0100 Subject: [PATCH 124/211] adjusted handling or null and empty ref --- src/main/java/org/openmaptiles/layers/Transportation.java | 2 +- src/main/java/org/openmaptiles/layers/TransportationName.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 e0ac379b..ba828a44 100644 --- a/src/main/java/org/openmaptiles/layers/Transportation.java +++ b/src/main/java/org/openmaptiles/layers/Transportation.java @@ -291,7 +291,7 @@ public List preprocessOsmRelation(OsmElement.Relation relation) if (relation.hasTag("route", "road", "hiking")) { RouteNetwork networkType = null; String network = relation.getString("network"); - String ref = nullIfEmpty(relation.getString("ref")); + String ref = relation.getString("ref"); String name = nullIfEmpty(relation.getString("name")); String colour = coalesce( nullIfEmpty(relation.getString("colour")), nullIfEmpty(relation.getString("ref:colour"))); diff --git a/src/main/java/org/openmaptiles/layers/TransportationName.java b/src/main/java/org/openmaptiles/layers/TransportationName.java index f5561f78..1d78469d 100644 --- a/src/main/java/org/openmaptiles/layers/TransportationName.java +++ b/src/main/java/org/openmaptiles/layers/TransportationName.java @@ -281,7 +281,7 @@ public void process(Tables.OsmHighwayLinestring element, FeatureCollector featur String keyPrefix = "route_" + routes.size() + "_"; feature.setAttr(keyPrefix + "network", route.network()); - feature.setAttr(keyPrefix + "ref", route.ref()); + feature.setAttr(keyPrefix + "ref", nullIfEmpty(route.ref())); feature.setAttr(keyPrefix + "name", route.name()); feature.setAttr(keyPrefix + "colour", route.colour()); } From 9299238fe3d5c0ad536112f69dd940fa374d601f Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Tue, 20 Feb 2024 20:01:02 +0100 Subject: [PATCH 125/211] regenerate-openmaptiles.sh master (to match content of OMT PR 1620) --- .../generated/OpenMapTilesSchema.java | 70 +++++++++---------- .../org/openmaptiles/generated/Tables.java | 5 +- 2 files changed, 37 insertions(+), 38 deletions(-) diff --git a/src/main/java/org/openmaptiles/generated/OpenMapTilesSchema.java b/src/main/java/org/openmaptiles/generated/OpenMapTilesSchema.java index d86ad220..676b5a46 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 fff7110aeb61882abfafe22d1618fbe6181d96cb. + * 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; @@ -287,7 +287,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; @@ -443,8 +443,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; @@ -540,7 +540,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; @@ -620,8 +620,8 @@ final class FieldMappings { * "https://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; @@ -683,8 +683,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; @@ -793,8 +793,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; @@ -854,7 +854,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; @@ -1198,8 +1198,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; @@ -1241,7 +1241,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; @@ -1318,7 +1318,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; @@ -1589,8 +1589,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; @@ -1714,7 +1714,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; @@ -1746,8 +1746,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; @@ -1927,12 +1926,13 @@ final class FieldMappings { public static final MultiExpression Class = MultiExpression.of(List.of( MultiExpression.entry("shop", matchAny("subclass", "accessories", "antiques", "beauty", "bed", "boutique", "camera", "carpet", "charity", - "chemist", "coffee", "computer", "convenience", "copyshop", "cosmetics", "garden_centre", "doityourself", - "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", - "paint", "perfumery", "perfume", "pet", "photo", "second_hand", "shoes", "sports", "stationery", "tailor", - "tattoo", "ticket", "tobacco", "toys", "travel_agency", "watches", "weapons", "wholesale")), + "chemist", "chocolate", "coffee", "computer", "convenience", "confectionery", "copyshop", "cosmetics", + "garden_centre", "doityourself", "erotic", "electronics", "fabric", "florist", "frozen_food", "furniture", + "video_games", "video", "general", "gift", "hardware", "hearing_aids", "hifi", "interior_decoration", + "jewelry", "kiosk", "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", @@ -1964,7 +1964,7 @@ final class FieldMappings { MultiExpression.entry("lodging", matchAny("subclass", "hotel", "motel", "bed_and_breakfast", "guest_house", "hostel", "chalet", "alpine_hut", "dormitory")), - MultiExpression.entry("ice_cream", matchAny("subclass", "chocolate", "confectionery")), + MultiExpression.entry("ice_cream", matchAny("subclass", "ice_cream")), MultiExpression.entry("post", matchAny("subclass", "post_box", "post_office", "parcel_locker")), MultiExpression.entry("cafe", matchAny("subclass", "cafe")), MultiExpression.entry("school", matchAny("subclass", "school", "kindergarten")), @@ -1990,7 +1990,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 c3da1810..88fa4ec2 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 66349555b99b9ae7afb33cb4dcbba501bb009870 Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Wed, 28 Feb 2024 19:09:38 +0100 Subject: [PATCH 126/211] pointOnSurface() used instead of centroid() to make sure the point is within the boundary --- src/main/java/org/openmaptiles/layers/Place.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/openmaptiles/layers/Place.java b/src/main/java/org/openmaptiles/layers/Place.java index f3545a93..f764d15f 100644 --- a/src/main/java/org/openmaptiles/layers/Place.java +++ b/src/main/java/org/openmaptiles/layers/Place.java @@ -378,7 +378,7 @@ public void process(Tables.OsmBoundaryPolygon element, FeatureCollector features int rank = AREA_RANKS.ceilingEntry(element.source().area()).getValue(); int minzoom = rank <= 4 ? rank + 5 : 10; - features.centroid(LAYER_NAME).setBufferPixels(BUFFER_SIZE) + features.pointOnSurface(LAYER_NAME).setBufferPixels(BUFFER_SIZE) .putAttrs(OmtLanguageUtils.getNames(element.source().tags(), translations)) .setAttr(OpenMapTilesSchema.Boundary.Fields.CLASS, element.boundary()) .setAttr(Fields.RANK, rank) From b7e3fb40a297efb19ca775afdb03aa64fb4063f6 Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Wed, 28 Feb 2024 20:03:05 +0100 Subject: [PATCH 127/211] adjusted deduplication of route__ attributes --- .../org/openmaptiles/layers/TransportationName.java | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/openmaptiles/layers/TransportationName.java b/src/main/java/org/openmaptiles/layers/TransportationName.java index 1d78469d..4ee1041f 100644 --- a/src/main/java/org/openmaptiles/layers/TransportationName.java +++ b/src/main/java/org/openmaptiles/layers/TransportationName.java @@ -43,6 +43,7 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE import com.carrotsearch.hppc.LongArrayList; import com.carrotsearch.hppc.LongByteMap; import com.carrotsearch.hppc.LongHashSet; +import com.carrotsearch.hppc.LongObjectMap; import com.carrotsearch.hppc.LongSet; import com.onthegomap.planetiler.FeatureCollector; import com.onthegomap.planetiler.FeatureMerge; @@ -62,6 +63,7 @@ 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.Function; +import org.locationtech.jts.geom.prep.PreparedGeometry; import org.openmaptiles.OpenMapTilesProfile; import org.openmaptiles.generated.OpenMapTilesSchema; import org.openmaptiles.generated.Tables; @@ -275,9 +277,13 @@ public void process(Tables.OsmHighwayLinestring element, FeatureCollector featur .setMinZoom(minzoom); // populate route_1_, route_2_, ... route_n_ tags and remove duplicates - Set routes = new HashSet<>(); + Set routes = new HashSet<>(); for (var route : relations) { - if (routes.add(route.id())) { + String routeString = route.network() + "=" + + coalesce(route.ref(), "") + "=" + + coalesce(route.name(), "") + "=" + + coalesce(route.colour(), ""); + if (routes.add(routeString)) { String keyPrefix = "route_" + routes.size() + "_"; feature.setAttr(keyPrefix + "network", route.network()); From e36cb3c4ab0c8d1d70783b7d0ae53db0ed6712d6 Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Wed, 28 Feb 2024 20:07:25 +0100 Subject: [PATCH 128/211] clean-up of some previous adjustments of importantMarinePoints --- src/main/java/org/openmaptiles/layers/WaterName.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/openmaptiles/layers/WaterName.java b/src/main/java/org/openmaptiles/layers/WaterName.java index 9bbd0127..cd3a1a8b 100644 --- a/src/main/java/org/openmaptiles/layers/WaterName.java +++ b/src/main/java/org/openmaptiles/layers/WaterName.java @@ -89,7 +89,7 @@ public class WaterName implements // need to synchronize updates from multiple threads private final LongObjectMap lakeCenterlines = Hppc.newLongObjectHashMap(); // may be updated concurrently by multiple threads - private ConcurrentSkipListMap importantMarinePoints = new ConcurrentSkipListMap<>(); + private final ConcurrentSkipListMap importantMarinePoints = new ConcurrentSkipListMap<>(); private final Stats stats; public WaterName(Translations translations, PlanetilerConfig config, Stats stats) { @@ -100,7 +100,7 @@ public WaterName(Translations translations, PlanetilerConfig config, Stats stats @Override public void release() { lakeCenterlines.release(); - importantMarinePoints = null; + importantMarinePoints.clear(); } @Override From 1d8dbabb394d7dfe23d2d4d14adb8c3934780a4d Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Wed, 28 Feb 2024 21:19:51 +0100 Subject: [PATCH 129/211] clean-up: removed unused imports --- src/main/java/org/openmaptiles/layers/TransportationName.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/main/java/org/openmaptiles/layers/TransportationName.java b/src/main/java/org/openmaptiles/layers/TransportationName.java index 4ee1041f..714ce776 100644 --- a/src/main/java/org/openmaptiles/layers/TransportationName.java +++ b/src/main/java/org/openmaptiles/layers/TransportationName.java @@ -43,7 +43,6 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE import com.carrotsearch.hppc.LongArrayList; import com.carrotsearch.hppc.LongByteMap; import com.carrotsearch.hppc.LongHashSet; -import com.carrotsearch.hppc.LongObjectMap; import com.carrotsearch.hppc.LongSet; import com.onthegomap.planetiler.FeatureCollector; import com.onthegomap.planetiler.FeatureMerge; @@ -63,7 +62,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.Function; -import org.locationtech.jts.geom.prep.PreparedGeometry; import org.openmaptiles.OpenMapTilesProfile; import org.openmaptiles.generated.OpenMapTilesSchema; import org.openmaptiles.generated.Tables; From 8a97fd6e0c619886f554dee4c3399f4803c701a3 Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Wed, 28 Feb 2024 22:51:18 +0100 Subject: [PATCH 130/211] mvn spotless:apply --- .../java/org/openmaptiles/layers/TransportationName.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/openmaptiles/layers/TransportationName.java b/src/main/java/org/openmaptiles/layers/TransportationName.java index 714ce776..2ac9f400 100644 --- a/src/main/java/org/openmaptiles/layers/TransportationName.java +++ b/src/main/java/org/openmaptiles/layers/TransportationName.java @@ -278,9 +278,9 @@ public void process(Tables.OsmHighwayLinestring element, FeatureCollector featur Set routes = new HashSet<>(); for (var route : relations) { String routeString = route.network() + "=" + - coalesce(route.ref(), "") + "=" + - coalesce(route.name(), "") + "=" + - coalesce(route.colour(), ""); + coalesce(route.ref(), "") + "=" + + coalesce(route.name(), "") + "=" + + coalesce(route.colour(), ""); if (routes.add(routeString)) { String keyPrefix = "route_" + routes.size() + "_"; From 21032b8c3a2de50dd8ab9a34690ce15e688603ef Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Mon, 11 Mar 2024 09:30:24 +0100 Subject: [PATCH 131/211] OsmWaterPolygon minzoom calculation reverted to v3.14 and then adjusted for new 1/4 threshold --- .../org/openmaptiles/layers/WaterName.java | 71 +++++++++++-------- 1 file changed, 43 insertions(+), 28 deletions(-) diff --git a/src/main/java/org/openmaptiles/layers/WaterName.java b/src/main/java/org/openmaptiles/layers/WaterName.java index cd3a1a8b..27d8bc36 100644 --- a/src/main/java/org/openmaptiles/layers/WaterName.java +++ b/src/main/java/org/openmaptiles/layers/WaterName.java @@ -82,6 +82,7 @@ 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 static final double IMPORTANT_MARINE_REGIONS_JOIN_DISTANCE = GeoUtils.metersToPixelAtEquator(0, 50_000) / 256d; @@ -214,35 +215,49 @@ public void process(Tables.OsmMarinePoint element, FeatureCollector features) { @Override public void process(Tables.OsmWaterPolygon element, FeatureCollector features) { if (nullIfEmpty(element.name()) != null) { - 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 + 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 + // TODO: ... but by doing this we're diverging from OpenMapTiles since say "Notre Dame Bay" is shown: + // 1) OpenMapTiles: as point from Z7 + // 2) planetiler-openmaptiles: as line from Z9 + 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); + // Show a label if a water feature covers at least 1/4 of a tile or z14+ + Geometry geometry = element.source().worldGeometry(); + double area = geometry.getArea(); + minzoom = (int) Math.floor(-1d - Math.log(Math.sqrt(area)) / LOG2); + if (place != null && SEA_OR_OCEAN_PLACE.contains(place)) { + minzoom = Math.clamp(minzoom, 0, 14); + } else { + minzoom = Math.clamp(minzoom, 3, 14); + } + } + 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()); } - feature - .setAttr(Fields.CLASS, clazz) - .setBufferPixels(BUFFER_SIZE) - .putAttrs(OmtLanguageUtils.getNames(element.source().tags(), translations)) - .setAttr(Fields.INTERMITTENT, element.isIntermittent() ? 1 : 0) - .setMinZoom(minzoom); } } From 7fa8f06043d3d45deb7e49cbe55928aef82a67e2 Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Mon, 11 Mar 2024 09:41:26 +0100 Subject: [PATCH 132/211] part of process(Tables.OsmWaterPolygon, ...) moved to setupOsmWaterPolygonFeature() --- .../org/openmaptiles/layers/WaterName.java | 24 +++++++++++-------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/src/main/java/org/openmaptiles/layers/WaterName.java b/src/main/java/org/openmaptiles/layers/WaterName.java index 27d8bc36..9d505edd 100644 --- a/src/main/java/org/openmaptiles/layers/WaterName.java +++ b/src/main/java/org/openmaptiles/layers/WaterName.java @@ -234,12 +234,11 @@ public void process(Tables.OsmWaterPolygon element, FeatureCollector features) { // TODO: ... but by doing this we're diverging from OpenMapTiles since say "Notre Dame Bay" is shown: // 1) OpenMapTiles: as point from Z7 // 2) planetiler-openmaptiles: as line from Z9 - feature = features.geometry(LAYER_NAME, centerlineGeometry) + setupOsmWaterPolygonFeature(element, features.geometry(LAYER_NAME, centerlineGeometry), clazz, minzoom) .setMinPixelSizeBelowZoom(13, 6d * element.name().length()); } else { - // otherwise just use a label point inside the lake - feature = features.pointOnSurface(LAYER_NAME); - // Show a label if a water feature covers at least 1/4 of a tile or z14+ + // otherwise just use a label point inside the lake, + // show a label if a water feature covers at least 1/4 of a tile or z14+ Geometry geometry = element.source().worldGeometry(); double area = geometry.getArea(); minzoom = (int) Math.floor(-1d - Math.log(Math.sqrt(area)) / LOG2); @@ -248,19 +247,24 @@ public void process(Tables.OsmWaterPolygon element, FeatureCollector features) { } else { minzoom = Math.clamp(minzoom, 3, 14); } + setupOsmWaterPolygonFeature(element, features.pointOnSurface(LAYER_NAME), clazz, minzoom); } - 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()); } } } + private FeatureCollector.Feature setupOsmWaterPolygonFeature(Tables.OsmWaterPolygon element, FeatureCollector.Feature output, String clazz, int minzoom) { + output + .setAttr(Fields.CLASS, clazz) + .setBufferPixels(BUFFER_SIZE) + .putAttrs(OmtLanguageUtils.getNames(element.source().tags(), translations)) + .setAttr(Fields.INTERMITTENT, element.isIntermittent() ? 1 : 0) + .setMinZoom(minzoom); + return output; + } + private record NaturalEarthRegion( Geometry geometry, int scalerank From d9c8684cba16deebe4cd06907e863570b5a49ca9 Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Mon, 11 Mar 2024 10:14:23 +0100 Subject: [PATCH 133/211] if feature with centerline has minzoom bellow Z9, use also a label point between minzoom and Z8 --- .../org/openmaptiles/layers/WaterName.java | 50 +++++++++++-------- 1 file changed, 30 insertions(+), 20 deletions(-) diff --git a/src/main/java/org/openmaptiles/layers/WaterName.java b/src/main/java/org/openmaptiles/layers/WaterName.java index 9d505edd..bc9f4b41 100644 --- a/src/main/java/org/openmaptiles/layers/WaterName.java +++ b/src/main/java/org/openmaptiles/layers/WaterName.java @@ -86,6 +86,9 @@ public class WaterName implements private static final Set SEA_OR_OCEAN_PLACE = Set.of("sea", "ocean"); private static final double IMPORTANT_MARINE_REGIONS_JOIN_DISTANCE = GeoUtils.metersToPixelAtEquator(0, 50_000) / 256d; + private static final int MINZOOM_BAY = 9; + private static final int MINZOOM_LAKE = 3; + private static final int MINZOOM_SEA_AND_OCEAN = 0; private final Translations translations; // need to synchronize updates from multiple threads private final LongObjectMap lakeCenterlines = Hppc.newLongObjectHashMap(); @@ -217,8 +220,7 @@ 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; + int minzoom = MINZOOM_BAY; String place = element.place(); String clazz; if ("bay".equals(element.natural())) { @@ -227,27 +229,34 @@ public void process(Tables.OsmWaterPolygon element, FeatureCollector features) { clazz = FieldValues.CLASS_SEA; } else { clazz = FieldValues.CLASS_LAKE; - minzoom = 3; + minzoom = MINZOOM_LAKE; } if (centerlineGeometry != null) { - // prefer lake centerline if it exists - // TODO: ... but by doing this we're diverging from OpenMapTiles since say "Notre Dame Bay" is shown: - // 1) OpenMapTiles: as point from Z7 - // 2) planetiler-openmaptiles: as line from Z9 - setupOsmWaterPolygonFeature(element, features.geometry(LAYER_NAME, centerlineGeometry), clazz, minzoom) - .setMinPixelSizeBelowZoom(13, 6d * element.name().length()); + // prefer lake centerline if it exists, but point will be also used if minzoom bellow 9 is calculated from area + // note: Here we're diverging from OpenMapTiles: For bays with minzoom (based on area) point is used between + // minzoom and Z8 and for Z9+ centerline is used, while OpenMaptiles sticks with points. + setupOsmWaterPolygonFeature( + element, features.geometry(LAYER_NAME, centerlineGeometry), clazz, Math.min(minzoom, MINZOOM_BAY)) + .setMinPixelSizeBelowZoom(13, 6d * element.name().length()); + } + + // Show a label if a water feature covers at least 1/4 of a tile or z14+ + Geometry geometry = element.source().worldGeometry(); + double area = geometry.getArea(); + minzoom = (int) Math.floor(-1d - Math.log(Math.sqrt(area)) / LOG2); + if (place != null && SEA_OR_OCEAN_PLACE.contains(place)) { + minzoom = Math.clamp(minzoom, MINZOOM_SEA_AND_OCEAN, 14); } else { - // otherwise just use a label point inside the lake, - // show a label if a water feature covers at least 1/4 of a tile or z14+ - Geometry geometry = element.source().worldGeometry(); - double area = geometry.getArea(); - minzoom = (int) Math.floor(-1d - Math.log(Math.sqrt(area)) / LOG2); - if (place != null && SEA_OR_OCEAN_PLACE.contains(place)) { - minzoom = Math.clamp(minzoom, 0, 14); - } else { - minzoom = Math.clamp(minzoom, 3, 14); + minzoom = Math.clamp(minzoom, MINZOOM_LAKE, 14); + } + + if (centerlineGeometry == null || minzoom < MINZOOM_BAY) { + // otherwise just use a label point inside the lake + var feature = setupOsmWaterPolygonFeature(element, features.pointOnSurface(LAYER_NAME), clazz, minzoom); + if (centerlineGeometry != null) { + // centerline already created, so make sure we're not having both at same zoom level + feature.setMaxZoom(MINZOOM_BAY - 1); } - setupOsmWaterPolygonFeature(element, features.pointOnSurface(LAYER_NAME), clazz, minzoom); } } catch (GeometryException e) { e.log(stats, "omt_water_polygon", "Unable to get geometry for water polygon " + element.source().id()); @@ -255,7 +264,8 @@ public void process(Tables.OsmWaterPolygon element, FeatureCollector features) { } } - private FeatureCollector.Feature setupOsmWaterPolygonFeature(Tables.OsmWaterPolygon element, FeatureCollector.Feature output, String clazz, int minzoom) { + private FeatureCollector.Feature setupOsmWaterPolygonFeature(Tables.OsmWaterPolygon element, + FeatureCollector.Feature output, String clazz, int minzoom) { output .setAttr(Fields.CLASS, clazz) .setBufferPixels(BUFFER_SIZE) From 1f6deafe7564b0879b48c17e838167ae4a86b861 Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Mon, 11 Mar 2024 10:18:11 +0100 Subject: [PATCH 134/211] bay point vs. centerline difference mentioned in the README --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index ee3ae320..e6afa49f 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,8 @@ 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 +- Some line and polygon tolerances are different, can be tweaked with `--simplify-tolerance` parameter +- For bigger bays whose label points show above Z9, centerline is used for Z9+ ## Customizing From 6f849a28d65e08357ec991cb4ce6741a3d1eda9b Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Mon, 11 Mar 2024 10:54:03 +0100 Subject: [PATCH 135/211] testWaterNameBay for small bay adjusted, testWaterNameBay for big bay added --- .../openmaptiles/layers/WaterNameTest.java | 42 ++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/src/test/java/org/openmaptiles/layers/WaterNameTest.java b/src/test/java/org/openmaptiles/layers/WaterNameTest.java index 02b7c984..53810744 100644 --- a/src/test/java/org/openmaptiles/layers/WaterNameTest.java +++ b/src/test/java/org/openmaptiles/layers/WaterNameTest.java @@ -126,7 +126,7 @@ void testWaterNameMultipleLakelines() { } @Test - void testWaterNameBay() { + void testWaterNameBaySmall() { assertFeatures(11, List.of(), process(SimpleFeature.create( newLineString(0, 0, 1, 1), new HashMap<>(Map.of( @@ -146,6 +146,46 @@ void testWaterNameBay() { "_minzoom", 9, "_maxzoom", 14, "_minpixelsize", "bay".length() * 6d + )), process(SimpleFeature.create( + GeoUtils.worldToLatLonCoords(rectangle(0, Math.sqrt(1E-7))), + new HashMap<>(Map.of( + "name", "bay", + "name:es", "bay es", + "natural", "bay" + )), + OpenMapTilesProfile.OSM_SOURCE, + null, + 10 + ))); + } + + @Test + void testWaterNameBayBig() { + 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", + "_minzoom", 9, + "_maxzoom", 14 + ), Map.of( + "name", "bay", + "name:es", "bay es", + + "_layer", "water_name", + "_type", "point", + "_minzoom", 3, + "_maxzoom", 8 )), process(SimpleFeature.create( GeoUtils.worldToLatLonCoords(rectangle(0, Math.sqrt(1))), new HashMap<>(Map.of( From 90659fd1959db4401e808a466a219b4d838002d3 Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Mon, 11 Mar 2024 11:40:42 +0100 Subject: [PATCH 136/211] testWaterNameLakeline and testWaterNameMultipleLakelines tweaked to use smaller test areas --- src/test/java/org/openmaptiles/layers/WaterNameTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/java/org/openmaptiles/layers/WaterNameTest.java b/src/test/java/org/openmaptiles/layers/WaterNameTest.java index 53810744..fcecbaa1 100644 --- a/src/test/java/org/openmaptiles/layers/WaterNameTest.java +++ b/src/test/java/org/openmaptiles/layers/WaterNameTest.java @@ -62,7 +62,7 @@ void testWaterNameLakeline() { "_maxzoom", 14, "_minpixelsize", "waterway".length() * 6d )), process(SimpleFeature.create( - GeoUtils.worldToLatLonCoords(rectangle(0, Math.sqrt(1))), + GeoUtils.worldToLatLonCoords(rectangle(0, Math.sqrt(1E-7))), new HashMap<>(Map.of( "name", "waterway", "name:es", "waterway es", @@ -112,7 +112,7 @@ void testWaterNameMultipleLakelines() { "_maxzoom", 14, "_minpixelsize", "waterway".length() * 6d )), process(SimpleFeature.create( - GeoUtils.worldToLatLonCoords(rectangle(0, Math.sqrt(1))), + GeoUtils.worldToLatLonCoords(rectangle(0, Math.sqrt(1E-7))), new HashMap<>(Map.of( "name", "waterway", "name:es", "waterway es", From fdcbbaf1c21934d1111737f911f1d91674bf8df0 Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Mon, 11 Mar 2024 11:43:57 +0100 Subject: [PATCH 137/211] mvn spotless:apply --- .../org/openmaptiles/layers/WaterNameTest.java | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/test/java/org/openmaptiles/layers/WaterNameTest.java b/src/test/java/org/openmaptiles/layers/WaterNameTest.java index fcecbaa1..1d70e1cc 100644 --- a/src/test/java/org/openmaptiles/layers/WaterNameTest.java +++ b/src/test/java/org/openmaptiles/layers/WaterNameTest.java @@ -162,13 +162,13 @@ void testWaterNameBaySmall() { @Test void testWaterNameBayBig() { 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 + 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", From 16d079f3465ebfea6620ae91e14f7067303dd0d1 Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Mon, 11 Mar 2024 12:52:15 +0100 Subject: [PATCH 138/211] added unit test which checks minzoom for admin_level=2 disputed border (derived from way) --- .../org/openmaptiles/layers/BoundaryTest.java | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/test/java/org/openmaptiles/layers/BoundaryTest.java b/src/test/java/org/openmaptiles/layers/BoundaryTest.java index 95e56d09..dda4405e 100644 --- a/src/test/java/org/openmaptiles/layers/BoundaryTest.java +++ b/src/test/java/org/openmaptiles/layers/BoundaryTest.java @@ -382,6 +382,29 @@ void testOsmBoundaryDisputedFromWay() { )); } + @Test + void testOsmAl2BoundaryDisputedMinZoom() { + var relation = new OsmElement.Relation(1); + relation.setTag("type", "boundary"); + relation.setTag("admin_level", "2"); + relation.setTag("boundary", "administrative"); + + assertFeatures(3, List.of(Map.of( + "_layer", "boundary", + "_type", "line", + "_minzoom", 4, + + "disputed", 1, + "maritime", 0, + "admin_level", 2 + )), process(lineFeatureWithRelation( + profile.preprocessOsmRelation(relation), + Map.of( + "disputed", "yes" + )) + )); + } + @Test void testCountryBoundaryEmittedIfNoName() { var relation = new OsmElement.Relation(1); From 2cc7a73a718eff2816733afcc11dc91da27c6b8a Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Mon, 11 Mar 2024 12:53:40 +0100 Subject: [PATCH 139/211] fixed missing admin_level=2 disputed borders at Z3 --- src/main/java/org/openmaptiles/layers/Boundary.java | 3 ++- src/test/java/org/openmaptiles/layers/BoundaryTest.java | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/openmaptiles/layers/Boundary.java b/src/main/java/org/openmaptiles/layers/Boundary.java index 99a29977..a964cbc4 100644 --- a/src/main/java/org/openmaptiles/layers/Boundary.java +++ b/src/main/java/org/openmaptiles/layers/Boundary.java @@ -263,7 +263,8 @@ public void processAllOsm(SourceFeature feature, FeatureCollector features) { feature.hasTag("natural", "coastline") || feature.hasTag("boundary_type", "maritime"); int minzoom = - (maritime && minAdminLevel == 2) ? 4 : + (disputed && minAdminLevel == 2) ? 3 : + ((maritime || disputed) && minAdminLevel == 2) ? 4 : minAdminLevel <= 4 ? 5 : minAdminLevel <= 6 ? 9 : minAdminLevel <= 8 ? 11 : 12; diff --git a/src/test/java/org/openmaptiles/layers/BoundaryTest.java b/src/test/java/org/openmaptiles/layers/BoundaryTest.java index dda4405e..63c03d59 100644 --- a/src/test/java/org/openmaptiles/layers/BoundaryTest.java +++ b/src/test/java/org/openmaptiles/layers/BoundaryTest.java @@ -392,7 +392,7 @@ void testOsmAl2BoundaryDisputedMinZoom() { assertFeatures(3, List.of(Map.of( "_layer", "boundary", "_type", "line", - "_minzoom", 4, + "_minzoom", 3, "disputed", 1, "maritime", 0, From f7140dcffdeb51d9d255be9491ef0d22cd204df5 Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Mon, 11 Mar 2024 18:34:09 +0100 Subject: [PATCH 140/211] landuse fix for Z5: include all features from ne_50m_urban_areas --- src/main/java/org/openmaptiles/layers/Landuse.java | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/openmaptiles/layers/Landuse.java b/src/main/java/org/openmaptiles/layers/Landuse.java index 16dfcfb0..76c765d4 100644 --- a/src/main/java/org/openmaptiles/layers/Landuse.java +++ b/src/main/java/org/openmaptiles/layers/Landuse.java @@ -87,11 +87,10 @@ public Landuse(Translations translations, PlanetilerConfig config, Stats stats) public void processNaturalEarth(String table, SourceFeature feature, FeatureCollector features) { if ("ne_50m_urban_areas".equals(table)) { Double scalerank = Parse.parseDoubleOrNull(feature.getTag("scalerank")); - if (scalerank != null && scalerank <= 2) { - features.polygon(LAYER_NAME).setBufferPixels(BUFFER_SIZE) - .setAttr(Fields.CLASS, FieldValues.CLASS_RESIDENTIAL) - .setZoomRange(4, 5); - } + int minzoom = (scalerank != null && scalerank <= 2) ? 4 : 5; + features.polygon(LAYER_NAME).setBufferPixels(BUFFER_SIZE) + .setAttr(Fields.CLASS, FieldValues.CLASS_RESIDENTIAL) + .setZoomRange(minzoom, 5); } } From 8543c8ebefeed9c0371bed6480c09667f3123b54 Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Mon, 11 Mar 2024 19:27:52 +0100 Subject: [PATCH 141/211] minDist and buffer tweaked for residential areas for Z6-Z8 to better match OpenMapTiles --- .../java/org/openmaptiles/layers/Landuse.java | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/openmaptiles/layers/Landuse.java b/src/main/java/org/openmaptiles/layers/Landuse.java index 76c765d4..dec916a3 100644 --- a/src/main/java/org/openmaptiles/layers/Landuse.java +++ b/src/main/java/org/openmaptiles/layers/Landuse.java @@ -52,6 +52,7 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE import java.util.List; import java.util.Map; import java.util.Set; +import java.util.TreeMap; import org.openmaptiles.OpenMapTilesProfile; import org.openmaptiles.generated.OpenMapTilesSchema; import org.openmaptiles.generated.Tables; @@ -74,6 +75,14 @@ public class Landuse implements 7, 2, 6, 1 )); + private static final TreeMap MINDIST_AND_BUFFER_SIZES = new TreeMap<>(Map.of( + 5, 0.1, + // there is quite huge jump between Z5:NE and Z6:OSM => bigger generalization needed to make the transition more smooth + 6, 0.5, + 7, 0.25, + 8, 0.125, + Integer.MAX_VALUE, 0.1 + )); private static final Set Z6_CLASSES = Set.of( FieldValues.CLASS_RESIDENTIAL, FieldValues.CLASS_SUBURB, @@ -134,10 +143,14 @@ public List postProcess(int zoom, result.add(item); } } - var merged = zoom <= 12 ? - FeatureMerge.mergeNearbyPolygons(toMerge, 1, 1, 0.1, 0.1) : + List merged; + if (zoom <= 12) { + double minDistAndBuffer = MINDIST_AND_BUFFER_SIZES.ceilingEntry(zoom).getValue(); + merged = FeatureMerge.mergeNearbyPolygons(toMerge, 1, 1, minDistAndBuffer, minDistAndBuffer); + } else { // reduces size of some heavy z13-14 tiles with lots of small polygons - FeatureMerge.mergeMultiPolygon(toMerge); + merged = FeatureMerge.mergeMultiPolygon(toMerge); + } result.addAll(merged); return result; } From 51f1343a99a63979923443c97fcaaebc248227b6 Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Mon, 11 Mar 2024 19:34:12 +0100 Subject: [PATCH 142/211] landuse fix for Z5: unit test updated --- src/test/java/org/openmaptiles/layers/LanduseTest.java | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/test/java/org/openmaptiles/layers/LanduseTest.java b/src/test/java/org/openmaptiles/layers/LanduseTest.java index c3449364..57d7ea8e 100644 --- a/src/test/java/org/openmaptiles/layers/LanduseTest.java +++ b/src/test/java/org/openmaptiles/layers/LanduseTest.java @@ -19,7 +19,8 @@ void testNaturalEarthUrbanAreas() { assertFeatures(0, List.of(Map.of( "_layer", "landuse", "class", "residential", - "_buffer", 4d + "_buffer", 4d, + "_minzoom", 4 )), process(SimpleFeature.create( GeoUtils.worldToLatLonCoords(rectangle(0, Math.sqrt(1))), Map.of("scalerank", 1.9), @@ -27,7 +28,12 @@ void testNaturalEarthUrbanAreas() { "ne_50m_urban_areas", 0 ))); - assertFeatures(0, List.of(), process(SimpleFeature.create( + assertFeatures(0, List.of(Map.of( + "_layer", "landuse", + "class", "residential", + "_buffer", 4d, + "_minzoom", 5 + )), process(SimpleFeature.create( GeoUtils.worldToLatLonCoords(rectangle(0, Math.sqrt(1))), Map.of("scalerank", 2.1), OpenMapTilesProfile.NATURAL_EARTH_SOURCE, From b653969eb417b08703ce0a8e356af31a621c64b7 Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Tue, 12 Mar 2024 20:25:24 +0100 Subject: [PATCH 143/211] bumped copyright statements --- LICENSE.md | 2 +- src/main/java/org/openmaptiles/Generate.java | 2 +- src/main/java/org/openmaptiles/layers/Housenumber.java | 2 +- src/main/java/org/openmaptiles/layers/Landuse.java | 2 +- src/main/java/org/openmaptiles/layers/Place.java | 2 +- src/main/java/org/openmaptiles/layers/Poi.java | 2 +- src/main/java/org/openmaptiles/layers/Transportation.java | 2 +- src/main/java/org/openmaptiles/layers/TransportationName.java | 2 +- src/main/java/org/openmaptiles/layers/WaterName.java | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/LICENSE.md b/LICENSE.md index 860a7563..973211fd 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,4 +1,4 @@ -Copyright (c) 2022, MapTiler.com & OpenMapTiles contributors. +Copyright (c) 2024, MapTiler.com & OpenMapTiles contributors. All rights reserved. The vector tile schema has been developed by Klokan Technologies GmbH and diff --git a/src/main/java/org/openmaptiles/Generate.java b/src/main/java/org/openmaptiles/Generate.java index c8af4db9..31835e3b 100644 --- a/src/main/java/org/openmaptiles/Generate.java +++ b/src/main/java/org/openmaptiles/Generate.java @@ -61,7 +61,7 @@ public class Generate { private static final String LINE_SEPARATOR = System.lineSeparator(); private static final String GENERATED_FILE_HEADER = """ /* - Copyright (c) 2021, MapTiler.com & OpenMapTiles contributors. + Copyright (c) 2024, MapTiler.com & OpenMapTiles contributors. All rights reserved. Code license: BSD 3-Clause License diff --git a/src/main/java/org/openmaptiles/layers/Housenumber.java b/src/main/java/org/openmaptiles/layers/Housenumber.java index 2d53c42a..8a0bddd5 100644 --- a/src/main/java/org/openmaptiles/layers/Housenumber.java +++ b/src/main/java/org/openmaptiles/layers/Housenumber.java @@ -1,5 +1,5 @@ /* -Copyright (c) 2021, MapTiler.com & OpenMapTiles contributors. +Copyright (c) 2024, MapTiler.com & OpenMapTiles contributors. All rights reserved. Code license: BSD 3-Clause License diff --git a/src/main/java/org/openmaptiles/layers/Landuse.java b/src/main/java/org/openmaptiles/layers/Landuse.java index dec916a3..1faeb60f 100644 --- a/src/main/java/org/openmaptiles/layers/Landuse.java +++ b/src/main/java/org/openmaptiles/layers/Landuse.java @@ -1,5 +1,5 @@ /* -Copyright (c) 2021, MapTiler.com & OpenMapTiles contributors. +Copyright (c) 2024, MapTiler.com & OpenMapTiles contributors. All rights reserved. Code license: BSD 3-Clause License diff --git a/src/main/java/org/openmaptiles/layers/Place.java b/src/main/java/org/openmaptiles/layers/Place.java index f764d15f..19e84942 100644 --- a/src/main/java/org/openmaptiles/layers/Place.java +++ b/src/main/java/org/openmaptiles/layers/Place.java @@ -1,5 +1,5 @@ /* -Copyright (c) 2021, MapTiler.com & OpenMapTiles contributors. +Copyright (c) 2024, MapTiler.com & OpenMapTiles contributors. All rights reserved. Code license: BSD 3-Clause License diff --git a/src/main/java/org/openmaptiles/layers/Poi.java b/src/main/java/org/openmaptiles/layers/Poi.java index e511cc50..22736837 100644 --- a/src/main/java/org/openmaptiles/layers/Poi.java +++ b/src/main/java/org/openmaptiles/layers/Poi.java @@ -1,5 +1,5 @@ /* -Copyright (c) 2021, MapTiler.com & OpenMapTiles contributors. +Copyright (c) 2024, MapTiler.com & OpenMapTiles contributors. All rights reserved. Code license: BSD 3-Clause License diff --git a/src/main/java/org/openmaptiles/layers/Transportation.java b/src/main/java/org/openmaptiles/layers/Transportation.java index ba828a44..2c442bf6 100644 --- a/src/main/java/org/openmaptiles/layers/Transportation.java +++ b/src/main/java/org/openmaptiles/layers/Transportation.java @@ -1,5 +1,5 @@ /* -Copyright (c) 2021, MapTiler.com & OpenMapTiles contributors. +Copyright (c) 2024, MapTiler.com & OpenMapTiles contributors. All rights reserved. Code license: BSD 3-Clause License diff --git a/src/main/java/org/openmaptiles/layers/TransportationName.java b/src/main/java/org/openmaptiles/layers/TransportationName.java index 2ac9f400..be6d7194 100644 --- a/src/main/java/org/openmaptiles/layers/TransportationName.java +++ b/src/main/java/org/openmaptiles/layers/TransportationName.java @@ -1,5 +1,5 @@ /* -Copyright (c) 2021, MapTiler.com & OpenMapTiles contributors. +Copyright (c) 2024, MapTiler.com & OpenMapTiles contributors. All rights reserved. Code license: BSD 3-Clause License diff --git a/src/main/java/org/openmaptiles/layers/WaterName.java b/src/main/java/org/openmaptiles/layers/WaterName.java index bc9f4b41..4a0ee16a 100644 --- a/src/main/java/org/openmaptiles/layers/WaterName.java +++ b/src/main/java/org/openmaptiles/layers/WaterName.java @@ -1,5 +1,5 @@ /* -Copyright (c) 2021, MapTiler.com & OpenMapTiles contributors. +Copyright (c) 2024, MapTiler.com & OpenMapTiles contributors. All rights reserved. Code license: BSD 3-Clause License From 69750d5dcd2e8796f10487cf919e43e5db70b1a2 Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Tue, 12 Mar 2024 20:32:47 +0100 Subject: [PATCH 144/211] added/adjusted acknowledgments --- README.md | 4 ++-- .../java/org/openmaptiles/OpenMapTilesMain.java | 14 ++++++++++++++ 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index e6afa49f..24ba6eed 100644 --- a/README.md +++ b/README.md @@ -166,8 +166,8 @@ If you want to regenerate from a different repository than the default openmapti All code in this repository is under the [BSD license](./LICENSE.md) and the cartography decisions encoded in the schema and SQL are licensed under [CC-BY](./LICENSE.md). -Products or services using maps derived from OpenMapTiles schema need to visibly credit "OpenMapTiles.org" or -reference "OpenMapTiles" with a link to https://openmaptiles.org/. Exceptions to attribution requirement can be granted +Products or services using maps derived from OpenMapTiles schema need to **visibly credit "OpenMapTiles.org"** or +**reference "OpenMapTiles"** with a link to https://openmaptiles.org/. Exceptions to attribution requirement can be granted on request. For a browsable electronic map based on OpenMapTiles and OpenStreetMap data, the diff --git a/src/main/java/org/openmaptiles/OpenMapTilesMain.java b/src/main/java/org/openmaptiles/OpenMapTilesMain.java index 6b82c0d3..b51156f1 100644 --- a/src/main/java/org/openmaptiles/OpenMapTilesMain.java +++ b/src/main/java/org/openmaptiles/OpenMapTilesMain.java @@ -4,12 +4,16 @@ import com.onthegomap.planetiler.config.Arguments; import java.nio.file.Path; import org.openmaptiles.generated.OpenMapTilesSchema; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * Main entrypoint for generating a map using the OpenMapTiles schema. */ public class OpenMapTilesMain { + private static final Logger LOGGER = LoggerFactory.getLogger(OpenMapTilesMain.class); + public static void main(String[] args) throws Exception { run(Arguments.fromArgsOrConfigFile(args)); } @@ -52,5 +56,15 @@ static void run(Arguments arguments) throws Exception { // override with --mbtiles=... argument or MBTILES=... env var or mbtiles=... in a config file .setOutput("mbtiles", dataDir.resolve("output.mbtiles")) .run(); + + LOGGER.info(""" + Acknowledgments + Generated vector tiles are produced work of OpenStreetMap data. + Such tiles are reusable under CC-BY license granted by OpenMapTiles team: + - https://github.com/openmaptiles/openmaptiles/#license + Maps made with these vector tiles must display a visible credit: + - © OpenMapTiles © OpenStreetMap contributors + Thanks to all free, open source software developers and Open Data Contributors! + """); } } From bc756f48610d30aa1ac1be559cceeecaef8ca6cd Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Tue, 12 Mar 2024 20:56:59 +0100 Subject: [PATCH 145/211] fixed handling of Baikonur border in Kazachstan --- src/main/java/org/openmaptiles/layers/Boundary.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/openmaptiles/layers/Boundary.java b/src/main/java/org/openmaptiles/layers/Boundary.java index a964cbc4..d9e3263a 100644 --- a/src/main/java/org/openmaptiles/layers/Boundary.java +++ b/src/main/java/org/openmaptiles/layers/Boundary.java @@ -179,7 +179,7 @@ record BoundaryInfo(int adminLevel, int minzoom, int maxzoom) {} BoundaryInfo info = switch (table) { case "ne_110m_admin_0_boundary_lines_land" -> new BoundaryInfo(2, 0, 0); case "ne_50m_admin_0_boundary_lines_land" -> new BoundaryInfo(2, 1, 3); - case "ne_10m_admin_0_boundary_lines_land" -> feature.hasTag("featurecla", "Lease Limit") ? null : + case "ne_10m_admin_0_boundary_lines_land" -> feature.hasTag("featurecla", "Lease limit") ? null : new BoundaryInfo(2, 4, 4); case "ne_10m_admin_1_states_provinces_lines" -> { Double minZoom = Parse.parseDoubleOrNull(feature.getTag("min_zoom")); From dcf66f3697cdb2634e414ef33c7d279f7d7c5c84 Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Tue, 12 Mar 2024 20:58:12 +0100 Subject: [PATCH 146/211] fixed handling of Baikonur border in Kazachstan (2) --- src/test/java/org/openmaptiles/layers/BoundaryTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/org/openmaptiles/layers/BoundaryTest.java b/src/test/java/org/openmaptiles/layers/BoundaryTest.java index 63c03d59..4c38a69f 100644 --- a/src/test/java/org/openmaptiles/layers/BoundaryTest.java +++ b/src/test/java/org/openmaptiles/layers/BoundaryTest.java @@ -118,7 +118,7 @@ void testNaturalEarthCountryBoundaries() { assertFeatures(0, List.of(), process(SimpleFeature.create( newLineString(0, 0, 1, 1), Map.of( - "featurecla", "Lease Limit" + "featurecla", "Lease limit" ), OpenMapTilesProfile.NATURAL_EARTH_SOURCE, "ne_10m_admin_0_boundary_lines_land", From ed013646395a49c6319824381e0e59f6decb32b3 Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Thu, 21 Mar 2024 20:18:59 +0100 Subject: [PATCH 147/211] OSM boundaries only for Z5+, non-disputed for Z4+ --- src/main/java/org/openmaptiles/layers/Boundary.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/java/org/openmaptiles/layers/Boundary.java b/src/main/java/org/openmaptiles/layers/Boundary.java index d9e3263a..a5e5d305 100644 --- a/src/main/java/org/openmaptiles/layers/Boundary.java +++ b/src/main/java/org/openmaptiles/layers/Boundary.java @@ -263,8 +263,7 @@ public void processAllOsm(SourceFeature feature, FeatureCollector features) { feature.hasTag("natural", "coastline") || feature.hasTag("boundary_type", "maritime"); int minzoom = - (disputed && minAdminLevel == 2) ? 3 : - ((maritime || disputed) && minAdminLevel == 2) ? 4 : + (maritime && minAdminLevel == 2) ? 4 : minAdminLevel <= 4 ? 5 : minAdminLevel <= 6 ? 9 : minAdminLevel <= 8 ? 11 : 12; From a05c7237500021229639a40654ad04ec03f0e080 Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Thu, 21 Mar 2024 21:33:35 +0100 Subject: [PATCH 148/211] OSM boundaries only for Z5+, non-disputed for Z4+ - unit test adjusted --- src/test/java/org/openmaptiles/layers/BoundaryTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/org/openmaptiles/layers/BoundaryTest.java b/src/test/java/org/openmaptiles/layers/BoundaryTest.java index 4c38a69f..14138ffd 100644 --- a/src/test/java/org/openmaptiles/layers/BoundaryTest.java +++ b/src/test/java/org/openmaptiles/layers/BoundaryTest.java @@ -392,7 +392,7 @@ void testOsmAl2BoundaryDisputedMinZoom() { assertFeatures(3, List.of(Map.of( "_layer", "boundary", "_type", "line", - "_minzoom", 3, + "_minzoom", 5, "disputed", 1, "maritime", 0, From c553a946d20b911426f46f0d6553326556bde6c4 Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Sat, 23 Mar 2024 19:15:20 +0100 Subject: [PATCH 149/211] OSM Lake IDs at low zooms --- .../java/org/openmaptiles/layers/Water.java | 199 +++++++++++++++++- .../org/openmaptiles/layers/WaterTest.java | 103 +++++++-- 2 files changed, 271 insertions(+), 31 deletions(-) diff --git a/src/main/java/org/openmaptiles/layers/Water.java b/src/main/java/org/openmaptiles/layers/Water.java index 8a3d9a9c..6655a9df 100644 --- a/src/main/java/org/openmaptiles/layers/Water.java +++ b/src/main/java/org/openmaptiles/layers/Water.java @@ -42,10 +42,20 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE import com.onthegomap.planetiler.config.PlanetilerConfig; import com.onthegomap.planetiler.expression.MultiExpression; import com.onthegomap.planetiler.geo.GeometryException; +import com.onthegomap.planetiler.reader.SimpleFeature; import com.onthegomap.planetiler.reader.SourceFeature; import com.onthegomap.planetiler.stats.Stats; import com.onthegomap.planetiler.util.Translations; +import java.util.ArrayList; import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Consumer; +import org.locationtech.jts.geom.Envelope; +import org.locationtech.jts.geom.Geometry; +import org.locationtech.jts.geom.TopologyException; +import org.locationtech.jts.geom.util.GeometryFixer; +import org.locationtech.jts.index.strtree.STRtree; import org.openmaptiles.OpenMapTilesProfile; import org.openmaptiles.generated.OpenMapTilesSchema; import org.openmaptiles.generated.Tables; @@ -62,7 +72,8 @@ public class Water implements Tables.OsmWaterPolygon.Handler, OpenMapTilesProfile.NaturalEarthProcessor, OpenMapTilesProfile.OsmWaterPolygonProcessor, - ForwardingProfile.FeaturePostProcessor { + ForwardingProfile.FeaturePostProcessor, + OpenMapTilesProfile.FinishHandler { /* * At low zoom levels, use natural earth for oceans and major lakes, and at high zoom levels @@ -71,12 +82,20 @@ public class Water implements * which infers ocean polygons by preprocessing all coastline elements. */ + // smallest NE lake is around 4.42E-13, this is roughly 1/2 of that and approx 17% of OSM features are smaller than this: + private static final double SMALLEST_OSM_LAKE_AREA = Math.pow(4, -21); + private final MultiExpression.Index classMapping; private final PlanetilerConfig config; + private final Stats stats; + private final Map neLakeIndexes = new ConcurrentHashMap<>(); + private final Map> neLakeNameMaps = new ConcurrentHashMap<>(); + private final List neAllLakeInfos = new ArrayList<>(); public Water(Translations translations, PlanetilerConfig config, Stats stats) { this.classMapping = FieldMappings.Class.index(); this.config = config; + this.stats = stats; } @Override @@ -86,21 +105,57 @@ record WaterInfo(int minZoom, int maxZoom, String clazz) {} case "ne_110m_ocean" -> new WaterInfo(0, 1, FieldValues.CLASS_OCEAN); case "ne_50m_ocean" -> new WaterInfo(2, 4, FieldValues.CLASS_OCEAN); case "ne_10m_ocean" -> new WaterInfo(5, 5, FieldValues.CLASS_OCEAN); - - // TODO: get OSM ID from low-zoom natural earth lakes - case "ne_110m_lakes" -> new WaterInfo(0, 1, FieldValues.CLASS_LAKE); - case "ne_50m_lakes" -> new WaterInfo(2, 3, FieldValues.CLASS_LAKE); - case "ne_10m_lakes" -> new WaterInfo(4, 5, FieldValues.CLASS_LAKE); default -> null; }; if (info != null) { - features.polygon(LAYER_NAME) - .setBufferPixels(BUFFER_SIZE) - .setZoomRange(info.minZoom, info.maxZoom) - .setAttr(Fields.CLASS, info.clazz); + setupNeWaterFeature(features, info.minZoom, info.maxZoom, info.clazz, null); + return; + } + + LakeInfo lakeInfo = switch (table) { + case "ne_110m_lakes" -> new LakeInfo(0, 1, FieldValues.CLASS_LAKE); + case "ne_50m_lakes" -> new LakeInfo(2, 3, FieldValues.CLASS_LAKE); + case "ne_10m_lakes" -> new LakeInfo(4, 5, FieldValues.CLASS_LAKE); + default -> null; + }; + if (lakeInfo != null) { + try { + var geom = feature.worldGeometry(); + lakeInfo.geom = geom; + lakeInfo.name = feature.getString("name"); + + var index = neLakeIndexes.computeIfAbsent(table, t -> new STRtree()); + var neLakeNameMap = neLakeNameMaps.computeIfAbsent(table, t -> new ConcurrentHashMap<>()); + + // need to externally synchronize inserts into the STRTree and ArrayList + synchronized (this) { + index.insert(geom.getEnvelopeInternal(), lakeInfo); + neAllLakeInfos.add(lakeInfo); + } + if (lakeInfo.name != null) { + if (!neLakeNameMap.containsKey(lakeInfo.name) || + lakeInfo.geom.getArea() > neLakeNameMap.get(lakeInfo.name).geom.getArea()) { + // on name collision, bigger lake gets on the name list + neLakeNameMap.put(lakeInfo.name, lakeInfo); + } + } + } catch (GeometryException e) { + e.log(stats, "omt_water_ne", + "Error getting geometry for natural earth feature " + table + " " + feature.getTag("ogc_fid")); + // make sure we have this NE lake even if without OSM ID + setupNeWaterFeature(features, lakeInfo.minZoom, lakeInfo.maxZoom, lakeInfo.clazz, null); + } } } + private void setupNeWaterFeature(FeatureCollector features, int minZoom, int maxZoom, String clazz, Long osmId) { + features.polygon(LAYER_NAME) + .setBufferPixels(BUFFER_SIZE) + .setZoomRange(minZoom, maxZoom) + .setAttr(Fields.CLASS, clazz) + .setAttr(Fields.ID, osmId); + } + @Override public void processOsmWater(SourceFeature feature, FeatureCollector features) { features.polygon(LAYER_NAME) @@ -121,6 +176,108 @@ public void process(Tables.OsmWaterPolygon element, FeatureCollector features) { .setAttr(Fields.INTERMITTENT, element.isIntermittent() ? 1 : 0) .setAttrWithMinzoom(Fields.BRUNNEL, Utils.brunnel(element.isBridge(), element.isTunnel()), 12) .setAttr(Fields.CLASS, clazz); + + fillOsmIdIntoNeLake(element); + } + } + + void fillOsmIdIntoNeLake(Tables.OsmWaterPolygon element) { + try { + // match by name: + boolean match = false; + if (element.name() != null) { + for (var map : neLakeNameMaps.values()) { + var lakeInfo = map.get(element.name()); + if (lakeInfo != null) { + match = true; + fillOsmIdIntoNeLake(element, element.source().worldGeometry(), lakeInfo); + } + } + } + if (match) { + return; + } + + // if OSM lake is too small for Z6 (e.g. area bellow ~4px) we assume there is no matching NE lake + Geometry geom = element.source().worldGeometry(); + if (geom.getArea() < SMALLEST_OSM_LAKE_AREA) { + return; + } + + // match by intersection: + Envelope envelope = geom.getEnvelopeInternal(); + + for (var index : neLakeIndexes.values()) { + var items = index.query(envelope); + if (items.size() == 1) { + if (items.getFirst()instanceof LakeInfo lakeInfo) { + fillOsmIdIntoNeLake(element, geom, lakeInfo); + } + } else if (!items.isEmpty()) { + for (var item : items) { + if (item instanceof LakeInfo lakeInfo) { + fillOsmIdIntoNeLake(element, geom, lakeInfo); + } + } + } + } + } catch (GeometryException e) { + e.log(stats, "omt_water", + "Error getting geometry for OSM feature " + element.source().id()); + } + } + + void fillOsmIdIntoNeLake(Tables.OsmWaterPolygon element, Geometry geom, LakeInfo lakeInfo) throws GeometryException { + Geometry neGeom = lakeInfo.geom; + Geometry intersection; + try { + if (!neGeom.intersects(geom)) { + return; + } + intersection = neGeom.intersection(geom); + } catch (TopologyException e) { + try { + Geometry fixedGeom = GeometryFixer.fix(geom); + if (!neGeom.intersects(fixedGeom)) { + return; + } + intersection = neGeom.intersection(fixedGeom); + } catch (TopologyException e2) { + throw new GeometryException("fix_omt_water_topology_error", + "error fixing polygon: " + e2 + "; original error: " + e); + } + } + + // should match following in OpenMapTiles: Distinct on keeps just the first occurence -> order by 'area_ratio DESC' + double areaRatio = intersection.getArea() / neGeom.getArea(); + if (areaRatio > lakeInfo.areaRatio) { + lakeInfo.osmId = element.source().id(); + lakeInfo.areaRatio = areaRatio; + } + } + + @Override + public void finish(String sourceName, FeatureCollector.Factory featureCollectors, + Consumer emit) { + if (OpenMapTilesProfile.NATURAL_EARTH_SOURCE.equals(sourceName)) { + var timer = stats.startStage("ne_lake_index"); + for (var index : neLakeIndexes.values()) { + index.build(); + } + timer.stop(); + } else if (OpenMapTilesProfile.OSM_SOURCE.equals(sourceName)) { + var timer = stats.startStage("ne_lakes"); + for (var item : neAllLakeInfos) { + var features = featureCollectors.get(SimpleFeature.fromWorldGeometry(item.geom)); + setupNeWaterFeature(features, item.minZoom, item.maxZoom, item.clazz, item.osmId); + for (var feature : features) { + emit.accept(feature); + } + } + neLakeNameMaps.clear(); + neLakeIndexes.clear(); + neAllLakeInfos.clear(); + timer.stop(); } } @@ -128,4 +285,26 @@ public void process(Tables.OsmWaterPolygon element, FeatureCollector features) { public List postProcess(int zoom, List items) throws GeometryException { return items.size() > 1 ? FeatureMerge.mergeOverlappingPolygons(items, config.minFeatureSize(zoom)) : items; } + + /** + * Information to hold onto from processing an NE lake to determine OSM ID later. + */ + private static class LakeInfo { + String name; + int minZoom; + int maxZoom; + String clazz; + Geometry geom; + Long osmId; + double areaRatio; + + public LakeInfo(int minZoom, int maxZoom, String clazz) { + this.name = null; + this.minZoom = minZoom; + this.maxZoom = maxZoom; + this.clazz = clazz; + this.osmId = null; + this.areaRatio = 0; + } + } } diff --git a/src/test/java/org/openmaptiles/layers/WaterTest.java b/src/test/java/org/openmaptiles/layers/WaterTest.java index ee900dc5..7dc852d3 100644 --- a/src/test/java/org/openmaptiles/layers/WaterTest.java +++ b/src/test/java/org/openmaptiles/layers/WaterTest.java @@ -2,8 +2,10 @@ import static com.onthegomap.planetiler.TestUtils.rectangle; +import com.onthegomap.planetiler.FeatureCollector; import com.onthegomap.planetiler.geo.GeoUtils; import com.onthegomap.planetiler.reader.SimpleFeature; +import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -16,7 +18,7 @@ class WaterTest extends AbstractLayerTest { @Test void testWaterNaturalEarth() { assertFeatures(0, List.of(Map.of( - "class", "lake", + "class", "ocean", "intermittent", "", "_layer", "water", "_type", "polygon", @@ -25,49 +27,108 @@ void testWaterNaturalEarth() { rectangle(0, 10), Map.of(), OpenMapTilesProfile.NATURAL_EARTH_SOURCE, - "ne_110m_lakes", + "ne_110m_ocean", 0 ))); - assertFeatures(0, List.of(Map.of( + assertFeatures(6, List.of(Map.of( "class", "ocean", - "intermittent", "", "_layer", "water", "_type", "polygon", - "_minzoom", 0 + "_maxzoom", 5 )), process(SimpleFeature.create( rectangle(0, 10), Map.of(), OpenMapTilesProfile.NATURAL_EARTH_SOURCE, - "ne_110m_ocean", + "ne_10m_ocean", 0 ))); + } - assertFeatures(6, List.of(Map.of( - "class", "lake", - "_layer", "water", - "_type", "polygon", - "_maxzoom", 5 - )), process(SimpleFeature.create( - rectangle(0, 10), + @Test + void testLakeNaturalEarthByIntersection() { + final var polygon = rectangle(0, 0.1); + // NE lakes: + process(SimpleFeature.create( + polygon, + Map.of(), + OpenMapTilesProfile.NATURAL_EARTH_SOURCE, + "ne_110m_lakes", + 0 + )); + process(SimpleFeature.create( + polygon, Map.of(), OpenMapTilesProfile.NATURAL_EARTH_SOURCE, "ne_10m_lakes", 0 - ))); + )); + // OSM lake to take the ID from: + process(SimpleFeature.create( + polygon, + new HashMap<>(Map.of( + "natural", "water", + "water", "reservoir" + )), + OpenMapTilesProfile.OSM_SOURCE, + null, + 123 + )); - assertFeatures(6, List.of(Map.of( - "class", "ocean", + List features = new ArrayList<>(); + profile.finish(OpenMapTilesProfile.OSM_SOURCE, new FeatureCollector.Factory(params, stats), features::add); + assertFeatures(0, List.of(Map.of( + "class", "lake", + "intermittent", "", + "id", 123L, "_layer", "water", "_type", "polygon", + "_minzoom", 0, + "_maxzoom", 1 + ), Map.of( + "class", "lake", + "intermittent", "", + "id", 123L, + "_layer", "water", + "_type", "polygon", + "_minzoom", 4, "_maxzoom", 5 - )), process(SimpleFeature.create( - rectangle(0, 10), - Map.of(), + )), features); + } + + @Test + void testLakeNaturalEarthByName() { + final var polygon = rectangle(0, 0.1); + // NE lake: + process(SimpleFeature.create( + polygon, + Map.of("name", "Test Lake"), OpenMapTilesProfile.NATURAL_EARTH_SOURCE, - "ne_10m_ocean", + "ne_50m_lakes", 0 - ))); + )); + // OSM lake to take the ID from: + process(SimpleFeature.create( + polygon, + new HashMap<>(Map.of( + "name", "Test Lake", + "natural", "water", + "water", "reservoir" + )), + OpenMapTilesProfile.OSM_SOURCE, + null, + 123 + )); + + List features = new ArrayList<>(); + profile.finish(OpenMapTilesProfile.OSM_SOURCE, new FeatureCollector.Factory(params, stats), features::add); + assertFeatures(0, List.of(Map.of( + "class", "lake", + "id", 123L, + "_layer", "water", + "_minzoom", 2, + "_maxzoom", 3 + )), features); } @Test From 57828cc99e5aae4ec394c327acd633a2e7dc4786 Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Sat, 23 Mar 2024 19:18:59 +0100 Subject: [PATCH 150/211] testLakeZoomLevels(): NE lakes removed, checked now in testLakeNaturalEarth*() --- .../org/openmaptiles/layers/WaterTest.java | 23 +------------------ 1 file changed, 1 insertion(+), 22 deletions(-) diff --git a/src/test/java/org/openmaptiles/layers/WaterTest.java b/src/test/java/org/openmaptiles/layers/WaterTest.java index 7dc852d3..3264e55e 100644 --- a/src/test/java/org/openmaptiles/layers/WaterTest.java +++ b/src/test/java/org/openmaptiles/layers/WaterTest.java @@ -316,28 +316,7 @@ void testOceanZoomLevels() { @Test void testLakeZoomLevels() { - assertCoversZoomRange(0, 14, "water", - process(SimpleFeature.create( - rectangle(0, 10), - Map.of(), - OpenMapTilesProfile.NATURAL_EARTH_SOURCE, - "ne_110m_lakes", - 0 - )), - process(SimpleFeature.create( - rectangle(0, 10), - Map.of(), - OpenMapTilesProfile.NATURAL_EARTH_SOURCE, - "ne_50m_lakes", - 0 - )), - process(SimpleFeature.create( - rectangle(0, 10), - Map.of(), - OpenMapTilesProfile.NATURAL_EARTH_SOURCE, - "ne_10m_lakes", - 0 - )), + assertCoversZoomRange(6, 14, "water", process(SimpleFeature.create( rectangle(0, 10), Map.of( From 30c16401d83739e2f1b682bbec8150a60e43d94a Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Wed, 27 Mar 2024 12:59:58 +0100 Subject: [PATCH 151/211] Use ne_10m_admin_0_boundary_lines_land_disputed to fix missing South Sudan boundary --- src/main/java/org/openmaptiles/layers/Boundary.java | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/openmaptiles/layers/Boundary.java b/src/main/java/org/openmaptiles/layers/Boundary.java index a5e5d305..52ad34aa 100644 --- a/src/main/java/org/openmaptiles/layers/Boundary.java +++ b/src/main/java/org/openmaptiles/layers/Boundary.java @@ -179,8 +179,17 @@ record BoundaryInfo(int adminLevel, int minzoom, int maxzoom) {} BoundaryInfo info = switch (table) { case "ne_110m_admin_0_boundary_lines_land" -> new BoundaryInfo(2, 0, 0); case "ne_50m_admin_0_boundary_lines_land" -> new BoundaryInfo(2, 1, 3); - case "ne_10m_admin_0_boundary_lines_land" -> feature.hasTag("featurecla", "Lease limit") ? null : - new BoundaryInfo(2, 4, 4); + case "ne_10m_admin_0_boundary_lines_land" -> { + boolean isDisputedSouthSudanAndKenya = false; + if (disputed) { + String left = feature.getString("adm0_left"); + String right = feature.getString("adm0_right"); + isDisputedSouthSudanAndKenya = "South Sudan".equals(left) && "Kenya".equals(right); + } + yield isDisputedSouthSudanAndKenya ? new BoundaryInfo(2, 1, 4) : + feature.hasTag("featurecla", "Lease limit") ? null : + new BoundaryInfo(2, 4, 4); + } case "ne_10m_admin_1_states_provinces_lines" -> { Double minZoom = Parse.parseDoubleOrNull(feature.getTag("min_zoom")); yield minZoom != null && minZoom <= 7 ? new BoundaryInfo(4, 1, 4) : From 567c6c5be5973b876f5f08c03bf993fae0291b97 Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Wed, 27 Mar 2024 13:14:24 +0100 Subject: [PATCH 152/211] testNaturalEarthCountryBoundaries() extended to cover adjusted handling of ne_10m_admin_0_boundary_lines_land --- .../org/openmaptiles/layers/BoundaryTest.java | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/test/java/org/openmaptiles/layers/BoundaryTest.java b/src/test/java/org/openmaptiles/layers/BoundaryTest.java index 14138ffd..ffc539a6 100644 --- a/src/test/java/org/openmaptiles/layers/BoundaryTest.java +++ b/src/test/java/org/openmaptiles/layers/BoundaryTest.java @@ -104,6 +104,7 @@ void testNaturalEarthCountryBoundaries() { assertFeatures(0, List.of(Map.of( "_layer", "boundary", "_type", "line", + "_minzoom", 4, "admin_level", 2 )), process(SimpleFeature.create( newLineString(0, 0, 1, 1), @@ -115,6 +116,23 @@ void testNaturalEarthCountryBoundaries() { 0 ))); + assertFeatures(0, List.of(Map.of( + "_layer", "boundary", + "_type", "line", + "_minzoom", 1, + "admin_level", 2 + )), process(SimpleFeature.create( + newLineString(0, 0, 1, 1), + Map.of( + "featurecla", "Disputed (please verify)", + "adm0_left", "South Sudan", + "adm0_right", "Kenya" + ), + OpenMapTilesProfile.NATURAL_EARTH_SOURCE, + "ne_10m_admin_0_boundary_lines_land", + 0 + ))); + assertFeatures(0, List.of(), process(SimpleFeature.create( newLineString(0, 0, 1, 1), Map.of( From 58c86c66b9ad7bfda7a04023bf9b8c9367c8850b Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Wed, 27 Mar 2024 21:53:54 +0100 Subject: [PATCH 153/211] OSM lake area limit increased 2x to match smallest NE lake --- src/main/java/org/openmaptiles/layers/Water.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/openmaptiles/layers/Water.java b/src/main/java/org/openmaptiles/layers/Water.java index 6655a9df..adb4ec5c 100644 --- a/src/main/java/org/openmaptiles/layers/Water.java +++ b/src/main/java/org/openmaptiles/layers/Water.java @@ -82,8 +82,8 @@ public class Water implements * which infers ocean polygons by preprocessing all coastline elements. */ - // smallest NE lake is around 4.42E-13, this is roughly 1/2 of that and approx 17% of OSM features are smaller than this: - private static final double SMALLEST_OSM_LAKE_AREA = Math.pow(4, -21); + // smallest NE lake is around 4.42E-13, this is roughly that and approx. 23% of OSM features are smaller than this: + private static final double SMALLEST_OSM_LAKE_AREA = Math.pow(4, -20.5); private final MultiExpression.Index classMapping; private final PlanetilerConfig config; @@ -210,7 +210,7 @@ void fillOsmIdIntoNeLake(Tables.OsmWaterPolygon element) { for (var index : neLakeIndexes.values()) { var items = index.query(envelope); if (items.size() == 1) { - if (items.getFirst()instanceof LakeInfo lakeInfo) { + if (items.getFirst() instanceof LakeInfo lakeInfo) { fillOsmIdIntoNeLake(element, geom, lakeInfo); } } else if (!items.isEmpty()) { From acbaa22dbac1dabbd50f09e411e9d74f2200fd28 Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Wed, 27 Mar 2024 21:56:16 +0100 Subject: [PATCH 154/211] OSM lake area limit increased 2x even further to match smallest observed during Planet processing --- src/main/java/org/openmaptiles/layers/Water.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/openmaptiles/layers/Water.java b/src/main/java/org/openmaptiles/layers/Water.java index adb4ec5c..56fa91db 100644 --- a/src/main/java/org/openmaptiles/layers/Water.java +++ b/src/main/java/org/openmaptiles/layers/Water.java @@ -82,8 +82,9 @@ public class Water implements * which infers ocean polygons by preprocessing all coastline elements. */ - // smallest NE lake is around 4.42E-13, this is roughly that and approx. 23% of OSM features are smaller than this: - private static final double SMALLEST_OSM_LAKE_AREA = Math.pow(4, -20.5); + // smallest NE lake is around 4.42E-13, smallest matching OSM lake is 9.34E-13, this is slightly bellow that + // and approx. 33% of OSM features are smaller than this, hence to save some CPU cycles: + private static final double SMALLEST_OSM_LAKE_AREA = Math.pow(4, -20); private final MultiExpression.Index classMapping; private final PlanetilerConfig config; From af13750adfa3e91ef3f8b658d7ab7e74d61552ad Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Wed, 27 Mar 2024 21:58:13 +0100 Subject: [PATCH 155/211] clean-up: SMALLEST_OSM_LAKE_AREA renamed to OSM_ID_MATCH_AREA_LIMIT to better match what it is --- src/main/java/org/openmaptiles/layers/Water.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/openmaptiles/layers/Water.java b/src/main/java/org/openmaptiles/layers/Water.java index 56fa91db..c0262415 100644 --- a/src/main/java/org/openmaptiles/layers/Water.java +++ b/src/main/java/org/openmaptiles/layers/Water.java @@ -84,7 +84,7 @@ public class Water implements // smallest NE lake is around 4.42E-13, smallest matching OSM lake is 9.34E-13, this is slightly bellow that // and approx. 33% of OSM features are smaller than this, hence to save some CPU cycles: - private static final double SMALLEST_OSM_LAKE_AREA = Math.pow(4, -20); + private static final double OSM_ID_MATCH_AREA_LIMIT = Math.pow(4, -20); private final MultiExpression.Index classMapping; private final PlanetilerConfig config; @@ -201,7 +201,7 @@ void fillOsmIdIntoNeLake(Tables.OsmWaterPolygon element) { // if OSM lake is too small for Z6 (e.g. area bellow ~4px) we assume there is no matching NE lake Geometry geom = element.source().worldGeometry(); - if (geom.getArea() < SMALLEST_OSM_LAKE_AREA) { + if (geom.getArea() < OSM_ID_MATCH_AREA_LIMIT) { return; } From baeb8797c3e24497bfc1f86e2a136a2cf7471ba6 Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Thu, 28 Mar 2024 11:05:02 +0100 Subject: [PATCH 156/211] mvn spotless:apply --- src/main/java/org/openmaptiles/layers/Water.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/openmaptiles/layers/Water.java b/src/main/java/org/openmaptiles/layers/Water.java index c0262415..5a7cf7b9 100644 --- a/src/main/java/org/openmaptiles/layers/Water.java +++ b/src/main/java/org/openmaptiles/layers/Water.java @@ -211,7 +211,7 @@ void fillOsmIdIntoNeLake(Tables.OsmWaterPolygon element) { for (var index : neLakeIndexes.values()) { var items = index.query(envelope); if (items.size() == 1) { - if (items.getFirst() instanceof LakeInfo lakeInfo) { + if (items.getFirst()instanceof LakeInfo lakeInfo) { fillOsmIdIntoNeLake(element, geom, lakeInfo); } } else if (!items.isEmpty()) { From 943287c3483b91b094c9ef0a7ba87bfcd5fb2a5f Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Tue, 2 Apr 2024 12:02:23 +0200 Subject: [PATCH 157/211] properly handle center line vs. point also for lakes --- src/main/java/org/openmaptiles/layers/WaterName.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/openmaptiles/layers/WaterName.java b/src/main/java/org/openmaptiles/layers/WaterName.java index 4a0ee16a..6ed3144e 100644 --- a/src/main/java/org/openmaptiles/layers/WaterName.java +++ b/src/main/java/org/openmaptiles/layers/WaterName.java @@ -220,7 +220,7 @@ public void process(Tables.OsmWaterPolygon element, FeatureCollector features) { if (nullIfEmpty(element.name()) != null) { try { Geometry centerlineGeometry = lakeCenterlines.get(element.source().id()); - int minzoom = MINZOOM_BAY; + int minzoomCL = MINZOOM_BAY; String place = element.place(); String clazz; if ("bay".equals(element.natural())) { @@ -229,33 +229,33 @@ public void process(Tables.OsmWaterPolygon element, FeatureCollector features) { clazz = FieldValues.CLASS_SEA; } else { clazz = FieldValues.CLASS_LAKE; - minzoom = MINZOOM_LAKE; + minzoomCL = MINZOOM_LAKE; } if (centerlineGeometry != null) { // prefer lake centerline if it exists, but point will be also used if minzoom bellow 9 is calculated from area // note: Here we're diverging from OpenMapTiles: For bays with minzoom (based on area) point is used between // minzoom and Z8 and for Z9+ centerline is used, while OpenMaptiles sticks with points. setupOsmWaterPolygonFeature( - element, features.geometry(LAYER_NAME, centerlineGeometry), clazz, Math.min(minzoom, MINZOOM_BAY)) + element, features.geometry(LAYER_NAME, centerlineGeometry), clazz, Math.min(minzoomCL, MINZOOM_BAY)) // TODO: drop the min(), not needed here .setMinPixelSizeBelowZoom(13, 6d * element.name().length()); } // Show a label if a water feature covers at least 1/4 of a tile or z14+ Geometry geometry = element.source().worldGeometry(); double area = geometry.getArea(); - minzoom = (int) Math.floor(-1d - Math.log(Math.sqrt(area)) / LOG2); + int minzoom = (int) Math.floor(-1d - Math.log(Math.sqrt(area)) / LOG2); if (place != null && SEA_OR_OCEAN_PLACE.contains(place)) { minzoom = Math.clamp(minzoom, MINZOOM_SEA_AND_OCEAN, 14); } else { minzoom = Math.clamp(minzoom, MINZOOM_LAKE, 14); } - if (centerlineGeometry == null || minzoom < MINZOOM_BAY) { + if (centerlineGeometry == null || minzoom < minzoomCL) { // otherwise just use a label point inside the lake var feature = setupOsmWaterPolygonFeature(element, features.pointOnSurface(LAYER_NAME), clazz, minzoom); if (centerlineGeometry != null) { // centerline already created, so make sure we're not having both at same zoom level - feature.setMaxZoom(MINZOOM_BAY - 1); + feature.setMaxZoom(minzoomCL - 1); } } } catch (GeometryException e) { From 806ae5ba15a0f0aa5e948ef1d5aa0a2b71698339 Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Tue, 2 Apr 2024 12:03:23 +0200 Subject: [PATCH 158/211] clean-up: min() dropped since nothing sets minzoomCL to value higher than MINZOOM_BAY --- 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 6ed3144e..3acf5f40 100644 --- a/src/main/java/org/openmaptiles/layers/WaterName.java +++ b/src/main/java/org/openmaptiles/layers/WaterName.java @@ -236,7 +236,7 @@ public void process(Tables.OsmWaterPolygon element, FeatureCollector features) { // note: Here we're diverging from OpenMapTiles: For bays with minzoom (based on area) point is used between // minzoom and Z8 and for Z9+ centerline is used, while OpenMaptiles sticks with points. setupOsmWaterPolygonFeature( - element, features.geometry(LAYER_NAME, centerlineGeometry), clazz, Math.min(minzoomCL, MINZOOM_BAY)) // TODO: drop the min(), not needed here + element, features.geometry(LAYER_NAME, centerlineGeometry), clazz, minzoomCL) .setMinPixelSizeBelowZoom(13, 6d * element.name().length()); } From c56b98fba238031781cca3523460d5ab1b340a60 Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Tue, 2 Apr 2024 20:00:08 +0200 Subject: [PATCH 159/211] Revert e-road and a-road. --- .../openmaptiles/layers/Transportation.java | 6 +----- .../layers/TransportationTest.java | 21 ++++++++++--------- 2 files changed, 12 insertions(+), 15 deletions(-) diff --git a/src/main/java/org/openmaptiles/layers/Transportation.java b/src/main/java/org/openmaptiles/layers/Transportation.java index 2c442bf6..464c96dc 100644 --- a/src/main/java/org/openmaptiles/layers/Transportation.java +++ b/src/main/java/org/openmaptiles/layers/Transportation.java @@ -296,11 +296,7 @@ public List preprocessOsmRelation(OsmElement.Relation relation) String colour = coalesce( nullIfEmpty(relation.getString("colour")), nullIfEmpty(relation.getString("ref:colour"))); - if ("e-road".equals(network)) { - networkType = RouteNetwork.E_ROAD; - } else if ("AsianHighway".equals(network)) { - networkType = RouteNetwork.A_ROAD; - } else if ("US:I".equals(network)) { + if ("US:I".equals(network)) { networkType = RouteNetwork.US_INTERSTATE; } else if ("US:US".equals(network)) { networkType = RouteNetwork.US_HIGHWAY; diff --git a/src/test/java/org/openmaptiles/layers/TransportationTest.java b/src/test/java/org/openmaptiles/layers/TransportationTest.java index 2210aa65..80fe4a44 100644 --- a/src/test/java/org/openmaptiles/layers/TransportationTest.java +++ b/src/test/java/org/openmaptiles/layers/TransportationTest.java @@ -2121,19 +2121,20 @@ void testARoad() { FeatureCollector features = process(lineFeatureWithRelation( profile.preprocessOsmRelation(rel), Map.of( - "highway", "trunk" + "highway", "trunk", + "name", "National Highway 7", + "ref", "7" ))); assertFeatures(13, List.of(Map.of( "_layer", "transportation", "class", "trunk", - "network", "a-road", - "_minzoom", 4 + "_minzoom", 5 ), Map.of( "_layer", "transportation_name", "class", "trunk", - "ref", "AH11", - "network", "a-road" + "ref", "7", + "route_1_ref", "AH11" )), features); } @@ -2143,24 +2144,24 @@ void testERoad() { rel.setTag("type", "route"); rel.setTag("route", "road"); rel.setTag("network", "e-road"); - rel.setTag("ref", "E 50"); + rel.setTag("ref", "E 77"); FeatureCollector features = process(lineFeatureWithRelation( profile.preprocessOsmRelation(rel), Map.of( - "highway", "motorway" + "highway", "motorway", + "ref", "S7" ))); assertFeatures(13, List.of(Map.of( "_layer", "transportation", "class", "motorway", - "network", "e-road", "_minzoom", 4 ), Map.of( "_layer", "transportation_name", "class", "motorway", - "ref", "E 50", - "network", "e-road" + "ref", "S7", + "route_1_ref", "E 77" )), features); } } From 4d3936ada0f489312c2e549cab6f7ec2716bfb47 Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Tue, 2 Apr 2024 20:12:25 +0200 Subject: [PATCH 160/211] Revert e-road and a-road. (follow-up) --- src/test/java/org/openmaptiles/layers/TransportationTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/java/org/openmaptiles/layers/TransportationTest.java b/src/test/java/org/openmaptiles/layers/TransportationTest.java index 80fe4a44..c777cbfc 100644 --- a/src/test/java/org/openmaptiles/layers/TransportationTest.java +++ b/src/test/java/org/openmaptiles/layers/TransportationTest.java @@ -580,8 +580,8 @@ void testPolishHighwayIssue165() { "_layer", "transportation_name", "class", "trunk", "name", "", - "ref", "E 28", - "ref_length", 4, + "ref", "S7", + "ref_length", 2, "route_1_network", "e-road", "route_1_ref", "E 28", "route_2_network", "e-road", From bef562bb5c3e921b1e88564ab268fd1119040490 Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Tue, 2 Apr 2024 20:14:33 +0200 Subject: [PATCH 161/211] added unit test to cover: More roads for z4. --- .../layers/TransportationTest.java | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/test/java/org/openmaptiles/layers/TransportationTest.java b/src/test/java/org/openmaptiles/layers/TransportationTest.java index c777cbfc..8e496444 100644 --- a/src/test/java/org/openmaptiles/layers/TransportationTest.java +++ b/src/test/java/org/openmaptiles/layers/TransportationTest.java @@ -670,6 +670,26 @@ void testInterstateMotorwayWithoutWayInfo() { )), features); } + @Test + void testMotorwayRoadConstruction() { + assertFeatures(13, List.of(Map.of( + "_layer", "transportation", + "class", "motorway_construction", + "oneway", 1, + "_minzoom", 4 + ), Map.of( + "_layer", "transportation_name", + "name", "D4 II/118 – Milín", + "class", "motorway_construction", + "_minzoom", 6 + )), process(lineFeature(Map.of( + "highway", "construction", + "construction", "motorway", + "name", "D4 II/118 – Milín", + "oneway", "yes" + )))); + } + @Test void testPrimaryRoadConstruction() { assertFeatures(13, List.of(Map.of( From a932793ba0fcb0394435988e1a8eba89d999640b Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Wed, 17 Apr 2024 21:04:07 +0200 Subject: [PATCH 162/211] improved handling of disputed KE:SS border: will work even if left/right reversed --- .../org/openmaptiles/layers/Boundary.java | 8 +++- .../org/openmaptiles/layers/BoundaryTest.java | 38 +++++++++++++++++++ 2 files changed, 45 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/openmaptiles/layers/Boundary.java b/src/main/java/org/openmaptiles/layers/Boundary.java index 52ad34aa..b075a01d 100644 --- a/src/main/java/org/openmaptiles/layers/Boundary.java +++ b/src/main/java/org/openmaptiles/layers/Boundary.java @@ -121,6 +121,8 @@ public class Boundary implements private static final Logger LOGGER = LoggerFactory.getLogger(Boundary.class); private static final double COUNTRY_TEST_OFFSET = GeoUtils.metersToPixelAtEquator(0, 10) / 256d; + private static final String COUNTRY_KE = "Kenya"; + private static final String COUNTRY_SS = "South Sudan"; private final Stats stats; private final boolean addCountryNames; private final boolean onlyOsmBoundaries; @@ -184,7 +186,11 @@ record BoundaryInfo(int adminLevel, int minzoom, int maxzoom) {} if (disputed) { String left = feature.getString("adm0_left"); String right = feature.getString("adm0_right"); - isDisputedSouthSudanAndKenya = "South Sudan".equals(left) && "Kenya".equals(right); + if (COUNTRY_SS.equals(left)) { + isDisputedSouthSudanAndKenya = COUNTRY_KE.equals(right); + } else if (COUNTRY_KE.equals(left)) { + isDisputedSouthSudanAndKenya = COUNTRY_SS.equals(right); + } } yield isDisputedSouthSudanAndKenya ? new BoundaryInfo(2, 1, 4) : feature.hasTag("featurecla", "Lease limit") ? null : diff --git a/src/test/java/org/openmaptiles/layers/BoundaryTest.java b/src/test/java/org/openmaptiles/layers/BoundaryTest.java index ffc539a6..1aa9e676 100644 --- a/src/test/java/org/openmaptiles/layers/BoundaryTest.java +++ b/src/test/java/org/openmaptiles/layers/BoundaryTest.java @@ -144,6 +144,44 @@ void testNaturalEarthCountryBoundaries() { ))); } + @Test + void testNaturalEarthCountryKeSsBoundaryReversed() { + assertFeatures(0, List.of(Map.of( + "_layer", "boundary", + "_minzoom", 1, + "admin_level", 2 + )), process(SimpleFeature.create( + newLineString(0, 0, 1, 1), + Map.of( + "featurecla", "Disputed (please verify)", + "adm0_right", "South Sudan", + "adm0_left", "Kenya" + ), + OpenMapTilesProfile.NATURAL_EARTH_SOURCE, + "ne_10m_admin_0_boundary_lines_land", + 0 + ))); + } + + @Test + void testNaturalEarthCountryNotKeSsBoundary() { + assertFeatures(0, List.of(Map.of( + "_layer", "boundary", + "_minzoom", 4, + "admin_level", 2 + )), process(SimpleFeature.create( + newLineString(0, 0, 1, 1), + Map.of( + "featurecla", "Disputed (please verify)", + "adm0_left", "South Sudan", + "adm0_right", "Uganda" + ), + OpenMapTilesProfile.NATURAL_EARTH_SOURCE, + "ne_10m_admin_0_boundary_lines_land", + 0 + ))); + } + @Test void testNaturalEarthStateBoundaries() { assertFeatures(0, List.of(Map.of( From cc797f9aa7e08e7239f4789d29c122f4fdf756a3 Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Wed, 17 Apr 2024 21:57:41 +0200 Subject: [PATCH 163/211] new PolygonIndex.getIntersecting() used for OSM lake ID matching + increased unit test coverage --- .../java/org/openmaptiles/layers/Water.java | 41 ++-- .../org/openmaptiles/layers/WaterTest.java | 187 ++++++++++++++++++ 2 files changed, 203 insertions(+), 25 deletions(-) diff --git a/src/main/java/org/openmaptiles/layers/Water.java b/src/main/java/org/openmaptiles/layers/Water.java index 5a7cf7b9..c54f3c8d 100644 --- a/src/main/java/org/openmaptiles/layers/Water.java +++ b/src/main/java/org/openmaptiles/layers/Water.java @@ -42,6 +42,7 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE import com.onthegomap.planetiler.config.PlanetilerConfig; import com.onthegomap.planetiler.expression.MultiExpression; import com.onthegomap.planetiler.geo.GeometryException; +import com.onthegomap.planetiler.geo.PolygonIndex; import com.onthegomap.planetiler.reader.SimpleFeature; import com.onthegomap.planetiler.reader.SourceFeature; import com.onthegomap.planetiler.stats.Stats; @@ -51,11 +52,9 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.function.Consumer; -import org.locationtech.jts.geom.Envelope; import org.locationtech.jts.geom.Geometry; import org.locationtech.jts.geom.TopologyException; import org.locationtech.jts.geom.util.GeometryFixer; -import org.locationtech.jts.index.strtree.STRtree; import org.openmaptiles.OpenMapTilesProfile; import org.openmaptiles.generated.OpenMapTilesSchema; import org.openmaptiles.generated.Tables; @@ -89,7 +88,7 @@ public class Water implements private final MultiExpression.Index classMapping; private final PlanetilerConfig config; private final Stats stats; - private final Map neLakeIndexes = new ConcurrentHashMap<>(); + private final Map> neLakeIndexes = new ConcurrentHashMap<>(); private final Map> neLakeNameMaps = new ConcurrentHashMap<>(); private final List neAllLakeInfos = new ArrayList<>(); @@ -125,12 +124,12 @@ record WaterInfo(int minZoom, int maxZoom, String clazz) {} lakeInfo.geom = geom; lakeInfo.name = feature.getString("name"); - var index = neLakeIndexes.computeIfAbsent(table, t -> new STRtree()); + var index = neLakeIndexes.computeIfAbsent(table, t -> PolygonIndex.create()); var neLakeNameMap = neLakeNameMaps.computeIfAbsent(table, t -> new ConcurrentHashMap<>()); // need to externally synchronize inserts into the STRTree and ArrayList synchronized (this) { - index.insert(geom.getEnvelopeInternal(), lakeInfo); + index.put(geom, lakeInfo); neAllLakeInfos.add(lakeInfo); } if (lakeInfo.name != null) { @@ -191,7 +190,7 @@ void fillOsmIdIntoNeLake(Tables.OsmWaterPolygon element) { var lakeInfo = map.get(element.name()); if (lakeInfo != null) { match = true; - fillOsmIdIntoNeLake(element, element.source().worldGeometry(), lakeInfo); + fillOsmIdIntoNeLake(element, element.source().worldGeometry(), lakeInfo, true); } } } @@ -206,20 +205,10 @@ void fillOsmIdIntoNeLake(Tables.OsmWaterPolygon element) { } // match by intersection: - Envelope envelope = geom.getEnvelopeInternal(); - for (var index : neLakeIndexes.values()) { - var items = index.query(envelope); - if (items.size() == 1) { - if (items.getFirst()instanceof LakeInfo lakeInfo) { - fillOsmIdIntoNeLake(element, geom, lakeInfo); - } - } else if (!items.isEmpty()) { - for (var item : items) { - if (item instanceof LakeInfo lakeInfo) { - fillOsmIdIntoNeLake(element, geom, lakeInfo); - } - } + var items = index.getIntersecting(geom); + for (var lakeInfo : items) { + fillOsmIdIntoNeLake(element, geom, lakeInfo, false); } } } catch (GeometryException e) { @@ -228,18 +217,23 @@ void fillOsmIdIntoNeLake(Tables.OsmWaterPolygon element) { } } - void fillOsmIdIntoNeLake(Tables.OsmWaterPolygon element, Geometry geom, LakeInfo lakeInfo) throws GeometryException { + /* + * When we match lakes with `neLakeIndexes` then `intersetsCheckNeeded` should be `false`, + * otherwise `true`, to make sure we DO check the intersection but to avoid checking it twice. + */ + void fillOsmIdIntoNeLake(Tables.OsmWaterPolygon element, Geometry geom, LakeInfo lakeInfo, + boolean intersetsCheckNeeded) throws GeometryException { Geometry neGeom = lakeInfo.geom; Geometry intersection; try { - if (!neGeom.intersects(geom)) { + if (intersetsCheckNeeded && !neGeom.intersects(geom)) { return; } intersection = neGeom.intersection(geom); } catch (TopologyException e) { try { Geometry fixedGeom = GeometryFixer.fix(geom); - if (!neGeom.intersects(fixedGeom)) { + if (intersetsCheckNeeded && !neGeom.intersects(fixedGeom)) { return; } intersection = neGeom.intersection(fixedGeom); @@ -262,9 +256,6 @@ public void finish(String sourceName, FeatureCollector.Factory featureCollectors Consumer emit) { if (OpenMapTilesProfile.NATURAL_EARTH_SOURCE.equals(sourceName)) { var timer = stats.startStage("ne_lake_index"); - for (var index : neLakeIndexes.values()) { - index.build(); - } timer.stop(); } else if (OpenMapTilesProfile.OSM_SOURCE.equals(sourceName)) { var timer = stats.startStage("ne_lakes"); diff --git a/src/test/java/org/openmaptiles/layers/WaterTest.java b/src/test/java/org/openmaptiles/layers/WaterTest.java index 3264e55e..62995de2 100644 --- a/src/test/java/org/openmaptiles/layers/WaterTest.java +++ b/src/test/java/org/openmaptiles/layers/WaterTest.java @@ -96,6 +96,114 @@ void testLakeNaturalEarthByIntersection() { )), features); } + @Test + void testLakeNaturalEarthIntersectionMiss() { + final var polygon1 = rectangle(0, 0.1); + final var polygon2 = rectangle(0.2, 0.3); + // NE lakes: + process(SimpleFeature.create( + polygon1, + Map.of(), + OpenMapTilesProfile.NATURAL_EARTH_SOURCE, + "ne_110m_lakes", + 0 + )); + process(SimpleFeature.create( + polygon1, + Map.of(), + OpenMapTilesProfile.NATURAL_EARTH_SOURCE, + "ne_10m_lakes", + 0 + )); + // OSM lake to take the ID from: + process(SimpleFeature.create( + polygon2, + new HashMap<>(Map.of( + "natural", "water", + "water", "reservoir" + )), + OpenMapTilesProfile.OSM_SOURCE, + null, + 123 + )); + + List features = new ArrayList<>(); + profile.finish(OpenMapTilesProfile.OSM_SOURCE, new FeatureCollector.Factory(params, stats), features::add); + assertFeatures(0, List.of(Map.of( + "class", "lake", + "id", "", + "_layer", "water", + "_type", "polygon" + ), Map.of( + "class", "lake", + "id", "", + "_layer", "water", + "_type", "polygon" + )), features); + } + + @Test + void testLakeNaturalEarthByBiggerIntersection() { + final var polygon1 = rectangle(0, 0.1); + final var polygon2 = rectangle(0, 0.2); + // NE lakes: + process(SimpleFeature.create( + polygon2, + Map.of(), + OpenMapTilesProfile.NATURAL_EARTH_SOURCE, + "ne_110m_lakes", + 0 + )); + process(SimpleFeature.create( + polygon2, + Map.of(), + OpenMapTilesProfile.NATURAL_EARTH_SOURCE, + "ne_10m_lakes", + 0 + )); + // OSM lakes to take the ID from: + process(SimpleFeature.create( + polygon1, + new HashMap<>(Map.of( + "natural", "water", + "water", "reservoir" + )), + OpenMapTilesProfile.OSM_SOURCE, + null, + 123 + )); + process(SimpleFeature.create( + polygon2, + new HashMap<>(Map.of( + "natural", "water", + "water", "reservoir" + )), + OpenMapTilesProfile.OSM_SOURCE, + null, + 234 + )); + + List features = new ArrayList<>(); + profile.finish(OpenMapTilesProfile.OSM_SOURCE, new FeatureCollector.Factory(params, stats), features::add); + assertFeatures(0, List.of(Map.of( + "class", "lake", + "intermittent", "", + "id", 234L, + "_layer", "water", + "_type", "polygon", + "_minzoom", 0, + "_maxzoom", 1 + ), Map.of( + "class", "lake", + "intermittent", "", + "id", 234L, + "_layer", "water", + "_type", "polygon", + "_minzoom", 4, + "_maxzoom", 5 + )), features); + } + @Test void testLakeNaturalEarthByName() { final var polygon = rectangle(0, 0.1); @@ -131,6 +239,85 @@ void testLakeNaturalEarthByName() { )), features); } + @Test + void testLakeNaturalEarthByNameIntersectionMiss() { + final var polygon1 = rectangle(0, 0.1); + final var polygon2 = rectangle(0.2, 0.3); + // NE lake: + process(SimpleFeature.create( + polygon1, + Map.of("name", "Test Lake"), + OpenMapTilesProfile.NATURAL_EARTH_SOURCE, + "ne_50m_lakes", + 0 + )); + // OSM lake to take the ID from: + process(SimpleFeature.create( + polygon2, + new HashMap<>(Map.of( + "name", "Test Lake", + "natural", "water", + "water", "reservoir" + )), + OpenMapTilesProfile.OSM_SOURCE, + null, + 123 + )); + + List features = new ArrayList<>(); + profile.finish(OpenMapTilesProfile.OSM_SOURCE, new FeatureCollector.Factory(params, stats), features::add); + assertFeatures(0, List.of(Map.of( + "class", "lake", + "id", "", + "_layer", "water" + )), features); + } + + @Test + void testLakeNaturalEarthByNameAndBiggerIntersection() { + final var polygon1 = rectangle(0, 0.1); + final var polygon2 = rectangle(0, 0.2); + // NE lake: + process(SimpleFeature.create( + polygon2, + Map.of("name", "Test Lake"), + OpenMapTilesProfile.NATURAL_EARTH_SOURCE, + "ne_50m_lakes", + 0 + )); + // OSM lakes to take the ID from: + process(SimpleFeature.create( + polygon1, + new HashMap<>(Map.of( + "name", "Test Lake", + "natural", "water", + "water", "reservoir" + )), + OpenMapTilesProfile.OSM_SOURCE, + null, + 123 + )); + process(SimpleFeature.create( + polygon2, + new HashMap<>(Map.of( + "name", "Test Lake", + "natural", "water", + "water", "reservoir" + )), + OpenMapTilesProfile.OSM_SOURCE, + null, + 234 + )); + + List features = new ArrayList<>(); + profile.finish(OpenMapTilesProfile.OSM_SOURCE, new FeatureCollector.Factory(params, stats), features::add); + assertFeatures(0, List.of(Map.of( + "class", "lake", + "id", 234L, + "_layer", "water" + )), features); + } + @Test void testWaterOsmWaterPolygon() { assertFeatures(0, List.of(Map.of( From 976751287c0d4637ddda5ca59946ce0ac8741d75 Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Wed, 17 Apr 2024 22:20:02 +0200 Subject: [PATCH 164/211] neLakeIndexes simplified into neLakeIndex --- .../java/org/openmaptiles/layers/Water.java | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/src/main/java/org/openmaptiles/layers/Water.java b/src/main/java/org/openmaptiles/layers/Water.java index c54f3c8d..97be97e3 100644 --- a/src/main/java/org/openmaptiles/layers/Water.java +++ b/src/main/java/org/openmaptiles/layers/Water.java @@ -88,9 +88,9 @@ public class Water implements private final MultiExpression.Index classMapping; private final PlanetilerConfig config; private final Stats stats; - private final Map> neLakeIndexes = new ConcurrentHashMap<>(); - private final Map> neLakeNameMaps = new ConcurrentHashMap<>(); - private final List neAllLakeInfos = new ArrayList<>(); + private PolygonIndex neLakeIndex = PolygonIndex.create(); + private final Map> neLakeNameMaps = new ConcurrentHashMap<>(); // TODO: simplify in the same way as neLakeIndexes + private final List neAllLakeInfos = new ArrayList<>(); // TODO: once neLakeNameMaps is simplified, we may remove this and use only neLakeNameMap.values() public Water(Translations translations, PlanetilerConfig config, Stats stats) { this.classMapping = FieldMappings.Class.index(); @@ -124,12 +124,11 @@ record WaterInfo(int minZoom, int maxZoom, String clazz) {} lakeInfo.geom = geom; lakeInfo.name = feature.getString("name"); - var index = neLakeIndexes.computeIfAbsent(table, t -> PolygonIndex.create()); var neLakeNameMap = neLakeNameMaps.computeIfAbsent(table, t -> new ConcurrentHashMap<>()); // need to externally synchronize inserts into the STRTree and ArrayList synchronized (this) { - index.put(geom, lakeInfo); + neLakeIndex.put(geom, lakeInfo); // TODO: this no longer needs `synchronized` neAllLakeInfos.add(lakeInfo); } if (lakeInfo.name != null) { @@ -205,11 +204,9 @@ void fillOsmIdIntoNeLake(Tables.OsmWaterPolygon element) { } // match by intersection: - for (var index : neLakeIndexes.values()) { - var items = index.getIntersecting(geom); - for (var lakeInfo : items) { - fillOsmIdIntoNeLake(element, geom, lakeInfo, false); - } + var items = neLakeIndex.getIntersecting(geom); + for (var lakeInfo : items) { + fillOsmIdIntoNeLake(element, geom, lakeInfo, false); } } catch (GeometryException e) { e.log(stats, "omt_water", @@ -267,7 +264,7 @@ public void finish(String sourceName, FeatureCollector.Factory featureCollectors } } neLakeNameMaps.clear(); - neLakeIndexes.clear(); + neLakeIndex = null; neAllLakeInfos.clear(); timer.stop(); } From f31efa65b7ecf1923ba53fca2d45ff64a5a1b875 Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Wed, 17 Apr 2024 22:21:01 +0200 Subject: [PATCH 165/211] test several NE lakes also in testLakeNaturalEarthByName() --- .../java/org/openmaptiles/layers/WaterTest.java | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/test/java/org/openmaptiles/layers/WaterTest.java b/src/test/java/org/openmaptiles/layers/WaterTest.java index 62995de2..3cd16705 100644 --- a/src/test/java/org/openmaptiles/layers/WaterTest.java +++ b/src/test/java/org/openmaptiles/layers/WaterTest.java @@ -207,7 +207,7 @@ void testLakeNaturalEarthByBiggerIntersection() { @Test void testLakeNaturalEarthByName() { final var polygon = rectangle(0, 0.1); - // NE lake: + // NE lakes: process(SimpleFeature.create( polygon, Map.of("name", "Test Lake"), @@ -215,6 +215,13 @@ void testLakeNaturalEarthByName() { "ne_50m_lakes", 0 )); + process(SimpleFeature.create( + polygon, + Map.of("name", "Test Lake"), + OpenMapTilesProfile.NATURAL_EARTH_SOURCE, + "ne_10m_lakes", + 0 + )); // OSM lake to take the ID from: process(SimpleFeature.create( polygon, @@ -236,6 +243,12 @@ void testLakeNaturalEarthByName() { "_layer", "water", "_minzoom", 2, "_maxzoom", 3 + ), Map.of( + "class", "lake", + "id", 123L, + "_layer", "water", + "_minzoom", 4, + "_maxzoom", 5 )), features); } From cde56853af98df2f11a3b2dafbf90d33e80e3f56 Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Wed, 17 Apr 2024 22:22:59 +0200 Subject: [PATCH 166/211] clean-up: synchronized NOT needed for PolygonIndex.put() --- src/main/java/org/openmaptiles/layers/Water.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/openmaptiles/layers/Water.java b/src/main/java/org/openmaptiles/layers/Water.java index 97be97e3..562219fa 100644 --- a/src/main/java/org/openmaptiles/layers/Water.java +++ b/src/main/java/org/openmaptiles/layers/Water.java @@ -126,11 +126,11 @@ record WaterInfo(int minZoom, int maxZoom, String clazz) {} var neLakeNameMap = neLakeNameMaps.computeIfAbsent(table, t -> new ConcurrentHashMap<>()); - // need to externally synchronize inserts into the STRTree and ArrayList + // need to externally synchronize inserts into ArrayList synchronized (this) { - neLakeIndex.put(geom, lakeInfo); // TODO: this no longer needs `synchronized` neAllLakeInfos.add(lakeInfo); } + neLakeIndex.put(geom, lakeInfo); if (lakeInfo.name != null) { if (!neLakeNameMap.containsKey(lakeInfo.name) || lakeInfo.geom.getArea() > neLakeNameMap.get(lakeInfo.name).geom.getArea()) { From 6f4759caa620c0bfb7c437695c93389f67d41991 Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Wed, 17 Apr 2024 22:26:52 +0200 Subject: [PATCH 167/211] neLakeNameMaps will stay as is, otherwise same names from several NE tables will clash --- src/main/java/org/openmaptiles/layers/Water.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/openmaptiles/layers/Water.java b/src/main/java/org/openmaptiles/layers/Water.java index 562219fa..579a25be 100644 --- a/src/main/java/org/openmaptiles/layers/Water.java +++ b/src/main/java/org/openmaptiles/layers/Water.java @@ -89,7 +89,7 @@ public class Water implements private final PlanetilerConfig config; private final Stats stats; private PolygonIndex neLakeIndex = PolygonIndex.create(); - private final Map> neLakeNameMaps = new ConcurrentHashMap<>(); // TODO: simplify in the same way as neLakeIndexes + private final Map> neLakeNameMaps = new ConcurrentHashMap<>(); private final List neAllLakeInfos = new ArrayList<>(); // TODO: once neLakeNameMaps is simplified, we may remove this and use only neLakeNameMap.values() public Water(Translations translations, PlanetilerConfig config, Stats stats) { From 7c0d4aab27019cec5a8815fb624476a5edfd64bc Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Wed, 17 Apr 2024 23:40:11 +0200 Subject: [PATCH 168/211] neAllLakeInfos stays, since neLakeNameMaps contains only some and hard(er) to get all from neLakeIndex --- src/main/java/org/openmaptiles/layers/Water.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/openmaptiles/layers/Water.java b/src/main/java/org/openmaptiles/layers/Water.java index 579a25be..b80448ff 100644 --- a/src/main/java/org/openmaptiles/layers/Water.java +++ b/src/main/java/org/openmaptiles/layers/Water.java @@ -90,7 +90,7 @@ public class Water implements private final Stats stats; private PolygonIndex neLakeIndex = PolygonIndex.create(); private final Map> neLakeNameMaps = new ConcurrentHashMap<>(); - private final List neAllLakeInfos = new ArrayList<>(); // TODO: once neLakeNameMaps is simplified, we may remove this and use only neLakeNameMap.values() + private final List neAllLakeInfos = new ArrayList<>(); public Water(Translations translations, PlanetilerConfig config, Stats stats) { this.classMapping = FieldMappings.Class.index(); From 34b6f783e69882443110bde171b07b1d1f5615e3 Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Wed, 17 Apr 2024 23:41:35 +0200 Subject: [PATCH 169/211] added test for NE lake ame collision: bigger one gets to be matched --- .../org/openmaptiles/layers/WaterTest.java | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/src/test/java/org/openmaptiles/layers/WaterTest.java b/src/test/java/org/openmaptiles/layers/WaterTest.java index 3cd16705..e413538e 100644 --- a/src/test/java/org/openmaptiles/layers/WaterTest.java +++ b/src/test/java/org/openmaptiles/layers/WaterTest.java @@ -331,6 +331,51 @@ void testLakeNaturalEarthByNameAndBiggerIntersection() { )), features); } + @Test + void testLakeNaturalEarthByNameWithColision() { + final var polygonSmaller = rectangle(0, 0.1); + final var polygonBigger = rectangle(0, 0.2); + // NE lakes: + process(SimpleFeature.create( + polygonSmaller, + Map.of("name", "Test Lake"), + OpenMapTilesProfile.NATURAL_EARTH_SOURCE, + "ne_10m_lakes", + 0 + )); + process(SimpleFeature.create( + polygonBigger, + Map.of("name", "Test Lake"), + OpenMapTilesProfile.NATURAL_EARTH_SOURCE, + "ne_10m_lakes", + 0 + )); + // OSM lake to take the ID from: + process(SimpleFeature.create( + polygonBigger, + new HashMap<>(Map.of( + "name", "Test Lake", + "natural", "water", + "water", "reservoir" + )), + OpenMapTilesProfile.OSM_SOURCE, + null, + 123 + )); + + List features = new ArrayList<>(); + profile.finish(OpenMapTilesProfile.OSM_SOURCE, new FeatureCollector.Factory(params, stats), features::add); + assertFeatures(0, List.of(Map.of( + "class", "lake", + "id", "", + "_layer", "water" + ), Map.of( + "class", "lake", + "id", 123L, + "_layer", "water" + )), features); + } + @Test void testWaterOsmWaterPolygon() { assertFeatures(0, List.of(Map.of( From 9756cd6134e3a328a4389a4a5b06561e8b5160d1 Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Wed, 17 Apr 2024 23:55:03 +0200 Subject: [PATCH 170/211] simplified NE->OSM lake matching: ignore small OSM lakes right away --- src/main/java/org/openmaptiles/layers/Water.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/openmaptiles/layers/Water.java b/src/main/java/org/openmaptiles/layers/Water.java index b80448ff..3df69040 100644 --- a/src/main/java/org/openmaptiles/layers/Water.java +++ b/src/main/java/org/openmaptiles/layers/Water.java @@ -182,6 +182,12 @@ public void process(Tables.OsmWaterPolygon element, FeatureCollector features) { void fillOsmIdIntoNeLake(Tables.OsmWaterPolygon element) { try { + // if OSM lake is too small for Z6 (e.g. area bellow ~4px) we assume there is no matching NE lake + Geometry geom = element.source().worldGeometry(); + if (geom.getArea() < OSM_ID_MATCH_AREA_LIMIT) { + return; + } + // match by name: boolean match = false; if (element.name() != null) { @@ -197,12 +203,6 @@ void fillOsmIdIntoNeLake(Tables.OsmWaterPolygon element) { return; } - // if OSM lake is too small for Z6 (e.g. area bellow ~4px) we assume there is no matching NE lake - Geometry geom = element.source().worldGeometry(); - if (geom.getArea() < OSM_ID_MATCH_AREA_LIMIT) { - return; - } - // match by intersection: var items = neLakeIndex.getIntersecting(geom); for (var lakeInfo : items) { From 63ac1918dd8a98ff38e0aa70a6a200f85c17c7cd Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Thu, 18 Apr 2024 15:16:20 +0200 Subject: [PATCH 171/211] minor speed-up: compare intersection area instead of intersction area ratio --- src/main/java/org/openmaptiles/layers/Water.java | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/openmaptiles/layers/Water.java b/src/main/java/org/openmaptiles/layers/Water.java index 3df69040..59e44598 100644 --- a/src/main/java/org/openmaptiles/layers/Water.java +++ b/src/main/java/org/openmaptiles/layers/Water.java @@ -240,11 +240,13 @@ void fillOsmIdIntoNeLake(Tables.OsmWaterPolygon element, Geometry geom, LakeInfo } } - // should match following in OpenMapTiles: Distinct on keeps just the first occurence -> order by 'area_ratio DESC' - double areaRatio = intersection.getArea() / neGeom.getArea(); - if (areaRatio > lakeInfo.areaRatio) { + // Should match following in OpenMapTiles: Distinct on keeps just the first occurence -> order by 'area_ratio DESC' + // With a twist: NE geometry is always the same, hence we can make it a little bit faster by dropping "ratio" + // and compare only the intersection area: bigger area -> bigger ratio. + double area = intersection.getArea(); + if (area > lakeInfo.area) { lakeInfo.osmId = element.source().id(); - lakeInfo.areaRatio = areaRatio; + lakeInfo.area = area; } } @@ -285,7 +287,7 @@ private static class LakeInfo { String clazz; Geometry geom; Long osmId; - double areaRatio; + double area; public LakeInfo(int minZoom, int maxZoom, String clazz) { this.name = null; @@ -293,7 +295,7 @@ public LakeInfo(int minZoom, int maxZoom, String clazz) { this.maxZoom = maxZoom; this.clazz = clazz; this.osmId = null; - this.areaRatio = 0; + this.area = 0; } } } From 6b75450425b6de2a035e7fe61424008f51bcb312 Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Thu, 18 Apr 2024 15:18:24 +0200 Subject: [PATCH 172/211] fixed minor typo --- 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 3acf5f40..cb66c274 100644 --- a/src/main/java/org/openmaptiles/layers/WaterName.java +++ b/src/main/java/org/openmaptiles/layers/WaterName.java @@ -232,7 +232,7 @@ public void process(Tables.OsmWaterPolygon element, FeatureCollector features) { minzoomCL = MINZOOM_LAKE; } if (centerlineGeometry != null) { - // prefer lake centerline if it exists, but point will be also used if minzoom bellow 9 is calculated from area + // prefer lake centerline if it exists, but point will be also used if minzoom below 9 is calculated from area // note: Here we're diverging from OpenMapTiles: For bays with minzoom (based on area) point is used between // minzoom and Z8 and for Z9+ centerline is used, while OpenMaptiles sticks with points. setupOsmWaterPolygonFeature( From 94e868bbb09d634af4573943d826a3da0b419640 Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Thu, 18 Apr 2024 15:31:31 +0200 Subject: [PATCH 173/211] minor clean-up --- src/main/java/org/openmaptiles/layers/WaterName.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/java/org/openmaptiles/layers/WaterName.java b/src/main/java/org/openmaptiles/layers/WaterName.java index cb66c274..0701dbea 100644 --- a/src/main/java/org/openmaptiles/layers/WaterName.java +++ b/src/main/java/org/openmaptiles/layers/WaterName.java @@ -241,8 +241,7 @@ public void process(Tables.OsmWaterPolygon element, FeatureCollector features) { } // Show a label if a water feature covers at least 1/4 of a tile or z14+ - Geometry geometry = element.source().worldGeometry(); - double area = geometry.getArea(); + final double area = element.source().worldGeometry().getArea(); int minzoom = (int) Math.floor(-1d - Math.log(Math.sqrt(area)) / LOG2); if (place != null && SEA_OR_OCEAN_PLACE.contains(place)) { minzoom = Math.clamp(minzoom, MINZOOM_SEA_AND_OCEAN, 14); From 87b2bdd849e1fc8c10e4b092a0d39abfd62914b7 Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Fri, 19 Apr 2024 14:09:42 +0200 Subject: [PATCH 174/211] minor clean-up after previous changes --- src/main/java/org/openmaptiles/layers/Water.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/openmaptiles/layers/Water.java b/src/main/java/org/openmaptiles/layers/Water.java index 59e44598..df867c7c 100644 --- a/src/main/java/org/openmaptiles/layers/Water.java +++ b/src/main/java/org/openmaptiles/layers/Water.java @@ -195,7 +195,7 @@ void fillOsmIdIntoNeLake(Tables.OsmWaterPolygon element) { var lakeInfo = map.get(element.name()); if (lakeInfo != null) { match = true; - fillOsmIdIntoNeLake(element, element.source().worldGeometry(), lakeInfo, true); + fillOsmIdIntoNeLake(element, geom, lakeInfo, true); } } } From 80bd1acfd13733da54cece6b6d67d06db0a36f0e Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Fri, 19 Apr 2024 15:17:18 +0200 Subject: [PATCH 175/211] adjusted handling of TopologyException to match previous tweaks --- .../java/org/openmaptiles/layers/Water.java | 90 +++++++++---------- 1 file changed, 43 insertions(+), 47 deletions(-) diff --git a/src/main/java/org/openmaptiles/layers/Water.java b/src/main/java/org/openmaptiles/layers/Water.java index df867c7c..27f52ad6 100644 --- a/src/main/java/org/openmaptiles/layers/Water.java +++ b/src/main/java/org/openmaptiles/layers/Water.java @@ -176,41 +176,51 @@ public void process(Tables.OsmWaterPolygon element, FeatureCollector features) { .setAttrWithMinzoom(Fields.BRUNNEL, Utils.brunnel(element.isBridge(), element.isTunnel()), 12) .setAttr(Fields.CLASS, clazz); - fillOsmIdIntoNeLake(element); + try { + var geom = element.source().worldGeometry(); + try { + attemptNeLakeIdMapping(element, geom); + } catch (TopologyException e) { + try { + var fixedGeom = GeometryFixer.fix(geom); + attemptNeLakeIdMapping(element, fixedGeom); + } catch (TopologyException e2) { + throw new GeometryException("fix_omt_water_topology_error", + "error fixing polygon: " + e2 + "; original error: " + e); + } + } + } catch (GeometryException e) { + e.log(stats, "omt_water", + "Error getting geometry for OSM feature " + element.source().id()); + } } } - void fillOsmIdIntoNeLake(Tables.OsmWaterPolygon element) { - try { - // if OSM lake is too small for Z6 (e.g. area bellow ~4px) we assume there is no matching NE lake - Geometry geom = element.source().worldGeometry(); - if (geom.getArea() < OSM_ID_MATCH_AREA_LIMIT) { - return; - } + void attemptNeLakeIdMapping(Tables.OsmWaterPolygon element, Geometry geom) throws GeometryException { + // if OSM lake is too small for Z6 (e.g. area bellow ~4px) we assume there is no matching NE lake + if (geom.getArea() < OSM_ID_MATCH_AREA_LIMIT) { + return; + } - // match by name: - boolean match = false; - if (element.name() != null) { - for (var map : neLakeNameMaps.values()) { - var lakeInfo = map.get(element.name()); - if (lakeInfo != null) { - match = true; - fillOsmIdIntoNeLake(element, geom, lakeInfo, true); - } + // match by name: + boolean match = false; + if (element.name() != null) { + for (var map : neLakeNameMaps.values()) { + var lakeInfo = map.get(element.name()); + if (lakeInfo != null) { + match = true; + fillOsmIdIntoNeLake(element, geom, lakeInfo, true); } } - if (match) { - return; - } + } + if (match) { + return; + } - // match by intersection: - var items = neLakeIndex.getIntersecting(geom); - for (var lakeInfo : items) { - fillOsmIdIntoNeLake(element, geom, lakeInfo, false); - } - } catch (GeometryException e) { - e.log(stats, "omt_water", - "Error getting geometry for OSM feature " + element.source().id()); + // match by intersection: + var items = neLakeIndex.getIntersecting(geom); + for (var lakeInfo : items) { + fillOsmIdIntoNeLake(element, geom, lakeInfo, false); } } @@ -219,26 +229,12 @@ void fillOsmIdIntoNeLake(Tables.OsmWaterPolygon element) { * otherwise `true`, to make sure we DO check the intersection but to avoid checking it twice. */ void fillOsmIdIntoNeLake(Tables.OsmWaterPolygon element, Geometry geom, LakeInfo lakeInfo, - boolean intersetsCheckNeeded) throws GeometryException { - Geometry neGeom = lakeInfo.geom; - Geometry intersection; - try { - if (intersetsCheckNeeded && !neGeom.intersects(geom)) { - return; - } - intersection = neGeom.intersection(geom); - } catch (TopologyException e) { - try { - Geometry fixedGeom = GeometryFixer.fix(geom); - if (intersetsCheckNeeded && !neGeom.intersects(fixedGeom)) { - return; - } - intersection = neGeom.intersection(fixedGeom); - } catch (TopologyException e2) { - throw new GeometryException("fix_omt_water_topology_error", - "error fixing polygon: " + e2 + "; original error: " + e); - } + boolean intersetsCheckNeeded) { + final Geometry neGeom = lakeInfo.geom; + if (intersetsCheckNeeded && !neGeom.intersects(geom)) { + return; } + var intersection = neGeom.intersection(geom); // Should match following in OpenMapTiles: Distinct on keeps just the first occurence -> order by 'area_ratio DESC' // With a twist: NE geometry is always the same, hence we can make it a little bit faster by dropping "ratio" From fc62c13c33369ef167f63ef1cf7d84be0e81906f Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Fri, 19 Apr 2024 15:22:58 +0200 Subject: [PATCH 176/211] mvn spotless:apply --- src/main/java/org/openmaptiles/layers/Water.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/openmaptiles/layers/Water.java b/src/main/java/org/openmaptiles/layers/Water.java index 27f52ad6..d3fc8bf8 100644 --- a/src/main/java/org/openmaptiles/layers/Water.java +++ b/src/main/java/org/openmaptiles/layers/Water.java @@ -186,12 +186,12 @@ public void process(Tables.OsmWaterPolygon element, FeatureCollector features) { attemptNeLakeIdMapping(element, fixedGeom); } catch (TopologyException e2) { throw new GeometryException("fix_omt_water_topology_error", - "error fixing polygon: " + e2 + "; original error: " + e); + "error fixing polygon: " + e2 + "; original error: " + e); } } } catch (GeometryException e) { e.log(stats, "omt_water", - "Error getting geometry for OSM feature " + element.source().id()); + "Error getting geometry for OSM feature " + element.source().id()); } } } From 5ec171aae4dd2f273c6a71c53fec4affb88c62fb Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Fri, 19 Apr 2024 15:27:21 +0200 Subject: [PATCH 177/211] tweaked error message, to better mach cases when TopologyException occurs --- src/main/java/org/openmaptiles/layers/Water.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/openmaptiles/layers/Water.java b/src/main/java/org/openmaptiles/layers/Water.java index d3fc8bf8..700d101f 100644 --- a/src/main/java/org/openmaptiles/layers/Water.java +++ b/src/main/java/org/openmaptiles/layers/Water.java @@ -191,7 +191,7 @@ public void process(Tables.OsmWaterPolygon element, FeatureCollector features) { } } catch (GeometryException e) { e.log(stats, "omt_water", - "Error getting geometry for OSM feature " + element.source().id()); + "Unable to process intersections for OSM feature " + element.source().id()); } } } From ec1dc8ea92e64a7af046f68f5d46c17532892019 Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Wed, 24 Apr 2024 18:37:15 +0200 Subject: [PATCH 178/211] adjusted trunks: Z6+ by default, some from Z5, some even from Z4 --- .../openmaptiles/layers/Transportation.java | 48 +++++++++++++++---- 1 file changed, 40 insertions(+), 8 deletions(-) diff --git a/src/main/java/org/openmaptiles/layers/Transportation.java b/src/main/java/org/openmaptiles/layers/Transportation.java index 464c96dc..ee1fa536 100644 --- a/src/main/java/org/openmaptiles/layers/Transportation.java +++ b/src/main/java/org/openmaptiles/layers/Transportation.java @@ -139,13 +139,23 @@ 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( + // ... and also Z4_MOTORWAY_NY_NETWORK, except those in Z5_MOTORWAYS_BY_NETWORK: + private static final Set Z5_TRUNK_BY_NETWORK = Set.of( RouteNetwork.CA_TRANSCANADA, RouteNetwork.CA_PROVINCIAL_ARTERIAL, RouteNetwork.US_INTERSTATE, + RouteNetwork.US_HIGHWAY, + RouteNetwork.GB_MOTORWAY, + RouteNetwork.GB_TRUNK, + RouteNetwork.IE_MOTORWAY, + RouteNetwork.IE_NATIONAL, RouteNetwork.E_ROAD, RouteNetwork.A_ROAD ); + private static final Set Z5_MOTORWAYS_BY_NETWORK = Set.of( + RouteNetwork.GB_TRUNK, + RouteNetwork.US_HIGHWAY + ); private static final Set CA_AB_PRIMARY_AS_ARTERIAL_BY_REF = Set.of( "2", "3", "4" ); @@ -194,7 +204,7 @@ public Transportation(Translations translations, PlanetilerConfig config, Stats entry(FieldValues.CLASS_BUS_GUIDEWAY, 11), entry(FieldValues.CLASS_SECONDARY, 9), entry(FieldValues.CLASS_PRIMARY, 7), - entry(FieldValues.CLASS_TRUNK, 5), + entry(FieldValues.CLASS_TRUNK, 6), entry(FieldValues.CLASS_MOTORWAY, 4) ); } @@ -245,6 +255,26 @@ private static boolean isResidentialOrUnclassified(String highway) { return "residential".equals(highway) || "unclassified".equals(highway); } + private static boolean isTrunkForZ5(String highway, List routeRelations) { + // Allow trunk roads that are part of a nation's most important route network to show at z5 + if (!"trunk".equals(highway)) { + return false; + } + return routeRelations.stream() + .map(RouteRelation::networkType) + .filter(Objects::nonNull) + .anyMatch(Z5_TRUNK_BY_NETWORK::contains); + } + + private static boolean isMotorwayWithNetworkForZ4(List routeRelations) { + // All roads in network included in osm_national_network except gb-trunk and us-highway + return routeRelations.stream() + .map(RouteRelation::networkType) + .filter(Objects::nonNull) + .filter(nt -> !Z5_MOTORWAYS_BY_NETWORK.contains(nt)) + .anyMatch(Z5_TRUNK_BY_NETWORK::contains); + } + private static boolean isDrivewayOrParkingAisle(String service) { return FieldValues.SERVICE_PARKING_AISLE.equals(service) || FieldValues.SERVICE_DRIVEWAY.equals(service); } @@ -504,12 +534,14 @@ int getMinzoom(Tables.OsmHighwayLinestring element, String highwayClass) { 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) - .anyMatch(TRUNK_AS_MOTORWAY_BY_NETWORK::contains) ? FieldValues.CLASS_MOTORWAY : FieldValues.CLASS_TRUNK; - yield MINZOOMS.getOrDefault(clazz, Integer.MAX_VALUE); + boolean z5trunk = isTrunkForZ5(highway, routeRelations); + // and if it is good for Z5, it may be good also for Z4 (see CLASS_MOTORWAY bellow): + String clazz = FieldValues.CLASS_TRUNK; + if (z5trunk && isMotorwayWithNetworkForZ4(routeRelations)) { + clazz = FieldValues.CLASS_MOTORWAY; + z5trunk = false; + } + yield (z5trunk) ? 5 : MINZOOMS.getOrDefault(clazz, Integer.MAX_VALUE); } default -> MINZOOMS.getOrDefault(baseClass, Integer.MAX_VALUE); }; From ec0cdc15bf860ffff53155c19048e685243f0db3 Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Wed, 24 Apr 2024 19:09:17 +0200 Subject: [PATCH 179/211] adjusted motorways: only some at Z4 --- .../openmaptiles/layers/Transportation.java | 28 +++++++++++++++---- 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/openmaptiles/layers/Transportation.java b/src/main/java/org/openmaptiles/layers/Transportation.java index ee1fa536..1f2f698d 100644 --- a/src/main/java/org/openmaptiles/layers/Transportation.java +++ b/src/main/java/org/openmaptiles/layers/Transportation.java @@ -153,8 +153,8 @@ public class Transportation implements RouteNetwork.A_ROAD ); private static final Set Z5_MOTORWAYS_BY_NETWORK = Set.of( - RouteNetwork.GB_TRUNK, - RouteNetwork.US_HIGHWAY + RouteNetwork.GB_TRUNK, + RouteNetwork.US_HIGHWAY ); private static final Set CA_AB_PRIMARY_AS_ARTERIAL_BY_REF = Set.of( "2", "3", "4" @@ -269,10 +269,24 @@ private static boolean isTrunkForZ5(String highway, List routeRel private static boolean isMotorwayWithNetworkForZ4(List routeRelations) { // All roads in network included in osm_national_network except gb-trunk and us-highway return routeRelations.stream() - .map(RouteRelation::networkType) - .filter(Objects::nonNull) - .filter(nt -> !Z5_MOTORWAYS_BY_NETWORK.contains(nt)) - .anyMatch(Z5_TRUNK_BY_NETWORK::contains); + .map(RouteRelation::networkType) + .filter(Objects::nonNull) + .filter(nt -> !Z5_MOTORWAYS_BY_NETWORK.contains(nt)) + .anyMatch(Z5_TRUNK_BY_NETWORK::contains); + } + + private static boolean isMotorwayWoNetworkForZ4(List routeRelations) { + // All motorways without network (e.g. EU, Asia, South America) + return routeRelations.stream() + .map(RouteRelation::networkType) + .noneMatch(Objects::nonNull); + } + + private static boolean isMotorwayForZ4(List routeRelations) { + if (isMotorwayWoNetworkForZ4(routeRelations)) { + return true; + } + return isMotorwayWithNetworkForZ4(routeRelations); } private static boolean isDrivewayOrParkingAisle(String service) { @@ -543,6 +557,8 @@ int getMinzoom(Tables.OsmHighwayLinestring element, String highwayClass) { } yield (z5trunk) ? 5 : MINZOOMS.getOrDefault(clazz, Integer.MAX_VALUE); } + case FieldValues.CLASS_MOTORWAY -> isMotorwayForZ4(routeRelations) ? + MINZOOMS.getOrDefault(FieldValues.CLASS_MOTORWAY, Integer.MAX_VALUE) : 5; default -> MINZOOMS.getOrDefault(baseClass, Integer.MAX_VALUE); }; } From bdab24e1c8bdf4167d020991f99c457901e4ef9a Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Wed, 24 Apr 2024 20:18:10 +0200 Subject: [PATCH 180/211] adjusted transportation: clip also construction links to Z9+ --- .../java/org/openmaptiles/layers/Transportation.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/openmaptiles/layers/Transportation.java b/src/main/java/org/openmaptiles/layers/Transportation.java index 1f2f698d..17f17e2a 100644 --- a/src/main/java/org/openmaptiles/layers/Transportation.java +++ b/src/main/java/org/openmaptiles/layers/Transportation.java @@ -261,9 +261,9 @@ private static boolean isTrunkForZ5(String highway, List routeRel return false; } return routeRelations.stream() - .map(RouteRelation::networkType) - .filter(Objects::nonNull) - .anyMatch(Z5_TRUNK_BY_NETWORK::contains); + .map(RouteRelation::networkType) + .filter(Objects::nonNull) + .anyMatch(Z5_TRUNK_BY_NETWORK::contains); } private static boolean isMotorwayWithNetworkForZ4(List routeRelations) { @@ -535,6 +535,7 @@ int getMinzoom(Tables.OsmHighwayLinestring element, String highwayClass) { } } String highway = element.highway(); + String construction = element.construction(); int minzoom; if ("pier".equals(element.manMade())) { @@ -563,7 +564,7 @@ int getMinzoom(Tables.OsmHighwayLinestring element, String highwayClass) { }; } - if (isLink(highway)) { + if (isLink(highway) || isLink(construction)) { minzoom = Math.max(minzoom, 9); } return minzoom; From 29deac34de2f15cfd94849669fb615922bfefd2c Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Wed, 24 Apr 2024 21:12:29 +0200 Subject: [PATCH 181/211] unit tests adjusted to the recent changes --- .../openmaptiles/layers/TransportationTest.java | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/test/java/org/openmaptiles/layers/TransportationTest.java b/src/test/java/org/openmaptiles/layers/TransportationTest.java index 8e496444..729b3b05 100644 --- a/src/test/java/org/openmaptiles/layers/TransportationTest.java +++ b/src/test/java/org/openmaptiles/layers/TransportationTest.java @@ -353,7 +353,7 @@ void testDuplicateRoute() { "_layer", "transportation", "class", "trunk", "network", "us-state", - "_minzoom", 5 + "_minzoom", 6 ), Map.of( "_layer", "transportation_name", "class", "trunk", @@ -1174,7 +1174,7 @@ void testTransCanadaProvincialCaOnPrimaryRefOther() { "_layer", "transportation", "class", "trunk", "network", "ca-provincial", - "_minzoom", 5 + "_minzoom", 6 ), Map.of( "_layer", "transportation_name", "class", "trunk", @@ -1228,7 +1228,7 @@ void testTransCanadaProvincialCaMbPthRefOther() { "_layer", "transportation", "class", "trunk", "network", "ca-provincial", - "_minzoom", 5 + "_minzoom", 6 ), Map.of( "_layer", "transportation_name", "class", "trunk", @@ -1282,7 +1282,7 @@ void testTransCanadaProvincialCaAbPrimaryRefOther() { "_layer", "transportation", "class", "trunk", "network", "ca-provincial", - "_minzoom", 5 + "_minzoom", 6 ), Map.of( "_layer", "transportation_name", "class", "trunk", @@ -1336,7 +1336,7 @@ void testTransCanadaProvincialCaBcRefOther() { "_layer", "transportation", "class", "trunk", "network", "ca-provincial", - "_minzoom", 5 + "_minzoom", 6 ), Map.of( "_layer", "transportation_name", "class", "trunk", @@ -1361,7 +1361,7 @@ void testTransCanadaProvincialCaOther() { assertFeatures(13, List.of(Map.of( "_layer", "transportation", "class", "trunk", - "_minzoom", 5 + "_minzoom", 6 )), features); boolean caProvPresent = StreamSupport.stream(features.spliterator(), false) .flatMap(f -> f.getAttrsAtZoom(13).entrySet().stream()) @@ -1647,7 +1647,7 @@ void testIrelandTrunk() { assertFeatures(13, List.of(Map.of( "_layer", "transportation", "class", "trunk", - "_minzoom", 5 + "_minzoom", 4 ), Map.of( "_layer", "transportation_name", "class", "trunk", @@ -2149,7 +2149,7 @@ void testARoad() { assertFeatures(13, List.of(Map.of( "_layer", "transportation", "class", "trunk", - "_minzoom", 5 + "_minzoom", 6 ), Map.of( "_layer", "transportation_name", "class", "trunk", From e23c0858d9c6ccfc8f8b45e89564d82cf3d8fc43 Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Fri, 26 Apr 2024 14:40:23 +0200 Subject: [PATCH 182/211] use merge() to avoid problems with concurrent updates --- src/main/java/org/openmaptiles/layers/Water.java | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/openmaptiles/layers/Water.java b/src/main/java/org/openmaptiles/layers/Water.java index 700d101f..a95de36b 100644 --- a/src/main/java/org/openmaptiles/layers/Water.java +++ b/src/main/java/org/openmaptiles/layers/Water.java @@ -132,11 +132,8 @@ record WaterInfo(int minZoom, int maxZoom, String clazz) {} } neLakeIndex.put(geom, lakeInfo); if (lakeInfo.name != null) { - if (!neLakeNameMap.containsKey(lakeInfo.name) || - lakeInfo.geom.getArea() > neLakeNameMap.get(lakeInfo.name).geom.getArea()) { - // on name collision, bigger lake gets on the name list - neLakeNameMap.put(lakeInfo.name, lakeInfo); - } + // on name collision, bigger lake gets on the name list + neLakeNameMap.merge(lakeInfo.name, lakeInfo, (prev, next) -> next.geom.getArea() > prev.geom.getArea() ? next : prev); } } catch (GeometryException e) { e.log(stats, "omt_water_ne", From d58cbdb319e132fb07e2f4f4f2256f4f86bd4942 Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Fri, 26 Apr 2024 14:50:36 +0200 Subject: [PATCH 183/211] synchronized mergeId() added to avoid problems with concurrent updates --- src/main/java/org/openmaptiles/layers/Water.java | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/openmaptiles/layers/Water.java b/src/main/java/org/openmaptiles/layers/Water.java index a95de36b..01a8a214 100644 --- a/src/main/java/org/openmaptiles/layers/Water.java +++ b/src/main/java/org/openmaptiles/layers/Water.java @@ -237,10 +237,7 @@ void fillOsmIdIntoNeLake(Tables.OsmWaterPolygon element, Geometry geom, LakeInfo // With a twist: NE geometry is always the same, hence we can make it a little bit faster by dropping "ratio" // and compare only the intersection area: bigger area -> bigger ratio. double area = intersection.getArea(); - if (area > lakeInfo.area) { - lakeInfo.osmId = element.source().id(); - lakeInfo.area = area; - } + lakeInfo.mergeId(element.source().id(), area); } @Override @@ -290,5 +287,12 @@ public LakeInfo(int minZoom, int maxZoom, String clazz) { this.osmId = null; this.area = 0; } + + public synchronized void mergeId(Long newId, double newArea) { + if (newArea > area) { + osmId = newId; + area = newArea; + } + } } } From dd1c58f2e5ea4f48881df6649bb43294d57ee0d9 Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Fri, 26 Apr 2024 14:54:10 +0200 Subject: [PATCH 184/211] clean-up after cc797f9a --- src/main/java/org/openmaptiles/layers/Water.java | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/main/java/org/openmaptiles/layers/Water.java b/src/main/java/org/openmaptiles/layers/Water.java index 01a8a214..e1b00e7f 100644 --- a/src/main/java/org/openmaptiles/layers/Water.java +++ b/src/main/java/org/openmaptiles/layers/Water.java @@ -243,10 +243,7 @@ void fillOsmIdIntoNeLake(Tables.OsmWaterPolygon element, Geometry geom, LakeInfo @Override public void finish(String sourceName, FeatureCollector.Factory featureCollectors, Consumer emit) { - if (OpenMapTilesProfile.NATURAL_EARTH_SOURCE.equals(sourceName)) { - var timer = stats.startStage("ne_lake_index"); - timer.stop(); - } else if (OpenMapTilesProfile.OSM_SOURCE.equals(sourceName)) { + if (OpenMapTilesProfile.OSM_SOURCE.equals(sourceName)) { var timer = stats.startStage("ne_lakes"); for (var item : neAllLakeInfos) { var features = featureCollectors.get(SimpleFeature.fromWorldGeometry(item.geom)); From c39e8624684820c2181ddab315483b39adea16cd Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Fri, 26 Apr 2024 15:05:52 +0200 Subject: [PATCH 185/211] mvn spotless:apply --- src/main/java/org/openmaptiles/layers/Water.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/openmaptiles/layers/Water.java b/src/main/java/org/openmaptiles/layers/Water.java index e1b00e7f..f4008d80 100644 --- a/src/main/java/org/openmaptiles/layers/Water.java +++ b/src/main/java/org/openmaptiles/layers/Water.java @@ -133,7 +133,8 @@ record WaterInfo(int minZoom, int maxZoom, String clazz) {} neLakeIndex.put(geom, lakeInfo); if (lakeInfo.name != null) { // on name collision, bigger lake gets on the name list - neLakeNameMap.merge(lakeInfo.name, lakeInfo, (prev, next) -> next.geom.getArea() > prev.geom.getArea() ? next : prev); + neLakeNameMap.merge(lakeInfo.name, lakeInfo, + (prev, next) -> next.geom.getArea() > prev.geom.getArea() ? next : prev); } } catch (GeometryException e) { e.log(stats, "omt_water_ne", From 5893ac3b6c97723cbf82fa8ccad3c11e5b2afb2b Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Fri, 26 Apr 2024 15:23:50 +0200 Subject: [PATCH 186/211] label point in water_name refactored using setMinPixelSizeBelowZoom() --- .../org/openmaptiles/layers/WaterName.java | 69 ++++++++----------- .../openmaptiles/layers/WaterNameTest.java | 9 +++ 2 files changed, 38 insertions(+), 40 deletions(-) diff --git a/src/main/java/org/openmaptiles/layers/WaterName.java b/src/main/java/org/openmaptiles/layers/WaterName.java index 0701dbea..1caee175 100644 --- a/src/main/java/org/openmaptiles/layers/WaterName.java +++ b/src/main/java/org/openmaptiles/layers/WaterName.java @@ -218,47 +218,36 @@ 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()); - int minzoomCL = MINZOOM_BAY; - 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; - minzoomCL = MINZOOM_LAKE; - } - if (centerlineGeometry != null) { - // prefer lake centerline if it exists, but point will be also used if minzoom below 9 is calculated from area - // note: Here we're diverging from OpenMapTiles: For bays with minzoom (based on area) point is used between - // minzoom and Z8 and for Z9+ centerline is used, while OpenMaptiles sticks with points. - setupOsmWaterPolygonFeature( - element, features.geometry(LAYER_NAME, centerlineGeometry), clazz, minzoomCL) - .setMinPixelSizeBelowZoom(13, 6d * element.name().length()); - } - - // Show a label if a water feature covers at least 1/4 of a tile or z14+ - final double area = element.source().worldGeometry().getArea(); - int minzoom = (int) Math.floor(-1d - Math.log(Math.sqrt(area)) / LOG2); - if (place != null && SEA_OR_OCEAN_PLACE.contains(place)) { - minzoom = Math.clamp(minzoom, MINZOOM_SEA_AND_OCEAN, 14); - } else { - minzoom = Math.clamp(minzoom, MINZOOM_LAKE, 14); - } + Geometry centerlineGeometry = lakeCenterlines.get(element.source().id()); + int minzoomCL = MINZOOM_BAY; + 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; + minzoomCL = MINZOOM_LAKE; + } + if (centerlineGeometry != null) { + // prefer lake centerline if it exists, but point will be also used if minzoom below 9 is calculated from area + // note: Here we're diverging from OpenMapTiles: For bays with minzoom (based on area) point is used between + // minzoom and Z8 and for Z9+ centerline is used, while OpenMaptiles sticks with points. + setupOsmWaterPolygonFeature( + element, features.geometry(LAYER_NAME, centerlineGeometry), clazz, minzoomCL) + .setMinPixelSizeBelowZoom(13, 6d * element.name().length()); + } - if (centerlineGeometry == null || minzoom < minzoomCL) { - // otherwise just use a label point inside the lake - var feature = setupOsmWaterPolygonFeature(element, features.pointOnSurface(LAYER_NAME), clazz, minzoom); - if (centerlineGeometry != null) { - // centerline already created, so make sure we're not having both at same zoom level - feature.setMaxZoom(minzoomCL - 1); - } - } - } catch (GeometryException e) { - e.log(stats, "omt_water_polygon", "Unable to get geometry for water polygon " + element.source().id()); + int minzoom = place != null && SEA_OR_OCEAN_PLACE.contains(place) ? MINZOOM_SEA_AND_OCEAN : MINZOOM_LAKE; + if (centerlineGeometry == null || minzoom < minzoomCL) { + // use a label point inside the lake but ... + // ... if centerline already created, adjust maxzoom here to make sure we're not having both at same zoom level + int maxzoom = centerlineGeometry != null ? minzoomCL - 1 : 14; + setupOsmWaterPolygonFeature(element, features.pointOnSurface(LAYER_NAME), clazz, minzoom) + .setMaxZoom(maxzoom) + // Show a label if a water feature covers at least 1/4 of a tile or z14+ + .setMinPixelSizeBelowZoom(13, 128); } } } diff --git a/src/test/java/org/openmaptiles/layers/WaterNameTest.java b/src/test/java/org/openmaptiles/layers/WaterNameTest.java index 1d70e1cc..6ee686b6 100644 --- a/src/test/java/org/openmaptiles/layers/WaterNameTest.java +++ b/src/test/java/org/openmaptiles/layers/WaterNameTest.java @@ -146,6 +146,15 @@ void testWaterNameBaySmall() { "_minzoom", 9, "_maxzoom", 14, "_minpixelsize", "bay".length() * 6d + ), Map.of( + "name", "bay", + "name:es", "bay es", + + "_layer", "water_name", + "_type", "point", + "_minzoom", 3, + "_maxzoom", 8, + "_minpixelsize", 128d )), process(SimpleFeature.create( GeoUtils.worldToLatLonCoords(rectangle(0, Math.sqrt(1E-7))), new HashMap<>(Map.of( From 40217b380641ba93fbf7cc9f56c49fea1d10b60b Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Mon, 29 Apr 2024 15:21:30 +0200 Subject: [PATCH 187/211] clean-up: LOG2 removed since no longer used --- src/main/java/org/openmaptiles/layers/WaterName.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/org/openmaptiles/layers/WaterName.java b/src/main/java/org/openmaptiles/layers/WaterName.java index 1caee175..bd48b18d 100644 --- a/src/main/java/org/openmaptiles/layers/WaterName.java +++ b/src/main/java/org/openmaptiles/layers/WaterName.java @@ -82,7 +82,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 static final double IMPORTANT_MARINE_REGIONS_JOIN_DISTANCE = GeoUtils.metersToPixelAtEquator(0, 50_000) / 256d; From 4f1fa237ba8f02c65027c39909c7c046ce1705b9 Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Mon, 29 Apr 2024 15:47:41 +0200 Subject: [PATCH 188/211] refactored handling of TopologyException for easier troubleshooting of problematic polygons --- .../java/org/openmaptiles/layers/Water.java | 49 ++++++++++++------- 1 file changed, 32 insertions(+), 17 deletions(-) diff --git a/src/main/java/org/openmaptiles/layers/Water.java b/src/main/java/org/openmaptiles/layers/Water.java index f4008d80..2afc8427 100644 --- a/src/main/java/org/openmaptiles/layers/Water.java +++ b/src/main/java/org/openmaptiles/layers/Water.java @@ -59,6 +59,8 @@ 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.Utils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * Defines the logic for generating map elements for oceans and lakes in the {@code water} layer from source features. @@ -81,6 +83,7 @@ public class Water implements * which infers ocean polygons by preprocessing all coastline elements. */ + private static final Logger LOGGER = LoggerFactory.getLogger(Water.class); // smallest NE lake is around 4.42E-13, smallest matching OSM lake is 9.34E-13, this is slightly bellow that // and approx. 33% of OSM features are smaller than this, hence to save some CPU cycles: private static final double OSM_ID_MATCH_AREA_LIMIT = Math.pow(4, -20); @@ -123,6 +126,7 @@ record WaterInfo(int minZoom, int maxZoom, String clazz) {} var geom = feature.worldGeometry(); lakeInfo.geom = geom; lakeInfo.name = feature.getString("name"); + lakeInfo.neId = feature.getLong("ne_id"); var neLakeNameMap = neLakeNameMaps.computeIfAbsent(table, t -> new ConcurrentHashMap<>()); @@ -175,27 +179,17 @@ public void process(Tables.OsmWaterPolygon element, FeatureCollector features) { .setAttr(Fields.CLASS, clazz); try { - var geom = element.source().worldGeometry(); - try { - attemptNeLakeIdMapping(element, geom); - } catch (TopologyException e) { - try { - var fixedGeom = GeometryFixer.fix(geom); - attemptNeLakeIdMapping(element, fixedGeom); - } catch (TopologyException e2) { - throw new GeometryException("fix_omt_water_topology_error", - "error fixing polygon: " + e2 + "; original error: " + e); - } - } + attemptNeLakeIdMapping(element); } catch (GeometryException e) { e.log(stats, "omt_water", - "Unable to process intersections for OSM feature " + element.source().id()); + "Unable to process intersections for OSM feature " + element.source().id(), config.logJtsExceptions()); } } } - void attemptNeLakeIdMapping(Tables.OsmWaterPolygon element, Geometry geom) throws GeometryException { + void attemptNeLakeIdMapping(Tables.OsmWaterPolygon element) throws GeometryException { // if OSM lake is too small for Z6 (e.g. area bellow ~4px) we assume there is no matching NE lake + var geom = element.source().worldGeometry(); if (geom.getArea() < OSM_ID_MATCH_AREA_LIMIT) { return; } @@ -216,7 +210,12 @@ void attemptNeLakeIdMapping(Tables.OsmWaterPolygon element, Geometry geom) throw } // match by intersection: - var items = neLakeIndex.getIntersecting(geom); + List items; + try { + items = neLakeIndex.getIntersecting(geom); + } catch (TopologyException e) { + throw new GeometryException("omt_water_intersecting", "Error getting intersecting NE lakes from the index", e); + } for (var lakeInfo : items) { fillOsmIdIntoNeLake(element, geom, lakeInfo, false); } @@ -227,12 +226,26 @@ void attemptNeLakeIdMapping(Tables.OsmWaterPolygon element, Geometry geom) throw * otherwise `true`, to make sure we DO check the intersection but to avoid checking it twice. */ void fillOsmIdIntoNeLake(Tables.OsmWaterPolygon element, Geometry geom, LakeInfo lakeInfo, - boolean intersetsCheckNeeded) { + boolean intersetsCheckNeeded) throws GeometryException { final Geometry neGeom = lakeInfo.geom; if (intersetsCheckNeeded && !neGeom.intersects(geom)) { return; } - var intersection = neGeom.intersection(geom); + Geometry intersection; + try { + intersection = neGeom.intersection(geom); + } catch (TopologyException e) { + stats.dataError("omt_water_intersection"); + LOGGER.warn("omt_water_intersection, NE ID: {}, OSM ID: {}", + lakeInfo.neId, element.source().id(), e); + final var geomFixed = GeometryFixer.fix(geom); + final var neGeomfixed = GeometryFixer.fix(neGeom); + try { + intersection = neGeomfixed.intersection(geomFixed); + } catch (TopologyException e2) { + throw new GeometryException("omt_water_intersection_fix", "Error getting intersection", e); + } + } // Should match following in OpenMapTiles: Distinct on keeps just the first occurence -> order by 'area_ratio DESC' // With a twist: NE geometry is always the same, hence we can make it a little bit faster by dropping "ratio" @@ -275,6 +288,7 @@ private static class LakeInfo { String clazz; Geometry geom; Long osmId; + long neId; double area; public LakeInfo(int minZoom, int maxZoom, String clazz) { @@ -283,6 +297,7 @@ public LakeInfo(int minZoom, int maxZoom, String clazz) { this.maxZoom = maxZoom; this.clazz = clazz; this.osmId = null; + this.neId = -1; this.area = 0; } From 0b5cda0df3593d6d44e4bb6b35fb3cdfbb5bc333 Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Mon, 29 Apr 2024 16:01:06 +0200 Subject: [PATCH 189/211] fix invalid NE lake geometries + further TopologyException handling tweaks --- src/main/java/org/openmaptiles/layers/Water.java | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/openmaptiles/layers/Water.java b/src/main/java/org/openmaptiles/layers/Water.java index 2afc8427..88093b29 100644 --- a/src/main/java/org/openmaptiles/layers/Water.java +++ b/src/main/java/org/openmaptiles/layers/Water.java @@ -124,7 +124,12 @@ record WaterInfo(int minZoom, int maxZoom, String clazz) {} if (lakeInfo != null) { try { var geom = feature.worldGeometry(); - lakeInfo.geom = geom; + if (geom.isValid()) { + lakeInfo.geom = geom; + } else { + LOGGER.warn("fixing geometry of NE lake {}", feature.getLong("ne_id")); + lakeInfo.geom = GeometryFixer.fix(geom); + } lakeInfo.name = feature.getString("name"); lakeInfo.neId = feature.getLong("ne_id"); @@ -182,7 +187,7 @@ public void process(Tables.OsmWaterPolygon element, FeatureCollector features) { attemptNeLakeIdMapping(element); } catch (GeometryException e) { e.log(stats, "omt_water", - "Unable to process intersections for OSM feature " + element.source().id(), config.logJtsExceptions()); + "Unable to process intersections", config.logJtsExceptions()); } } } @@ -214,7 +219,7 @@ void attemptNeLakeIdMapping(Tables.OsmWaterPolygon element) throws GeometryExcep try { items = neLakeIndex.getIntersecting(geom); } catch (TopologyException e) { - throw new GeometryException("omt_water_intersecting", "Error getting intersecting NE lakes from the index", e); + throw new GeometryException("intersecting", "Error getting intersecting NE lakes from the index", e); } for (var lakeInfo : items) { fillOsmIdIntoNeLake(element, geom, lakeInfo, false); @@ -239,9 +244,8 @@ void fillOsmIdIntoNeLake(Tables.OsmWaterPolygon element, Geometry geom, LakeInfo LOGGER.warn("omt_water_intersection, NE ID: {}, OSM ID: {}", lakeInfo.neId, element.source().id(), e); final var geomFixed = GeometryFixer.fix(geom); - final var neGeomfixed = GeometryFixer.fix(neGeom); try { - intersection = neGeomfixed.intersection(geomFixed); + intersection = neGeom.intersection(geomFixed); } catch (TopologyException e2) { throw new GeometryException("omt_water_intersection_fix", "Error getting intersection", e); } From d4fe59c0e5311e5475a057b9feb7c322bff56027 Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Mon, 29 Apr 2024 16:05:40 +0200 Subject: [PATCH 190/211] TopologyException handling removed, since no longer needed after adding NE geometry fixing --- .../java/org/openmaptiles/layers/Water.java | 27 +++---------------- 1 file changed, 3 insertions(+), 24 deletions(-) diff --git a/src/main/java/org/openmaptiles/layers/Water.java b/src/main/java/org/openmaptiles/layers/Water.java index 88093b29..d10b8b5a 100644 --- a/src/main/java/org/openmaptiles/layers/Water.java +++ b/src/main/java/org/openmaptiles/layers/Water.java @@ -53,7 +53,6 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE import java.util.concurrent.ConcurrentHashMap; import java.util.function.Consumer; import org.locationtech.jts.geom.Geometry; -import org.locationtech.jts.geom.TopologyException; import org.locationtech.jts.geom.util.GeometryFixer; import org.openmaptiles.OpenMapTilesProfile; import org.openmaptiles.generated.OpenMapTilesSchema; @@ -131,7 +130,6 @@ record WaterInfo(int minZoom, int maxZoom, String clazz) {} lakeInfo.geom = GeometryFixer.fix(geom); } lakeInfo.name = feature.getString("name"); - lakeInfo.neId = feature.getLong("ne_id"); var neLakeNameMap = neLakeNameMaps.computeIfAbsent(table, t -> new ConcurrentHashMap<>()); @@ -216,11 +214,7 @@ void attemptNeLakeIdMapping(Tables.OsmWaterPolygon element) throws GeometryExcep // match by intersection: List items; - try { - items = neLakeIndex.getIntersecting(geom); - } catch (TopologyException e) { - throw new GeometryException("intersecting", "Error getting intersecting NE lakes from the index", e); - } + items = neLakeIndex.getIntersecting(geom); for (var lakeInfo : items) { fillOsmIdIntoNeLake(element, geom, lakeInfo, false); } @@ -231,25 +225,12 @@ void attemptNeLakeIdMapping(Tables.OsmWaterPolygon element) throws GeometryExcep * otherwise `true`, to make sure we DO check the intersection but to avoid checking it twice. */ void fillOsmIdIntoNeLake(Tables.OsmWaterPolygon element, Geometry geom, LakeInfo lakeInfo, - boolean intersetsCheckNeeded) throws GeometryException { + boolean intersetsCheckNeeded) { final Geometry neGeom = lakeInfo.geom; if (intersetsCheckNeeded && !neGeom.intersects(geom)) { return; } - Geometry intersection; - try { - intersection = neGeom.intersection(geom); - } catch (TopologyException e) { - stats.dataError("omt_water_intersection"); - LOGGER.warn("omt_water_intersection, NE ID: {}, OSM ID: {}", - lakeInfo.neId, element.source().id(), e); - final var geomFixed = GeometryFixer.fix(geom); - try { - intersection = neGeom.intersection(geomFixed); - } catch (TopologyException e2) { - throw new GeometryException("omt_water_intersection_fix", "Error getting intersection", e); - } - } + Geometry intersection = neGeom.intersection(geom); // Should match following in OpenMapTiles: Distinct on keeps just the first occurence -> order by 'area_ratio DESC' // With a twist: NE geometry is always the same, hence we can make it a little bit faster by dropping "ratio" @@ -292,7 +273,6 @@ private static class LakeInfo { String clazz; Geometry geom; Long osmId; - long neId; double area; public LakeInfo(int minZoom, int maxZoom, String clazz) { @@ -301,7 +281,6 @@ public LakeInfo(int minZoom, int maxZoom, String clazz) { this.maxZoom = maxZoom; this.clazz = clazz; this.osmId = null; - this.neId = -1; this.area = 0; } From ee70382150a18acd738c6495da533240d2b18b3a Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Mon, 29 Apr 2024 16:33:37 +0200 Subject: [PATCH 191/211] clean-up after previous changes --- src/main/java/org/openmaptiles/layers/Water.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/openmaptiles/layers/Water.java b/src/main/java/org/openmaptiles/layers/Water.java index d10b8b5a..6ea1afae 100644 --- a/src/main/java/org/openmaptiles/layers/Water.java +++ b/src/main/java/org/openmaptiles/layers/Water.java @@ -213,8 +213,7 @@ void attemptNeLakeIdMapping(Tables.OsmWaterPolygon element) throws GeometryExcep } // match by intersection: - List items; - items = neLakeIndex.getIntersecting(geom); + var items = neLakeIndex.getIntersecting(geom); for (var lakeInfo : items) { fillOsmIdIntoNeLake(element, geom, lakeInfo, false); } @@ -230,7 +229,7 @@ void fillOsmIdIntoNeLake(Tables.OsmWaterPolygon element, Geometry geom, LakeInfo if (intersetsCheckNeeded && !neGeom.intersects(geom)) { return; } - Geometry intersection = neGeom.intersection(geom); + var intersection = neGeom.intersection(geom); // Should match following in OpenMapTiles: Distinct on keeps just the first occurence -> order by 'area_ratio DESC' // With a twist: NE geometry is always the same, hence we can make it a little bit faster by dropping "ratio" From 1a622187c87526585ae7e282c4e4657dc6412aef Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Mon, 29 Apr 2024 17:04:40 +0200 Subject: [PATCH 192/211] OpenMapTiles 3.15 was released so we can use proper tag now --- README.md | 4 ++-- scripts/regenerate-openmaptiles.sh | 3 +-- src/main/java/org/openmaptiles/Generate.java | 2 +- .../java/org/openmaptiles/generated/OpenMapTilesSchema.java | 2 +- 4 files changed, 5 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 24ba6eed..24d43f9d 100644 --- a/README.md +++ b/README.md @@ -150,7 +150,7 @@ script with the OpenMapTiles release tag: ```bash -./scripts/regenerate-openmaptiles.sh v3.14 +./scripts/regenerate-openmaptiles.sh v3.15 ``` Then follow the instructions it prints for reformatting generated code. @@ -158,7 +158,7 @@ Then follow the instructions it prints for reformatting generated code. If you want to regenerate from a different repository than the default openmaptiles, you can specify the url like this: ```bash -./scripts/regenerate-openmaptiles.sh v3.14 https://raw.githubusercontent.com/openmaptiles/openmaptiles/ +./scripts/regenerate-openmaptiles.sh v3.15 https://raw.githubusercontent.com/openmaptiles/openmaptiles/ ``` ## License diff --git a/scripts/regenerate-openmaptiles.sh b/scripts/regenerate-openmaptiles.sh index 2597334f..a598d3b8 100755 --- a/scripts/regenerate-openmaptiles.sh +++ b/scripts/regenerate-openmaptiles.sh @@ -4,8 +4,7 @@ set -o errexit set -o pipefail set -o nounset -# TODO: change to "v3.15" once that is released -TAG="${1:-"master"}" +TAG="${1:-"v3.15"}" echo "tag=${TAG}" BASE_URL="${2:-"https://raw.githubusercontent.com/openmaptiles/openmaptiles/"}" diff --git a/src/main/java/org/openmaptiles/Generate.java b/src/main/java/org/openmaptiles/Generate.java index 31835e3b..69782ffd 100644 --- a/src/main/java/org/openmaptiles/Generate.java +++ b/src/main/java/org/openmaptiles/Generate.java @@ -133,7 +133,7 @@ static JsonNode parseYaml(String string) { public static void main(String[] args) throws IOException { Arguments arguments = Arguments.fromArgsOrConfigFile(args); PlanetilerConfig planetilerConfig = PlanetilerConfig.from(arguments); - String tag = arguments.getString("tag", "openmaptiles tag to use", "v3.14.0"); + String tag = arguments.getString("tag", "openmaptiles tag to use", "v3.15.0"); String baseUrl = arguments.getString("base-url", "the url used to download the openmaptiles.yml", "https://raw.githubusercontent.com/openmaptiles/openmaptiles/"); String base = baseUrl + tag + "/"; diff --git a/src/main/java/org/openmaptiles/generated/OpenMapTilesSchema.java b/src/main/java/org/openmaptiles/generated/OpenMapTilesSchema.java index 676b5a46..84decbd0 100644 --- a/src/main/java/org/openmaptiles/generated/OpenMapTilesSchema.java +++ b/src/main/java/org/openmaptiles/generated/OpenMapTilesSchema.java @@ -56,7 +56,7 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE public class OpenMapTilesSchema { public static final String NAME = "OpenMapTiles"; public static final String DESCRIPTION = "A tileset showcasing all layers in OpenMapTiles. https://openmaptiles.org"; - public static final String VERSION = "3.14.0"; + public static final String VERSION = "3.15.0"; public static final String ATTRIBUTION = "© OpenMapTiles © OpenStreetMap contributors"; public static final List LANGUAGES = List.of("am", "ar", "az", "be", "bg", "bn", "br", "bs", "ca", "co", "cs", From 9c7924938dcf12751dd470d0ee157ae5b6447a69 Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Mon, 29 Apr 2024 17:06:55 +0200 Subject: [PATCH 193/211] regenerate-openmaptiles.sh v3.15 to match official OpenMapTiles release --- .../generated/OpenMapTilesSchema.java | 45 ++++++++++--------- .../org/openmaptiles/generated/Tables.java | 4 +- 2 files changed, 26 insertions(+), 23 deletions(-) diff --git a/src/main/java/org/openmaptiles/generated/OpenMapTilesSchema.java b/src/main/java/org/openmaptiles/generated/OpenMapTilesSchema.java index 84decbd0..a8f2e4d1 100644 --- a/src/main/java/org/openmaptiles/generated/OpenMapTilesSchema.java +++ b/src/main/java/org/openmaptiles/generated/OpenMapTilesSchema.java @@ -1,5 +1,5 @@ /* -Copyright (c) 2021, MapTiler.com & OpenMapTiles contributors. +Copyright (c) 2024, MapTiler.com & OpenMapTiles contributors. All rights reserved. Code license: BSD 3-Clause License @@ -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 - * master. + * OpenMapTiles vector tile schema + * v3.15. */ @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 + * water.yaml */ public interface Water extends Layer { double BUFFER_SIZE = 4.0; @@ -182,7 +182,7 @@ final class FieldMappings { public static final MultiExpression Class = MultiExpression.of(List.of(MultiExpression.entry("dock", matchAny("waterway", "dock")), MultiExpression.entry("river", matchAny("water", "river", "stream", "canal", "ditch", "drain")), - MultiExpression.entry("pond", matchAny("water", "pond", "basin", "wastewater")), + MultiExpression.entry("pond", matchAny("water", "pond", "basin", "wastewater", "salt_pond")), MultiExpression.entry("lake", FALSE), MultiExpression.entry("ocean", FALSE), MultiExpression.entry("swimming_pool", matchAny("leisure", "swimming_pool")))); } @@ -195,7 +195,7 @@ final class FieldMappings { * field applied. Waterways do not have a subclass field. * * Generated from - * waterway.yaml + * waterway.yaml */ public interface Waterway extends Layer { double BUFFER_SIZE = 4.0; @@ -287,7 +287,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/v3.15/layers/landcover/landcover.yaml">landcover.yaml */ public interface Landcover extends Layer { double BUFFER_SIZE = 4.0; @@ -444,7 +444,7 @@ final class FieldMappings { * residential (urban) areas and at higher zoom levels mostly OSM landuse tags. * * Generated from - * landuse.yaml + * landuse.yaml */ public interface Landuse extends Layer { double BUFFER_SIZE = 4.0; @@ -540,7 +540,7 @@ final class FieldMappings { * Natural peaks * * Generated from mountain_peak.yaml + * "https://github.com/openmaptiles/openmaptiles/blob/v3.15/layers/mountain_peak/mountain_peak.yaml">mountain_peak.yaml */ public interface MountainPeak extends Layer { double BUFFER_SIZE = 64.0; @@ -563,7 +563,10 @@ final class Fields { * removed in a future release in favor of name:en. */ public static final String NAME_EN = "name_en"; - /** German name name:de if available, otherwise name or name:en. */ + /** + * German name name:de if available, otherwise name or name:en. This is + * deprecated and will be removed in a future release in favor of name:de. + */ public static final String NAME_DE = "name_de"; /** @@ -621,7 +624,7 @@ final class FieldMappings { * leisure=nature_reserve. * * Generated from - * park.yaml + * park.yaml */ public interface Park extends Layer { double BUFFER_SIZE = 4.0; @@ -684,7 +687,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 + * boundary.yaml */ public interface Boundary extends Layer { double BUFFER_SIZE = 4.0; @@ -794,7 +797,7 @@ final class FieldMappings { * in the aeroway layer. * * Generated from - * aeroway.yaml + * aeroway.yaml */ public interface Aeroway extends Layer { double BUFFER_SIZE = 4.0; @@ -854,7 +857,7 @@ final class FieldMappings { * features like plazas. * * Generated from transportation.yaml + * "https://github.com/openmaptiles/openmaptiles/blob/v3.15/layers/transportation/transportation.yaml">transportation.yaml */ public interface Transportation extends Layer { double BUFFER_SIZE = 4.0; @@ -1199,7 +1202,7 @@ final class FieldMappings { * location:underground are excluded. * * Generated from - * building.yaml + * building.yaml */ public interface Building extends Layer { double BUFFER_SIZE = 4.0; @@ -1241,7 +1244,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/v3.15/layers/water_name/water_name.yaml">water_name.yaml */ public interface WaterName extends Layer { double BUFFER_SIZE = 256.0; @@ -1318,7 +1321,7 @@ final class FieldMappings { * while for other roads you should use name. * * Generated from transportation_name.yaml + * "https://github.com/openmaptiles/openmaptiles/blob/v3.15/layers/transportation_name/transportation_name.yaml">transportation_name.yaml */ public interface TransportationName extends Layer { double BUFFER_SIZE = 8.0; @@ -1590,7 +1593,7 @@ final class FieldMappings { * create a text hierarchy. * * Generated from - * place.yaml + * place.yaml */ public interface Place extends Layer { double BUFFER_SIZE = 256.0; @@ -1714,7 +1717,7 @@ final class FieldMappings { * tag are prioritized for preservation). * * Generated from housenumber.yaml + * "https://github.com/openmaptiles/openmaptiles/blob/v3.15/layers/housenumber/housenumber.yaml">housenumber.yaml */ public interface Housenumber extends Layer { double BUFFER_SIZE = 8.0; @@ -1746,7 +1749,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; @@ -1990,7 +1993,7 @@ final class FieldMappings { * Aerodrome labels * * Generated from aerodrome_label.yaml + * "https://github.com/openmaptiles/openmaptiles/blob/v3.15/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 88fa4ec2..7c87d28a 100644 --- a/src/main/java/org/openmaptiles/generated/Tables.java +++ b/src/main/java/org/openmaptiles/generated/Tables.java @@ -1,5 +1,5 @@ /* -Copyright (c) 2021, MapTiler.com & OpenMapTiles contributors. +Copyright (c) 2024, MapTiler.com & OpenMapTiles contributors. All rights reserved. Code license: BSD 3-Clause License @@ -50,7 +50,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 vector tile + * 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 From c6594d1db59f0150fe9d88c56e4ba3a7a7949b41 Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Mon, 29 Apr 2024 17:17:30 +0200 Subject: [PATCH 194/211] Revert "clean-up after previous changes" This reverts commit ee70382150a18acd738c6495da533240d2b18b3a. --- src/main/java/org/openmaptiles/layers/Water.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/openmaptiles/layers/Water.java b/src/main/java/org/openmaptiles/layers/Water.java index 6ea1afae..d10b8b5a 100644 --- a/src/main/java/org/openmaptiles/layers/Water.java +++ b/src/main/java/org/openmaptiles/layers/Water.java @@ -213,7 +213,8 @@ void attemptNeLakeIdMapping(Tables.OsmWaterPolygon element) throws GeometryExcep } // match by intersection: - var items = neLakeIndex.getIntersecting(geom); + List items; + items = neLakeIndex.getIntersecting(geom); for (var lakeInfo : items) { fillOsmIdIntoNeLake(element, geom, lakeInfo, false); } @@ -229,7 +230,7 @@ void fillOsmIdIntoNeLake(Tables.OsmWaterPolygon element, Geometry geom, LakeInfo if (intersetsCheckNeeded && !neGeom.intersects(geom)) { return; } - var intersection = neGeom.intersection(geom); + Geometry intersection = neGeom.intersection(geom); // Should match following in OpenMapTiles: Distinct on keeps just the first occurence -> order by 'area_ratio DESC' // With a twist: NE geometry is always the same, hence we can make it a little bit faster by dropping "ratio" From 989475c177c9fec18734dba6ebef8e76d372150c Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Mon, 29 Apr 2024 17:17:43 +0200 Subject: [PATCH 195/211] Revert "TopologyException handling removed, since no longer needed after adding NE geometry fixing" This reverts commit d4fe59c0e5311e5475a057b9feb7c322bff56027. --- .../java/org/openmaptiles/layers/Water.java | 27 ++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/openmaptiles/layers/Water.java b/src/main/java/org/openmaptiles/layers/Water.java index d10b8b5a..88093b29 100644 --- a/src/main/java/org/openmaptiles/layers/Water.java +++ b/src/main/java/org/openmaptiles/layers/Water.java @@ -53,6 +53,7 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE import java.util.concurrent.ConcurrentHashMap; import java.util.function.Consumer; import org.locationtech.jts.geom.Geometry; +import org.locationtech.jts.geom.TopologyException; import org.locationtech.jts.geom.util.GeometryFixer; import org.openmaptiles.OpenMapTilesProfile; import org.openmaptiles.generated.OpenMapTilesSchema; @@ -130,6 +131,7 @@ record WaterInfo(int minZoom, int maxZoom, String clazz) {} lakeInfo.geom = GeometryFixer.fix(geom); } lakeInfo.name = feature.getString("name"); + lakeInfo.neId = feature.getLong("ne_id"); var neLakeNameMap = neLakeNameMaps.computeIfAbsent(table, t -> new ConcurrentHashMap<>()); @@ -214,7 +216,11 @@ void attemptNeLakeIdMapping(Tables.OsmWaterPolygon element) throws GeometryExcep // match by intersection: List items; - items = neLakeIndex.getIntersecting(geom); + try { + items = neLakeIndex.getIntersecting(geom); + } catch (TopologyException e) { + throw new GeometryException("intersecting", "Error getting intersecting NE lakes from the index", e); + } for (var lakeInfo : items) { fillOsmIdIntoNeLake(element, geom, lakeInfo, false); } @@ -225,12 +231,25 @@ void attemptNeLakeIdMapping(Tables.OsmWaterPolygon element) throws GeometryExcep * otherwise `true`, to make sure we DO check the intersection but to avoid checking it twice. */ void fillOsmIdIntoNeLake(Tables.OsmWaterPolygon element, Geometry geom, LakeInfo lakeInfo, - boolean intersetsCheckNeeded) { + boolean intersetsCheckNeeded) throws GeometryException { final Geometry neGeom = lakeInfo.geom; if (intersetsCheckNeeded && !neGeom.intersects(geom)) { return; } - Geometry intersection = neGeom.intersection(geom); + Geometry intersection; + try { + intersection = neGeom.intersection(geom); + } catch (TopologyException e) { + stats.dataError("omt_water_intersection"); + LOGGER.warn("omt_water_intersection, NE ID: {}, OSM ID: {}", + lakeInfo.neId, element.source().id(), e); + final var geomFixed = GeometryFixer.fix(geom); + try { + intersection = neGeom.intersection(geomFixed); + } catch (TopologyException e2) { + throw new GeometryException("omt_water_intersection_fix", "Error getting intersection", e); + } + } // Should match following in OpenMapTiles: Distinct on keeps just the first occurence -> order by 'area_ratio DESC' // With a twist: NE geometry is always the same, hence we can make it a little bit faster by dropping "ratio" @@ -273,6 +292,7 @@ private static class LakeInfo { String clazz; Geometry geom; Long osmId; + long neId; double area; public LakeInfo(int minZoom, int maxZoom, String clazz) { @@ -281,6 +301,7 @@ public LakeInfo(int minZoom, int maxZoom, String clazz) { this.maxZoom = maxZoom; this.clazz = clazz; this.osmId = null; + this.neId = -1; this.area = 0; } From 619ce2f1548ab964a9abf40e26ec1034f7e477a2 Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Mon, 29 Apr 2024 17:25:07 +0200 Subject: [PATCH 196/211] minor clean-up --- src/main/java/org/openmaptiles/layers/Water.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/openmaptiles/layers/Water.java b/src/main/java/org/openmaptiles/layers/Water.java index 88093b29..bab3bfec 100644 --- a/src/main/java/org/openmaptiles/layers/Water.java +++ b/src/main/java/org/openmaptiles/layers/Water.java @@ -127,7 +127,7 @@ record WaterInfo(int minZoom, int maxZoom, String clazz) {} if (geom.isValid()) { lakeInfo.geom = geom; } else { - LOGGER.warn("fixing geometry of NE lake {}", feature.getLong("ne_id")); + LOGGER.warn("Fixing geometry of NE lake {}", feature.getLong("ne_id")); lakeInfo.geom = GeometryFixer.fix(geom); } lakeInfo.name = feature.getString("name"); From d0e3dd873a8c1a5a617a281054118154c1034235 Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Mon, 29 Apr 2024 21:13:34 +0200 Subject: [PATCH 197/211] handle TopologyException also from intersects() --- src/main/java/org/openmaptiles/layers/Water.java | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/openmaptiles/layers/Water.java b/src/main/java/org/openmaptiles/layers/Water.java index bab3bfec..2b28084f 100644 --- a/src/main/java/org/openmaptiles/layers/Water.java +++ b/src/main/java/org/openmaptiles/layers/Water.java @@ -233,11 +233,11 @@ void attemptNeLakeIdMapping(Tables.OsmWaterPolygon element) throws GeometryExcep void fillOsmIdIntoNeLake(Tables.OsmWaterPolygon element, Geometry geom, LakeInfo lakeInfo, boolean intersetsCheckNeeded) throws GeometryException { final Geometry neGeom = lakeInfo.geom; - if (intersetsCheckNeeded && !neGeom.intersects(geom)) { - return; - } Geometry intersection; try { + if (intersetsCheckNeeded && !neGeom.intersects(geom)) { + return; + } intersection = neGeom.intersection(geom); } catch (TopologyException e) { stats.dataError("omt_water_intersection"); @@ -245,6 +245,9 @@ void fillOsmIdIntoNeLake(Tables.OsmWaterPolygon element, Geometry geom, LakeInfo lakeInfo.neId, element.source().id(), e); final var geomFixed = GeometryFixer.fix(geom); try { + if (intersetsCheckNeeded && !neGeom.intersects(geomFixed)) { + return; + } intersection = neGeom.intersection(geomFixed); } catch (TopologyException e2) { throw new GeometryException("omt_water_intersection_fix", "Error getting intersection", e); From 01b54a4171d0858cac3ea74db19634c227acb7c3 Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Tue, 30 Apr 2024 19:17:22 +0200 Subject: [PATCH 198/211] further tweaks for TopologyException handling --- src/main/java/org/openmaptiles/layers/Water.java | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/openmaptiles/layers/Water.java b/src/main/java/org/openmaptiles/layers/Water.java index 2b28084f..cc65198e 100644 --- a/src/main/java/org/openmaptiles/layers/Water.java +++ b/src/main/java/org/openmaptiles/layers/Water.java @@ -219,7 +219,16 @@ void attemptNeLakeIdMapping(Tables.OsmWaterPolygon element) throws GeometryExcep try { items = neLakeIndex.getIntersecting(geom); } catch (TopologyException e) { - throw new GeometryException("intersecting", "Error getting intersecting NE lakes from the index", e); + stats.dataError("omt_water_intersecting"); + LOGGER.warn("omt_water_intersecting, OSM ID: {}", + element.source().id(), e); + final var geomFixed = GeometryFixer.fix(geom); + try { + items = neLakeIndex.getIntersecting(geomFixed); + } catch (TopologyException e2) { + throw new GeometryException("omt_water_intersecting_fix", + "Error getting intersecting for OSM ID " + element.source().id(), e2); + } } for (var lakeInfo : items) { fillOsmIdIntoNeLake(element, geom, lakeInfo, false); @@ -250,7 +259,8 @@ void fillOsmIdIntoNeLake(Tables.OsmWaterPolygon element, Geometry geom, LakeInfo } intersection = neGeom.intersection(geomFixed); } catch (TopologyException e2) { - throw new GeometryException("omt_water_intersection_fix", "Error getting intersection", e); + throw new GeometryException("omt_water_intersection_fix", + "Error computing intersection for NE ID " + lakeInfo.neId + " and OSM ID " + element.source().id(), e2); } } From 0cb935071149350a0c58570dca37f07121e4da24 Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Tue, 30 Apr 2024 19:18:18 +0200 Subject: [PATCH 199/211] warnings demoted to debugs since we're going to re-try --- src/main/java/org/openmaptiles/layers/Water.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/openmaptiles/layers/Water.java b/src/main/java/org/openmaptiles/layers/Water.java index cc65198e..be97d325 100644 --- a/src/main/java/org/openmaptiles/layers/Water.java +++ b/src/main/java/org/openmaptiles/layers/Water.java @@ -220,7 +220,7 @@ void attemptNeLakeIdMapping(Tables.OsmWaterPolygon element) throws GeometryExcep items = neLakeIndex.getIntersecting(geom); } catch (TopologyException e) { stats.dataError("omt_water_intersecting"); - LOGGER.warn("omt_water_intersecting, OSM ID: {}", + LOGGER.debug("omt_water_intersecting, OSM ID: {}", element.source().id(), e); final var geomFixed = GeometryFixer.fix(geom); try { @@ -250,7 +250,7 @@ void fillOsmIdIntoNeLake(Tables.OsmWaterPolygon element, Geometry geom, LakeInfo intersection = neGeom.intersection(geom); } catch (TopologyException e) { stats.dataError("omt_water_intersection"); - LOGGER.warn("omt_water_intersection, NE ID: {}, OSM ID: {}", + LOGGER.debug("omt_water_intersection, NE ID: {}, OSM ID: {}", lakeInfo.neId, element.source().id(), e); final var geomFixed = GeometryFixer.fix(geom); try { From 7bdbd2054c9d602bc91ab5206de5c340d1abb5ff Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Tue, 30 Apr 2024 19:35:12 +0200 Subject: [PATCH 200/211] bumped copyright statements --- src/main/java/org/openmaptiles/layers/Boundary.java | 2 +- src/main/java/org/openmaptiles/layers/Water.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/openmaptiles/layers/Boundary.java b/src/main/java/org/openmaptiles/layers/Boundary.java index b075a01d..2f72e4fe 100644 --- a/src/main/java/org/openmaptiles/layers/Boundary.java +++ b/src/main/java/org/openmaptiles/layers/Boundary.java @@ -1,5 +1,5 @@ /* -Copyright (c) 2021, MapTiler.com & OpenMapTiles contributors. +Copyright (c) 2024, MapTiler.com & OpenMapTiles contributors. All rights reserved. Code license: BSD 3-Clause License diff --git a/src/main/java/org/openmaptiles/layers/Water.java b/src/main/java/org/openmaptiles/layers/Water.java index bab3bfec..d7e65824 100644 --- a/src/main/java/org/openmaptiles/layers/Water.java +++ b/src/main/java/org/openmaptiles/layers/Water.java @@ -1,5 +1,5 @@ /* -Copyright (c) 2021, MapTiler.com & OpenMapTiles contributors. +Copyright (c) 2024, MapTiler.com & OpenMapTiles contributors. All rights reserved. Code license: BSD 3-Clause License From dece99fee831edd9395f0ee19e97292d679f64e3 Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Tue, 30 Apr 2024 19:39:23 +0200 Subject: [PATCH 201/211] bumped copyright statements --- src/main/java/org/openmaptiles/layers/Building.java | 2 +- src/main/java/org/openmaptiles/layers/Landcover.java | 2 +- src/main/java/org/openmaptiles/layers/Park.java | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/openmaptiles/layers/Building.java b/src/main/java/org/openmaptiles/layers/Building.java index 49dc6b25..c1a1a3dc 100644 --- a/src/main/java/org/openmaptiles/layers/Building.java +++ b/src/main/java/org/openmaptiles/layers/Building.java @@ -1,5 +1,5 @@ /* -Copyright (c) 2021, MapTiler.com & OpenMapTiles contributors. +Copyright (c) 2023, MapTiler.com & OpenMapTiles contributors. All rights reserved. Code license: BSD 3-Clause License diff --git a/src/main/java/org/openmaptiles/layers/Landcover.java b/src/main/java/org/openmaptiles/layers/Landcover.java index 7bc66d78..43a5b647 100644 --- a/src/main/java/org/openmaptiles/layers/Landcover.java +++ b/src/main/java/org/openmaptiles/layers/Landcover.java @@ -1,5 +1,5 @@ /* -Copyright (c) 2021, MapTiler.com & OpenMapTiles contributors. +Copyright (c) 2023, MapTiler.com & OpenMapTiles contributors. All rights reserved. Code license: BSD 3-Clause License diff --git a/src/main/java/org/openmaptiles/layers/Park.java b/src/main/java/org/openmaptiles/layers/Park.java index 648df0f3..438745fb 100644 --- a/src/main/java/org/openmaptiles/layers/Park.java +++ b/src/main/java/org/openmaptiles/layers/Park.java @@ -1,5 +1,5 @@ /* -Copyright (c) 2021, MapTiler.com & OpenMapTiles contributors. +Copyright (c) 2024, MapTiler.com & OpenMapTiles contributors. All rights reserved. Code license: BSD 3-Clause License From 7440973169a6bb87f276bdf415edcdb0a3922c3c Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Thu, 2 May 2024 14:16:00 +0200 Subject: [PATCH 202/211] print errors+stack-traces only after retries with fixed polygons since we are not able to raise issues with JTS if we do stuff with invalid/unfixed polygons --- src/main/java/org/openmaptiles/layers/Water.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/main/java/org/openmaptiles/layers/Water.java b/src/main/java/org/openmaptiles/layers/Water.java index be97d325..8d361b11 100644 --- a/src/main/java/org/openmaptiles/layers/Water.java +++ b/src/main/java/org/openmaptiles/layers/Water.java @@ -220,8 +220,6 @@ void attemptNeLakeIdMapping(Tables.OsmWaterPolygon element) throws GeometryExcep items = neLakeIndex.getIntersecting(geom); } catch (TopologyException e) { stats.dataError("omt_water_intersecting"); - LOGGER.debug("omt_water_intersecting, OSM ID: {}", - element.source().id(), e); final var geomFixed = GeometryFixer.fix(geom); try { items = neLakeIndex.getIntersecting(geomFixed); @@ -250,8 +248,6 @@ void fillOsmIdIntoNeLake(Tables.OsmWaterPolygon element, Geometry geom, LakeInfo intersection = neGeom.intersection(geom); } catch (TopologyException e) { stats.dataError("omt_water_intersection"); - LOGGER.debug("omt_water_intersection, NE ID: {}, OSM ID: {}", - lakeInfo.neId, element.source().id(), e); final var geomFixed = GeometryFixer.fix(geom); try { if (intersetsCheckNeeded && !neGeom.intersects(geomFixed)) { From 4ed3d9dc7cda1f38e6019ef2cb738dbed622b0bd Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Thu, 2 May 2024 14:21:07 +0200 Subject: [PATCH 203/211] message about fixing NE lake geometry demoted: warn -> debug --- src/main/java/org/openmaptiles/layers/Water.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/openmaptiles/layers/Water.java b/src/main/java/org/openmaptiles/layers/Water.java index 8d361b11..4ac07591 100644 --- a/src/main/java/org/openmaptiles/layers/Water.java +++ b/src/main/java/org/openmaptiles/layers/Water.java @@ -127,7 +127,7 @@ record WaterInfo(int minZoom, int maxZoom, String clazz) {} if (geom.isValid()) { lakeInfo.geom = geom; } else { - LOGGER.warn("Fixing geometry of NE lake {}", feature.getLong("ne_id")); + LOGGER.debug("Fixing geometry of NE lake {}", feature.getLong("ne_id")); lakeInfo.geom = GeometryFixer.fix(geom); } lakeInfo.name = feature.getString("name"); From aebb96b8a86271fbf4fe034da7750f9c52f4d42f Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Thu, 2 May 2024 14:23:06 +0200 Subject: [PATCH 204/211] omt_water message adjusted to make it more clear what it relates to --- src/main/java/org/openmaptiles/layers/Water.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/openmaptiles/layers/Water.java b/src/main/java/org/openmaptiles/layers/Water.java index 4ac07591..e47276a7 100644 --- a/src/main/java/org/openmaptiles/layers/Water.java +++ b/src/main/java/org/openmaptiles/layers/Water.java @@ -187,7 +187,7 @@ public void process(Tables.OsmWaterPolygon element, FeatureCollector features) { attemptNeLakeIdMapping(element); } catch (GeometryException e) { e.log(stats, "omt_water", - "Unable to process intersections", config.logJtsExceptions()); + "Unable to to add OSM ID to natural earth water feature", config.logJtsExceptions()); } } } From d47a4f53592a829593f81df4b41adfb75dcccc4d Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Thu, 2 May 2024 14:30:02 +0200 Subject: [PATCH 205/211] do NOT store fixed geom in separate variable to avoid 2nd fixing in fillOsmIdIntoNeLake() --- src/main/java/org/openmaptiles/layers/Water.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/openmaptiles/layers/Water.java b/src/main/java/org/openmaptiles/layers/Water.java index e47276a7..97f21f69 100644 --- a/src/main/java/org/openmaptiles/layers/Water.java +++ b/src/main/java/org/openmaptiles/layers/Water.java @@ -220,9 +220,9 @@ void attemptNeLakeIdMapping(Tables.OsmWaterPolygon element) throws GeometryExcep items = neLakeIndex.getIntersecting(geom); } catch (TopologyException e) { stats.dataError("omt_water_intersecting"); - final var geomFixed = GeometryFixer.fix(geom); + geom= GeometryFixer.fix(geom); try { - items = neLakeIndex.getIntersecting(geomFixed); + items = neLakeIndex.getIntersecting(geom); } catch (TopologyException e2) { throw new GeometryException("omt_water_intersecting_fix", "Error getting intersecting for OSM ID " + element.source().id(), e2); From b1ebf609705a61ee54fd79aae2e46ebfb2019901 Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Thu, 2 May 2024 14:35:49 +0200 Subject: [PATCH 206/211] one more error message rephrased for clearer context --- src/main/java/org/openmaptiles/layers/Water.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/openmaptiles/layers/Water.java b/src/main/java/org/openmaptiles/layers/Water.java index 97f21f69..08fc6919 100644 --- a/src/main/java/org/openmaptiles/layers/Water.java +++ b/src/main/java/org/openmaptiles/layers/Water.java @@ -187,7 +187,7 @@ public void process(Tables.OsmWaterPolygon element, FeatureCollector features) { attemptNeLakeIdMapping(element); } catch (GeometryException e) { e.log(stats, "omt_water", - "Unable to to add OSM ID to natural earth water feature", config.logJtsExceptions()); + "Unable to add OSM ID to natural earth water feature", config.logJtsExceptions()); } } } @@ -220,7 +220,7 @@ void attemptNeLakeIdMapping(Tables.OsmWaterPolygon element) throws GeometryExcep items = neLakeIndex.getIntersecting(geom); } catch (TopologyException e) { stats.dataError("omt_water_intersecting"); - geom= GeometryFixer.fix(geom); + geom = GeometryFixer.fix(geom); try { items = neLakeIndex.getIntersecting(geom); } catch (TopologyException e2) { @@ -256,7 +256,9 @@ void fillOsmIdIntoNeLake(Tables.OsmWaterPolygon element, Geometry geom, LakeInfo intersection = neGeom.intersection(geomFixed); } catch (TopologyException e2) { throw new GeometryException("omt_water_intersection_fix", - "Error computing intersection for NE ID " + lakeInfo.neId + " and OSM ID " + element.source().id(), e2); + "Error computing intersection for NE ID " + lakeInfo.neId + " and OSM ID " + element.source().id() + + " while trying to assign OSM ID to natural earth water feature", + e2); } } From 22551cf3ac83f0b1c4e3c2be92deee3c73b471e8 Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Thu, 2 May 2024 14:38:34 +0200 Subject: [PATCH 207/211] Revert "print errors+stack-traces only after retries with fixed polygons" This reverts commit 7440973169a6bb87f276bdf415edcdb0a3922c3c. --- src/main/java/org/openmaptiles/layers/Water.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/java/org/openmaptiles/layers/Water.java b/src/main/java/org/openmaptiles/layers/Water.java index 08fc6919..1f8f4052 100644 --- a/src/main/java/org/openmaptiles/layers/Water.java +++ b/src/main/java/org/openmaptiles/layers/Water.java @@ -220,6 +220,8 @@ void attemptNeLakeIdMapping(Tables.OsmWaterPolygon element) throws GeometryExcep items = neLakeIndex.getIntersecting(geom); } catch (TopologyException e) { stats.dataError("omt_water_intersecting"); + LOGGER.debug("omt_water_intersecting, OSM ID: {}", + element.source().id(), e); geom = GeometryFixer.fix(geom); try { items = neLakeIndex.getIntersecting(geom); @@ -248,6 +250,8 @@ void fillOsmIdIntoNeLake(Tables.OsmWaterPolygon element, Geometry geom, LakeInfo intersection = neGeom.intersection(geom); } catch (TopologyException e) { stats.dataError("omt_water_intersection"); + LOGGER.debug("omt_water_intersection, NE ID: {}, OSM ID: {}", + lakeInfo.neId, element.source().id(), e); final var geomFixed = GeometryFixer.fix(geom); try { if (intersetsCheckNeeded && !neGeom.intersects(geomFixed)) { From eb56ed40af8f90868fa1481eff19338393e6e993 Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Thu, 2 May 2024 15:13:29 +0200 Subject: [PATCH 208/211] strip stack-trace from TopologyException debug messages --- src/main/java/org/openmaptiles/layers/Water.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/openmaptiles/layers/Water.java b/src/main/java/org/openmaptiles/layers/Water.java index 1f8f4052..60a231e7 100644 --- a/src/main/java/org/openmaptiles/layers/Water.java +++ b/src/main/java/org/openmaptiles/layers/Water.java @@ -220,8 +220,8 @@ void attemptNeLakeIdMapping(Tables.OsmWaterPolygon element) throws GeometryExcep items = neLakeIndex.getIntersecting(geom); } catch (TopologyException e) { stats.dataError("omt_water_intersecting"); - LOGGER.debug("omt_water_intersecting, OSM ID: {}", - element.source().id(), e); + LOGGER.debug("Unable to determine intersecting natural earth water features for OSM ID {}: {}", + element.source().id(), e.getMessage()); geom = GeometryFixer.fix(geom); try { items = neLakeIndex.getIntersecting(geom); @@ -250,8 +250,8 @@ void fillOsmIdIntoNeLake(Tables.OsmWaterPolygon element, Geometry geom, LakeInfo intersection = neGeom.intersection(geom); } catch (TopologyException e) { stats.dataError("omt_water_intersection"); - LOGGER.debug("omt_water_intersection, NE ID: {}, OSM ID: {}", - lakeInfo.neId, element.source().id(), e); + LOGGER.debug("Unable to compute intersection of natural earth water feature {} with OSM element {}: {}", + lakeInfo.neId, element.source().id(), e.getMessage()); final var geomFixed = GeometryFixer.fix(geom); try { if (intersetsCheckNeeded && !neGeom.intersects(geomFixed)) { @@ -261,7 +261,7 @@ void fillOsmIdIntoNeLake(Tables.OsmWaterPolygon element, Geometry geom, LakeInfo } catch (TopologyException e2) { throw new GeometryException("omt_water_intersection_fix", "Error computing intersection for NE ID " + lakeInfo.neId + " and OSM ID " + element.source().id() + - " while trying to assign OSM ID to natural earth water feature", + " while trying to add OSM ID to natural earth water feature", e2); } } From cbbcfbf9092f29a9d714d8de1e5ab3145a54dd04 Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Thu, 2 May 2024 17:20:18 +0200 Subject: [PATCH 209/211] fix OSM element geometry if not valid before trying to match NE lake This is same trick as done for NE lakes before. Thanks to that we then do not need to handle TopologyExceptions and do retries. --- .../java/org/openmaptiles/layers/Water.java | 48 +++++-------------- 1 file changed, 11 insertions(+), 37 deletions(-) diff --git a/src/main/java/org/openmaptiles/layers/Water.java b/src/main/java/org/openmaptiles/layers/Water.java index 60a231e7..bf5e902c 100644 --- a/src/main/java/org/openmaptiles/layers/Water.java +++ b/src/main/java/org/openmaptiles/layers/Water.java @@ -199,6 +199,12 @@ void attemptNeLakeIdMapping(Tables.OsmWaterPolygon element) throws GeometryExcep return; } + if (!geom.isValid()) { + geom = GeometryFixer.fix(geom); + stats.dataError("omt_fix_water_before_ne_intersect"); + LOGGER.debug("Fixing geometry of OSM element {} before attempt to add ID to natural earth water feature", element.source().id()); + } + // match by name: boolean match = false; if (element.name() != null) { @@ -215,21 +221,7 @@ void attemptNeLakeIdMapping(Tables.OsmWaterPolygon element) throws GeometryExcep } // match by intersection: - List items; - try { - items = neLakeIndex.getIntersecting(geom); - } catch (TopologyException e) { - stats.dataError("omt_water_intersecting"); - LOGGER.debug("Unable to determine intersecting natural earth water features for OSM ID {}: {}", - element.source().id(), e.getMessage()); - geom = GeometryFixer.fix(geom); - try { - items = neLakeIndex.getIntersecting(geom); - } catch (TopologyException e2) { - throw new GeometryException("omt_water_intersecting_fix", - "Error getting intersecting for OSM ID " + element.source().id(), e2); - } - } + List items = neLakeIndex.getIntersecting(geom); for (var lakeInfo : items) { fillOsmIdIntoNeLake(element, geom, lakeInfo, false); } @@ -240,31 +232,13 @@ void attemptNeLakeIdMapping(Tables.OsmWaterPolygon element) throws GeometryExcep * otherwise `true`, to make sure we DO check the intersection but to avoid checking it twice. */ void fillOsmIdIntoNeLake(Tables.OsmWaterPolygon element, Geometry geom, LakeInfo lakeInfo, - boolean intersetsCheckNeeded) throws GeometryException { + boolean intersetsCheckNeeded) { final Geometry neGeom = lakeInfo.geom; Geometry intersection; - try { - if (intersetsCheckNeeded && !neGeom.intersects(geom)) { - return; - } - intersection = neGeom.intersection(geom); - } catch (TopologyException e) { - stats.dataError("omt_water_intersection"); - LOGGER.debug("Unable to compute intersection of natural earth water feature {} with OSM element {}: {}", - lakeInfo.neId, element.source().id(), e.getMessage()); - final var geomFixed = GeometryFixer.fix(geom); - try { - if (intersetsCheckNeeded && !neGeom.intersects(geomFixed)) { - return; - } - intersection = neGeom.intersection(geomFixed); - } catch (TopologyException e2) { - throw new GeometryException("omt_water_intersection_fix", - "Error computing intersection for NE ID " + lakeInfo.neId + " and OSM ID " + element.source().id() + - " while trying to add OSM ID to natural earth water feature", - e2); - } + if (intersetsCheckNeeded && !neGeom.intersects(geom)) { + return; } + intersection = neGeom.intersection(geom); // Should match following in OpenMapTiles: Distinct on keeps just the first occurence -> order by 'area_ratio DESC' // With a twist: NE geometry is always the same, hence we can make it a little bit faster by dropping "ratio" From 7928731d03845dd4258e2820623c990b2e2da7ab Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Thu, 2 May 2024 17:23:05 +0200 Subject: [PATCH 210/211] clean-up --- src/main/java/org/openmaptiles/layers/Water.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/java/org/openmaptiles/layers/Water.java b/src/main/java/org/openmaptiles/layers/Water.java index bf5e902c..df9dd4ac 100644 --- a/src/main/java/org/openmaptiles/layers/Water.java +++ b/src/main/java/org/openmaptiles/layers/Water.java @@ -234,11 +234,10 @@ void attemptNeLakeIdMapping(Tables.OsmWaterPolygon element) throws GeometryExcep void fillOsmIdIntoNeLake(Tables.OsmWaterPolygon element, Geometry geom, LakeInfo lakeInfo, boolean intersetsCheckNeeded) { final Geometry neGeom = lakeInfo.geom; - Geometry intersection; if (intersetsCheckNeeded && !neGeom.intersects(geom)) { return; } - intersection = neGeom.intersection(geom); + final var intersection = neGeom.intersection(geom); // Should match following in OpenMapTiles: Distinct on keeps just the first occurence -> order by 'area_ratio DESC' // With a twist: NE geometry is always the same, hence we can make it a little bit faster by dropping "ratio" From 98d3054a8b9c83669c7cff81934a51a5bea480ef Mon Sep 17 00:00:00 2001 From: Peter Hanecak Date: Fri, 3 May 2024 09:42:33 +0200 Subject: [PATCH 211/211] clean-up: mvn spotless:apply --- src/main/java/org/openmaptiles/layers/Water.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/openmaptiles/layers/Water.java b/src/main/java/org/openmaptiles/layers/Water.java index df9dd4ac..afe424bc 100644 --- a/src/main/java/org/openmaptiles/layers/Water.java +++ b/src/main/java/org/openmaptiles/layers/Water.java @@ -53,7 +53,6 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE import java.util.concurrent.ConcurrentHashMap; import java.util.function.Consumer; import org.locationtech.jts.geom.Geometry; -import org.locationtech.jts.geom.TopologyException; import org.locationtech.jts.geom.util.GeometryFixer; import org.openmaptiles.OpenMapTilesProfile; import org.openmaptiles.generated.OpenMapTilesSchema; @@ -202,7 +201,8 @@ void attemptNeLakeIdMapping(Tables.OsmWaterPolygon element) throws GeometryExcep if (!geom.isValid()) { geom = GeometryFixer.fix(geom); stats.dataError("omt_fix_water_before_ne_intersect"); - LOGGER.debug("Fixing geometry of OSM element {} before attempt to add ID to natural earth water feature", element.source().id()); + LOGGER.debug("Fixing geometry of OSM element {} before attempt to add ID to natural earth water feature", + element.source().id()); } // match by name: