Upgrade planetiler-basemap to be compatible with OpenMapTiles 3.13 (#49)

Applying changes to layers from [OpenMapTiles 3.13 release](https://github.com/openmaptiles/openmaptiles/releases/tag/v3.13) (https://github.com/openmaptiles/openmaptiles/compare/v3.12.2...v3.13), minus transportation network connectivity improvements - those will be a separate change.
This commit is contained in:
Michael Barry
2022-01-19 05:36:44 -05:00
committed by GitHub
parent 8e49c0831c
commit 0baaf8d886
37 changed files with 1690 additions and 464 deletions

View File

@@ -10,6 +10,8 @@ import com.onthegomap.planetiler.Planetiler;
import com.onthegomap.planetiler.Profile;
import com.onthegomap.planetiler.basemap.generated.OpenMapTilesSchema;
import com.onthegomap.planetiler.basemap.generated.Tables;
import com.onthegomap.planetiler.basemap.layers.Transportation;
import com.onthegomap.planetiler.basemap.layers.TransportationName;
import com.onthegomap.planetiler.config.PlanetilerConfig;
import com.onthegomap.planetiler.expression.MultiExpression;
import com.onthegomap.planetiler.reader.SimpleFeature;
@@ -61,10 +63,27 @@ public class BasemapProfile extends ForwardingProfile {
// register release/finish/feature postprocessor/osm relationship handler methods...
List<Handler> layers = new ArrayList<>();
Transportation transportationLayer = null;
TransportationName transportationNameLayer = null;
for (Layer layer : OpenMapTilesSchema.createInstances(translations, config, stats)) {
if ((onlyLayers.isEmpty() || onlyLayers.contains(layer.name())) && !excludeLayers.contains(layer.name())) {
layers.add(layer);
registerHandler(layer);
if (layer instanceof TransportationName transportationName) {
transportationNameLayer = transportationName;
}
}
if (layer instanceof Transportation transportation) {
transportationLayer = transportation;
}
}
// special-case: transportation_name layer depends on transportation layer
if (transportationNameLayer != null) {
transportationNameLayer.needsTransportationLayer(transportationLayer);
if (!layers.contains(transportationLayer)) {
layers.add(transportationLayer);
registerHandler(transportationLayer);
}
}

View File

@@ -58,7 +58,7 @@ public class Generate {
private static final String LINE_SEPARATOR = System.lineSeparator();
private static final String GENERATED_FILE_HEADER = """
/*
Copyright (c) 2016, KlokanTech.com & OpenMapTiles contributors.
Copyright (c) 2021, MapTiler.com & OpenMapTiles contributors.
All rights reserved.
Code license: BSD 3-Clause License
@@ -690,12 +690,12 @@ public class Generate {
* Models for deserializing yaml into:
*/
private static record OpenmaptilesConfig(
private record OpenmaptilesConfig(
OpenmaptilesTileSet tileset
) {}
@JsonIgnoreProperties(ignoreUnknown = true)
private static record OpenmaptilesTileSet(
private record OpenmaptilesTileSet(
List<String> layers,
String version,
String attribution,
@@ -705,51 +705,52 @@ public class Generate {
) {}
@JsonIgnoreProperties(ignoreUnknown = true)
private static record LayerDetails(
private record LayerDetails(
String id,
String description,
Map<String, JsonNode> fields,
double buffer_size
) {}
private static record Datasource(
private record Datasource(
String type,
String mapping_file
) {}
@JsonIgnoreProperties(ignoreUnknown = true)
private static record LayerConfig(
private record LayerConfig(
LayerDetails layer,
List<Datasource> datasources
) {}
private static record Imposm3Column(
private record Imposm3Column(
String type,
String name,
String key,
boolean from_member
) {}
static record Imposm3Filters(
record Imposm3Filters(
JsonNode reject,
JsonNode require
) {}
static record Imposm3Table(
record Imposm3Table(
String type,
@JsonProperty("_resolve_wikidata") boolean resolveWikidata,
List<Imposm3Column> columns,
Imposm3Filters filters,
JsonNode mapping,
Map<String, JsonNode> type_mappings
Map<String, JsonNode> type_mappings,
@JsonProperty("relation_types") List<String> relationTypes
) {}
@JsonIgnoreProperties(ignoreUnknown = true)
private static record Imposm3Mapping(
private record Imposm3Mapping(
Map<String, Imposm3Table> tables
) {}
private static record OsmTableField(
private record OsmTableField(
String clazz,
String name,
String extractCode

View File

@@ -1,5 +1,5 @@
/*
Copyright (c) 2016, KlokanTech.com & OpenMapTiles contributors.
Copyright (c) 2021, MapTiler.com & OpenMapTiles contributors.
All rights reserved.
Code license: BSD 3-Clause License
@@ -52,16 +52,15 @@ import java.util.Set;
/**
* All vector tile layer definitions, attributes, and allowed values generated from the
* <a href="https://github.com/openmaptiles/openmaptiles/blob/v3.12.2/openmaptiles.yaml">OpenMapTiles vector tile
* schema
* v3.12.2</a>.
* <a href="https://github.com/openmaptiles/openmaptiles/blob/v3.13/openmaptiles.yaml">OpenMapTiles vector tile schema
* v3.13</a>.
*/
@SuppressWarnings("unused")
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.12.1";
public static final String VERSION = "3.13.0";
public static final String ATTRIBUTION = "<a href=\"https://www.openmaptiles.org/\" target=\"_blank\">&copy; OpenMapTiles</a> <a href=\"https://www.openstreetmap.org/copyright\" target=\"_blank\">&copy; OpenStreetMap contributors</a>";
public static final List<String> 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",
@@ -99,7 +98,7 @@ public class OpenMapTilesSchema {
* 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.
* <p>
* Generated from <a href="https://github.com/openmaptiles/openmaptiles/blob/v3.12.2/layers/water/water.yaml">water.yaml</a>
* Generated from <a href="https://github.com/openmaptiles/openmaptiles/blob/v3.13/layers/water/water.yaml">water.yaml</a>
*/
public interface Water extends Layer {
@@ -116,14 +115,16 @@ public class OpenMapTilesSchema {
/**
* All water polygons from <a href="http://osmdata.openstreetmap.de/">OpenStreetMapData</a> have the class
* <code>ocean</code>. Water bodies are classified as <code>lake</code> or <code>river</code> for water bodies
* with the <a href="http://wiki.openstreetmap.org/wiki/Key:waterway"><code>waterway</code></a> tag.
* <code>ocean</code>. Water bodies with the <a href="http://wiki.openstreetmap.org/wiki/Tag:waterway=riverbank"><code>waterway=riverbank</code></a>
* or <a href="http://wiki.openstreetmap.org/wiki/Tag:water=river"><code>water=river</code></a> tag are classified
* as river. Wet and dry docks tagged <a href="http://wiki.openstreetmap.org/wiki/Tag:waterway=dock"><code>waterway=dock</code></a>
* are classified as a <code>dock</code>. All other water bodies are classified as <code>lake</code>.
* <p>
* allowed values:
* <ul>
* <li>lake
* <li>dock
* <li>river
* <li>lake
* <li>ocean
* </ul>
*/
@@ -156,11 +157,11 @@ public class OpenMapTilesSchema {
/** Attribute values for map elements in the water layer. */
final class FieldValues {
public static final String CLASS_LAKE = "lake";
public static final String CLASS_DOCK = "dock";
public static final String CLASS_RIVER = "river";
public static final String CLASS_LAKE = "lake";
public static final String CLASS_OCEAN = "ocean";
public static final Set<String> CLASS_VALUES = Set.of("lake", "dock", "river", "ocean");
public static final Set<String> CLASS_VALUES = Set.of("dock", "river", "lake", "ocean");
public static final String BRUNNEL_BRIDGE = "bridge";
public static final String BRUNNEL_TUNNEL = "tunnel";
public static final Set<String> BRUNNEL_VALUES = Set.of("bridge", "tunnel");
@@ -170,9 +171,9 @@ public class OpenMapTilesSchema {
final class FieldMappings {
public static final MultiExpression<String> Class = MultiExpression.of(
List.of(MultiExpression.entry("lake", matchAny("waterway", "", "lake")),
MultiExpression.entry("dock", matchAny("waterway", "dock")), MultiExpression.entry("river", FALSE),
MultiExpression.entry("ocean", FALSE)));
List.of(MultiExpression.entry("dock", matchAny("waterway", "dock")),
MultiExpression.entry("river", or(matchAny("water", "river"), matchAny("waterway", "riverbank"))),
MultiExpression.entry("lake", matchAny("waterway", "")), MultiExpression.entry("ocean", FALSE)));
}
}
@@ -183,7 +184,7 @@ public class OpenMapTilesSchema {
* there is also <code>canal</code> generated, starting z13 there is no generalization according to <code>class</code>
* field applied. Waterways do not have a <code>subclass</code> field.
* <p>
* Generated from <a href="https://github.com/openmaptiles/openmaptiles/blob/v3.12.2/layers/waterway/waterway.yaml">waterway.yaml</a>
* Generated from <a href="https://github.com/openmaptiles/openmaptiles/blob/v3.13/layers/waterway/waterway.yaml">waterway.yaml</a>
*/
public interface Waterway extends Layer {
@@ -273,7 +274,7 @@ public class OpenMapTilesSchema {
* href="http://wiki.openstreetmap.org/wiki/Landcover">implied by OSM tags</a>. The most common use case for this
* layer is to style wood (<code>class=wood</code>) and grass (<code>class=grass</code>) areas.
* <p>
* Generated from <a href="https://github.com/openmaptiles/openmaptiles/blob/v3.12.2/layers/landcover/landcover.yaml">landcover.yaml</a>
* Generated from <a href="https://github.com/openmaptiles/openmaptiles/blob/v3.13/layers/landcover/landcover.yaml">landcover.yaml</a>
*/
public interface Landcover extends Layer {
@@ -428,7 +429,7 @@ public class OpenMapTilesSchema {
* 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 <code>landuse</code> tags.
* <p>
* Generated from <a href="https://github.com/openmaptiles/openmaptiles/blob/v3.12.2/layers/landuse/landuse.yaml">landuse.yaml</a>
* Generated from <a href="https://github.com/openmaptiles/openmaptiles/blob/v3.13/layers/landuse/landuse.yaml">landuse.yaml</a>
*/
public interface Landuse extends Layer {
@@ -527,7 +528,7 @@ public class OpenMapTilesSchema {
/**
* <a href="http://wiki.openstreetmap.org/wiki/Tag:natural%3Dpeak">Natural peaks</a>
* <p>
* Generated from <a href="https://github.com/openmaptiles/openmaptiles/blob/v3.12.2/layers/mountain_peak/mountain_peak.yaml">mountain_peak.yaml</a>
* Generated from <a href="https://github.com/openmaptiles/openmaptiles/blob/v3.13/layers/mountain_peak/mountain_peak.yaml">mountain_peak.yaml</a>
*/
public interface MountainPeak extends Layer {
@@ -556,13 +557,27 @@ public class OpenMapTilesSchema {
* <ul>
* <li>"peak"
* <li>"volcano"
* <li>"ridge"
* <li>"cliff"
* <li>"arete"
* </ul>
*/
public static final String CLASS = "class";
/** Elevation (<code>ele</code>) in meters. */
public static final String ELE = "ele";
/** Elevation (<code>ele</code>) in feets. */
/** Elevation (<code>ele</code>) in feet. */
public static final String ELE_FT = "ele_ft";
/**
* Value 1 for peaks in location where feet is used as customary unit (USA).
* <p>
* allowed values:
* <ul>
* <li>1
* <li>null
* </ul>
*/
public static final String CUSTOMARY_FT = "customary_ft";
/** Rank of the peak within one tile (starting at 1 that is the most important peak). */
public static final String RANK = "rank";
}
@@ -572,7 +587,10 @@ public class OpenMapTilesSchema {
public static final String CLASS_PEAK = "peak";
public static final String CLASS_VOLCANO = "volcano";
public static final Set<String> CLASS_VALUES = Set.of("peak", "volcano");
public static final String CLASS_RIDGE = "ridge";
public static final String CLASS_CLIFF = "cliff";
public static final String CLASS_ARETE = "arete";
public static final Set<String> CLASS_VALUES = Set.of("peak", "volcano", "ridge", "cliff", "arete");
}
/** Complex mappings to generate attribute values from OSM element tags in the mountain_peak layer. */
@@ -586,7 +604,7 @@ public class OpenMapTilesSchema {
* <a href="http://wiki.openstreetmap.org/wiki/Tag:boundary%3Dprotected_area"><code>boundary=protected_area</code></a>,
* or <a href="http://wiki.openstreetmap.org/wiki/Tag:leisure%3Dnature_reserve"><code>leisure=nature_reserve</code></a>.
* <p>
* Generated from <a href="https://github.com/openmaptiles/openmaptiles/blob/v3.12.2/layers/park/park.yaml">park.yaml</a>
* Generated from <a href="https://github.com/openmaptiles/openmaptiles/blob/v3.13/layers/park/park.yaml">park.yaml</a>
*/
public interface Park extends Layer {
@@ -645,7 +663,7 @@ public class OpenMapTilesSchema {
* contains several <a href="http://wiki.openstreetmap.org/wiki/Tag:boundary%3Dadministrative#admin_level"><code>admin_level</code></a>
* but for most styles it makes sense to just style <code>admin_level=2</code> and <code>admin_level=4</code>.
* <p>
* Generated from <a href="https://github.com/openmaptiles/openmaptiles/blob/v3.12.2/layers/boundary/boundary.yaml">boundary.yaml</a>
* Generated from <a href="https://github.com/openmaptiles/openmaptiles/blob/v3.13/layers/boundary/boundary.yaml">boundary.yaml</a>
*/
public interface Boundary extends Layer {
@@ -751,7 +769,7 @@ public class OpenMapTilesSchema {
* buildings are contained in the <strong>building</strong> layer but all other airport related polygons can be found
* in the <strong>aeroway</strong> layer.
* <p>
* Generated from <a href="https://github.com/openmaptiles/openmaptiles/blob/v3.12.2/layers/aeroway/aeroway.yaml">aeroway.yaml</a>
* Generated from <a href="https://github.com/openmaptiles/openmaptiles/blob/v3.13/layers/aeroway/aeroway.yaml">aeroway.yaml</a>
*/
public interface Aeroway extends Layer {
@@ -814,7 +832,7 @@ public class OpenMapTilesSchema {
* roads is the most essential part of the map. The <code>transportation</code> layer also contains polygons for
* features like plazas.
* <p>
* Generated from <a href="https://github.com/openmaptiles/openmaptiles/blob/v3.12.2/layers/transportation/transportation.yaml">transportation.yaml</a>
* Generated from <a href="https://github.com/openmaptiles/openmaptiles/blob/v3.13/layers/transportation/transportation.yaml">transportation.yaml</a>
*/
public interface Transportation extends Layer {
@@ -836,7 +854,7 @@ public class OpenMapTilesSchema {
* href="http://wiki.openstreetmap.org/wiki/Key:railway"><code>railway</code></a>, <a
* href="http://wiki.openstreetmap.org/wiki/Key:aerialway"><code>aerialway</code></a>, <a
* href="http://wiki.openstreetmap.org/wiki/Key:route"><code>route</code></a> tag (for shipping ways), or <a
* href="http://wiki.openstreetmap.org/wiki/Key:route"><code>man_made</code></a>.
* href="http://wiki.openstreetmap.org/wiki/Key:man_made"><code>man_made</code></a>.
* <p>
* allowed values:
* <ul>
@@ -850,6 +868,7 @@ public class OpenMapTilesSchema {
* <li>service
* <li>track
* <li>raceway
* <li>busway
* <li>motorway_construction
* <li>trunk_construction
* <li>primary_construction
@@ -892,6 +911,13 @@ public class OpenMapTilesSchema {
* </ul>
*/
public static final String SUBCLASS = "subclass";
/**
* The network type derived mainly from <a href="http://wiki.openstreetmap.org/wiki/Key:network"><code>network</code></a>
* tag of the road. See more info about <a href="http://wiki.openstreetmap.org/wiki/Road_signs_in_the_United_States"><code>us-
* </code></a>, <a href="https://en.wikipedia.org/wiki/Trans-Canada_Highway"><code>ca-transcanada</code></a>, or
* <a href="http://wiki.openstreetmap.org/wiki/United_Kingdom_Tagging_Guidelines#UK_roads"><code>gb- </code></a>.
*/
public static final String NETWORK = "network";
/**
* Mark whether way is a tunnel or bridge.
@@ -944,6 +970,40 @@ public class OpenMapTilesSchema {
* </ul>
*/
public static final String SERVICE = "service";
/**
* Access restrictions on this road. Supported values of the <a href="http://wiki.openstreetmap.org/wiki/Key:access"><code>access</code></a>
* tag are <code>no</code> and <code>private</code>, which resolve to <code>no</code>.
* <p>
* allowed values:
* <ul>
* <li>false
* </ul>
*/
public static final String ACCESS = "access";
/**
* Whether this is a toll road, based on the <a href="http://wiki.openstreetmap.org/wiki/Key:toll"><code>toll</code></a>
* tag.
* <p>
* allowed values:
* <ul>
* <li>0
* <li>1
* </ul>
*/
public static final String TOLL = "toll";
/**
* Whether this is an expressway, based on the <a href="http://wiki.openstreetmap.org/wiki/Key:expressway"><code>expressway</code></a>
* tag.
* <p>
* allowed values:
* <ul>
* <li>1
* </ul>
*/
public static final String EXPRESSWAY = "expressway";
/** Original value of the <a href="http://wiki.openstreetmap.org/wiki/Key:layer"><code>layer</code></a> tag. */
public static final String LAYER = "layer";
/**
@@ -1012,6 +1072,7 @@ public class OpenMapTilesSchema {
public static final String CLASS_SERVICE = "service";
public static final String CLASS_TRACK = "track";
public static final String CLASS_RACEWAY = "raceway";
public static final String CLASS_BUSWAY = "busway";
public static final String CLASS_MOTORWAY_CONSTRUCTION = "motorway_construction";
public static final String CLASS_TRUNK_CONSTRUCTION = "trunk_construction";
public static final String CLASS_PRIMARY_CONSTRUCTION = "primary_construction";
@@ -1023,7 +1084,7 @@ public class OpenMapTilesSchema {
public static final String CLASS_TRACK_CONSTRUCTION = "track_construction";
public static final String CLASS_RACEWAY_CONSTRUCTION = "raceway_construction";
public static final Set<String> CLASS_VALUES = Set.of("motorway", "trunk", "primary", "secondary", "tertiary",
"minor", "path", "service", "track", "raceway", "motorway_construction", "trunk_construction",
"minor", "path", "service", "track", "raceway", "busway", "motorway_construction", "trunk_construction",
"primary_construction", "secondary_construction", "tertiary_construction", "minor_construction",
"path_construction", "service_construction", "track_construction", "raceway_construction");
public static final String SUBCLASS_RAIL = "rail";
@@ -1079,7 +1140,7 @@ public class OpenMapTilesSchema {
MultiExpression.entry("service", matchAny("highway", "service")),
MultiExpression.entry("track", matchAny("highway", "track")),
MultiExpression.entry("raceway", matchAny("highway", "raceway")),
MultiExpression.entry("motorway_construction",
MultiExpression.entry("busway", matchAny("highway", "busway")), MultiExpression.entry("motorway_construction",
and(matchAny("highway", "construction"), matchAny("construction", "motorway", "motorway_link"))),
MultiExpression.entry("trunk_construction",
and(matchAny("highway", "construction"), matchAny("construction", "trunk", "trunk_link"))),
@@ -1107,7 +1168,7 @@ public class OpenMapTilesSchema {
* href="http://wiki.openstreetmap.org/wiki/Key:building"><code>building= </code></a>). The buildings are not yet
* ready for 3D rendering support and any help to improve this is welcomed.
* <p>
* Generated from <a href="https://github.com/openmaptiles/openmaptiles/blob/v3.12.2/layers/building/building.yaml">building.yaml</a>
* Generated from <a href="https://github.com/openmaptiles/openmaptiles/blob/v3.13/layers/building/building.yaml">building.yaml</a>
*/
public interface Building extends Layer {
@@ -1157,7 +1218,7 @@ public class OpenMapTilesSchema {
* Lake center lines for labelling lake bodies. This is based of the <a href="https://github.com/lukasmartinelli/osm-lakelines">osm-lakelines</a>
* project which derives nice centerlines from OSM water bodies. Only the most important lakes contain labels.
* <p>
* Generated from <a href="https://github.com/openmaptiles/openmaptiles/blob/v3.12.2/layers/water_name/water_name.yaml">water_name.yaml</a>
* Generated from <a href="https://github.com/openmaptiles/openmaptiles/blob/v3.13/layers/water_name/water_name.yaml">water_name.yaml</a>
*/
public interface WaterName extends Layer {
@@ -1221,7 +1282,7 @@ public class OpenMapTilesSchema {
* placement than having many small linestrings. For motorways you should use the <code>ref</code> field to label them
* while for other roads you should use <code>name</code>.
* <p>
* Generated from <a href="https://github.com/openmaptiles/openmaptiles/blob/v3.12.2/layers/transportation_name/transportation_name.yaml">transportation_name.yaml</a>
* Generated from <a href="https://github.com/openmaptiles/openmaptiles/blob/v3.13/layers/transportation_name/transportation_name.yaml">transportation_name.yaml</a>
*/
public interface TransportationName extends Layer {
@@ -1299,13 +1360,15 @@ public class OpenMapTilesSchema {
* <li>"raceway_construction"
* <li>"rail"
* <li>"transit"
* <li>"motorway_junction"
* </ul>
*/
public static final String CLASS = "class";
/**
* Distinguish more specific classes of path: Subclass is value of the <a href="http://wiki.openstreetmap.org/wiki/Key:highway"><code>highway</code></a>
* (for paths).
* (for paths), and &quot;junction&quot; for <a href="http://wiki.openstreetmap.org/wiki/Tag:highway=motorway_junction"><code>motorway
* junctions</code></a>.
* <p>
* allowed values:
* <ul>
@@ -1317,6 +1380,7 @@ public class OpenMapTilesSchema {
* <li>"bridleway"
* <li>"corridor"
* <li>"platform"
* <li>"junction"
* </ul>
*/
public static final String SUBCLASS = "subclass";
@@ -1353,6 +1417,18 @@ public class OpenMapTilesSchema {
* </ul>
*/
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";
}
/** Attribute values for map elements in the transportation_name layer. */
@@ -1389,10 +1465,12 @@ public class OpenMapTilesSchema {
public static final String CLASS_RACEWAY_CONSTRUCTION = "raceway_construction";
public static final String CLASS_RAIL = "rail";
public static final String CLASS_TRANSIT = "transit";
public static final String CLASS_MOTORWAY_JUNCTION = "motorway_junction";
public static final Set<String> CLASS_VALUES = Set.of("motorway", "trunk", "primary", "secondary", "tertiary",
"minor", "service", "track", "path", "raceway", "motorway_construction", "trunk_construction",
"primary_construction", "secondary_construction", "tertiary_construction", "minor_construction",
"service_construction", "track_construction", "path_construction", "raceway_construction", "rail", "transit");
"service_construction", "track_construction", "path_construction", "raceway_construction", "rail", "transit",
"motorway_junction");
public static final String SUBCLASS_PEDESTRIAN = "pedestrian";
public static final String SUBCLASS_PATH = "path";
public static final String SUBCLASS_FOOTWAY = "footway";
@@ -1401,8 +1479,9 @@ public class OpenMapTilesSchema {
public static final String SUBCLASS_BRIDLEWAY = "bridleway";
public static final String SUBCLASS_CORRIDOR = "corridor";
public static final String SUBCLASS_PLATFORM = "platform";
public static final String SUBCLASS_JUNCTION = "junction";
public static final Set<String> SUBCLASS_VALUES = Set.of("pedestrian", "path", "footway", "cycleway", "steps",
"bridleway", "corridor", "platform");
"bridleway", "corridor", "platform", "junction");
public static final String BRUNNEL_BRIDGE = "bridge";
public static final String BRUNNEL_TUNNEL = "tunnel";
public static final String BRUNNEL_FORD = "ford";
@@ -1422,7 +1501,7 @@ public class OpenMapTilesSchema {
* important layers to create a beautiful map. We suggest you use different font styles and sizes to create a text
* hierarchy.
* <p>
* Generated from <a href="https://github.com/openmaptiles/openmaptiles/blob/v3.12.2/layers/place/place.yaml">place.yaml</a>
* Generated from <a href="https://github.com/openmaptiles/openmaptiles/blob/v3.13/layers/place/place.yaml">place.yaml</a>
*/
public interface Place extends Layer {
@@ -1467,6 +1546,7 @@ public class OpenMapTilesSchema {
* <li>"continent"
* <li>"country"
* <li>"state"
* <li>"province"
* <li>"city"
* <li>"town"
* <li>"village"
@@ -1505,6 +1585,7 @@ public class OpenMapTilesSchema {
public static final String CLASS_CONTINENT = "continent";
public static final String CLASS_COUNTRY = "country";
public static final String CLASS_STATE = "state";
public static final String CLASS_PROVINCE = "province";
public static final String CLASS_CITY = "city";
public static final String CLASS_TOWN = "town";
public static final String CLASS_VILLAGE = "village";
@@ -1513,8 +1594,8 @@ public class OpenMapTilesSchema {
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 Set<String> CLASS_VALUES = Set.of("continent", "country", "state", "city", "town", "village",
"hamlet", "suburb", "quarter", "neighbourhood", "isolated_dwelling");
public static final Set<String> CLASS_VALUES = Set.of("continent", "country", "state", "province", "city", "town",
"village", "hamlet", "suburb", "quarter", "neighbourhood", "isolated_dwelling");
}
/** Complex mappings to generate attribute values from OSM element tags in the place layer. */
@@ -1528,7 +1609,7 @@ public class OpenMapTilesSchema {
* a map. This adds significant size to <em>z14</em>. For buildings the centroid of the building is used as
* housenumber.
* <p>
* Generated from <a href="https://github.com/openmaptiles/openmaptiles/blob/v3.12.2/layers/housenumber/housenumber.yaml">housenumber.yaml</a>
* Generated from <a href="https://github.com/openmaptiles/openmaptiles/blob/v3.13/layers/housenumber/housenumber.yaml">housenumber.yaml</a>
*/
public interface Housenumber extends Layer {
@@ -1562,7 +1643,7 @@ public class OpenMapTilesSchema {
* <a href="http://wiki.openstreetmap.org/wiki/Points_of_interest">Points of interests</a> containing a of a variety
* of OpenStreetMap tags. Mostly contains amenities, sport, shop and tourist POIs.
* <p>
* Generated from <a href="https://github.com/openmaptiles/openmaptiles/blob/v3.12.2/layers/poi/poi.yaml">poi.yaml</a>
* Generated from <a href="https://github.com/openmaptiles/openmaptiles/blob/v3.13/layers/poi/poi.yaml">poi.yaml</a>
*/
public interface Poi extends Layer {
@@ -1732,10 +1813,11 @@ public class OpenMapTilesSchema {
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", "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")), MultiExpression.entry("town_hall",
"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")),
MultiExpression.entry("town_hall",
matchAny("subclass", "townhall", "public_building", "courthouse", "community_centre")),
MultiExpression.entry("golf", matchAny("subclass", "golf", "golf_course", "miniature_golf")),
MultiExpression.entry("fast_food", matchAny("subclass", "fast_food", "food_court")),
@@ -1777,7 +1859,7 @@ public class OpenMapTilesSchema {
/**
* <a href="http://wiki.openstreetmap.org/wiki/Tag:aeroway%3Daerodrome">Aerodrome labels</a>
* <p>
* Generated from <a href="https://github.com/openmaptiles/openmaptiles/blob/v3.12.2/layers/aerodrome_label/aerodrome_label.yaml">aerodrome_label.yaml</a>
* Generated from <a href="https://github.com/openmaptiles/openmaptiles/blob/v3.13/layers/aerodrome_label/aerodrome_label.yaml">aerodrome_label.yaml</a>
*/
public interface AerodromeLabel extends Layer {

View File

@@ -1,5 +1,5 @@
/*
Copyright (c) 2016, KlokanTech.com & OpenMapTiles contributors.
Copyright (c) 2021, MapTiler.com & OpenMapTiles contributors.
All rights reserved.
Code license: BSD 3-Clause License
@@ -50,7 +50,7 @@ import java.util.Map;
/**
* OSM element parsers generated from the <a href="https://github.com/omniscale/imposm3">imposm3</a> table definitions
* in the <a href="https://github.com/openmaptiles/openmaptiles/blob/v3.12.2/openmaptiles.yaml">OpenMapTiles vector tile
* in the <a href="https://github.com/openmaptiles/openmaptiles/blob/v3.13/openmaptiles.yaml">OpenMapTiles vector tile
* schema</a>.
* <p>
* These filter and parse the raw OSM key/value attribute pairs on tags into records with fields that match the columns
@@ -75,7 +75,7 @@ public class Tables {
}
/** The {@code rowClass} of an imposm3 table row and its constructor coerced to a {@link Constructor}. */
public static record RowClassAndConstructor(
public record RowClassAndConstructor(
Class<? extends Row> rowClass,
Constructor create
) {}
@@ -89,31 +89,31 @@ public class Tables {
}
/** The {@code handlerClass} of a layer handler and it's {@code process} method coerced to a {@link RowHandler}. */
public static record RowHandlerAndClass<T extends Row>(
public record RowHandlerAndClass<T extends Row>(
Class<?> handlerClass,
RowHandler<T> handler
) {}
/** An OSM element that would appear in the {@code osm_water_polygon} table generated by imposm3. */
public static record OsmWaterPolygon(
public record OsmWaterPolygon(
@Override String name, @Override String nameEn, @Override String nameDe, @Override String natural,
@Override String landuse, @Override String waterway, @Override boolean isIntermittent, @Override boolean isTunnel,
@Override boolean isBridge, @Override SourceFeature source
) implements Row, WithName, WithNameEn, WithNameDe, WithNatural, WithLanduse, WithWaterway, WithIsIntermittent,
WithIsTunnel, WithIsBridge, WithSource {
@Override String landuse, @Override String waterway, @Override String water, @Override boolean isIntermittent,
@Override boolean isTunnel, @Override boolean isBridge, @Override SourceFeature source
) implements Row, WithName, WithNameEn, WithNameDe, WithNatural, WithLanduse, WithWaterway, 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.getBoolean("intermittent"), source.getBoolean("tunnel"), source.getBoolean("bridge"), source);
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"),
matchAny("waterway", "river", "riverbank", "stream", "canal", "drain", "ditch", "dock")),
not(matchAny("covered", "yes")), matchType("polygon"));
matchAny("natural", "water", "bay", "spring"), matchAny("waterway", "riverbank", "dock"),
matchAny("water", "river")), not(matchAny("covered", "yes")), matchType("polygon"));
/**
* Interface for layer implementations to extend to subscribe to OSM elements filtered and parsed as {@link
@@ -126,7 +126,7 @@ public class Tables {
}
/** An OSM element that would appear in the {@code osm_waterway_linestring} table generated by imposm3. */
public static record OsmWaterwayLinestring(
public record OsmWaterwayLinestring(
@Override String waterway, @Override String name, @Override String nameEn, @Override String nameDe,
@Override boolean isTunnel, @Override boolean isBridge, @Override boolean isIntermittent,
@Override SourceFeature source
@@ -154,7 +154,7 @@ public class Tables {
}
/** An OSM element that would appear in the {@code osm_landcover_polygon} table generated by imposm3. */
public static record OsmLandcoverPolygon(
public record OsmLandcoverPolygon(
@Override String subclass, @Override String mappingKey, @Override SourceFeature source
) implements Row, WithSubclass, WithMappingKey, WithSource {
@@ -165,7 +165,7 @@ public class Tables {
/** Imposm3 "mapping" to filter OSM elements that should appear in this "table". */
public static final Expression MAPPING = and(or(
matchAny("landuse", "allotments", "farm", "farmland", "orchard", "plant_nursery", "vineyard", "grass",
"grassland", "meadow", "forest", "village_green", "recreation_ground", "park"),
"grassland", "meadow", "forest", "village_green", "recreation_ground"),
matchAny("natural", "wood", "wetland", "fell", "grassland", "heath", "scrub", "tundra", "glacier", "bare_rock",
"scree", "beach", "sand", "dune"), matchAny("leisure", "park", "garden", "golf_course"),
matchAny("wetland", "bog", "swamp", "wet_meadow", "marsh", "reedbed", "saltern", "tidalflat", "saltmarsh",
@@ -182,7 +182,7 @@ public class Tables {
}
/** An OSM element that would appear in the {@code osm_landuse_polygon} table generated by imposm3. */
public static record OsmLandusePolygon(
public record OsmLandusePolygon(
@Override String landuse, @Override String amenity, @Override String leisure, @Override String tourism,
@Override String place, @Override String waterway, @Override SourceFeature source
) implements Row, WithLanduse, WithAmenity, WithLeisure, WithTourism, WithPlace, WithWaterway, WithSource {
@@ -196,9 +196,10 @@ public class Tables {
public static final Expression MAPPING = and(or(
matchAny("landuse", "railway", "cemetery", "military", "residential", "commercial", "industrial", "garages",
"retail"),
matchAny("amenity", "bus_station", "school", "university", "kindergarten", "college", "library", "hospital"),
matchAny("leisure", "stadium", "pitch", "playground", "track"), matchAny("tourism", "theme_park", "zoo"),
matchAny("place", "suburb", "quarter", "neighbourhood"), matchAny("waterway", "dam")), matchType("polygon"));
matchAny("amenity", "bus_station", "school", "university", "kindergarten", "college", "library", "hospital",
"grave_yard"), matchAny("leisure", "stadium", "pitch", "playground", "track"),
matchAny("tourism", "theme_park", "zoo"), matchAny("place", "suburb", "quarter", "neighbourhood"),
matchAny("waterway", "dam")), matchType("polygon"));
/**
* Interface for layer implementations to extend to subscribe to OSM elements filtered and parsed as {@link
@@ -211,7 +212,7 @@ public class Tables {
}
/** An OSM element that would appear in the {@code osm_peak_point} table generated by imposm3. */
public static record OsmPeakPoint(
public record OsmPeakPoint(
@Override String name, @Override String nameEn, @Override String nameDe, @Override String ele,
@Override String wikipedia, @Override SourceFeature source
) implements Row, WithName, WithNameEn, WithNameDe, WithEle, WithWikipedia, WithSource {
@@ -222,7 +223,7 @@ public class Tables {
}
/** Imposm3 "mapping" to filter OSM elements that should appear in this "table". */
public static final Expression MAPPING = and(matchAny("natural", "peak", "volcano"), matchType("point"));
public static final Expression MAPPING = and(matchAny("natural", "peak", "volcano", "saddle"), matchType("point"));
/**
* Interface for layer implementations to extend to subscribe to OSM elements filtered and parsed as {@link
@@ -234,8 +235,33 @@ public class Tables {
}
}
/** An OSM element that would appear in the {@code osm_mountain_linestring} table generated by imposm3. */
public record OsmMountainLinestring(
@Override String name, @Override String nameEn, @Override String nameDe, @Override String wikipedia,
@Override SourceFeature source
) implements Row, WithName, WithNameEn, WithNameDe, WithWikipedia, WithSource {
public OsmMountainLinestring(SourceFeature source, String mappingKey) {
this(source.getString("name"), source.getString("name:en"), source.getString("name:de"),
source.getString("wikipedia"), source);
}
/** Imposm3 "mapping" to filter OSM elements that should appear in this "table". */
public static final Expression MAPPING = and(matchAny("natural", "ridge", "cliff", "arete"),
matchType("linestring"));
/**
* Interface for layer implementations to extend to subscribe to OSM elements filtered and parsed as {@link
* OsmMountainLinestring}.
*/
public interface Handler {
void process(OsmMountainLinestring element, FeatureCollector features);
}
}
/** An OSM element that would appear in the {@code osm_park_polygon} table generated by imposm3. */
public static record OsmParkPolygon(
public record OsmParkPolygon(
@Override String name, @Override String nameEn, @Override String nameDe, @Override String landuse,
@Override String leisure, @Override String boundary, @Override String protectionTitle,
@Override SourceFeature source
@@ -264,7 +290,7 @@ public class Tables {
}
/** An OSM element that would appear in the {@code osm_aeroway_polygon} table generated by imposm3. */
public static record OsmAerowayPolygon(
public record OsmAerowayPolygon(
@Override String ref, @Override String aeroway, @Override SourceFeature source
) implements Row, WithRef, WithAeroway, WithSource {
@@ -289,7 +315,7 @@ public class Tables {
}
/** An OSM element that would appear in the {@code osm_aeroway_linestring} table generated by imposm3. */
public static record OsmAerowayLinestring(
public record OsmAerowayLinestring(
@Override String ref, @Override String aeroway, @Override SourceFeature source
) implements Row, WithRef, WithAeroway, WithSource {
@@ -311,7 +337,7 @@ public class Tables {
}
/** An OSM element that would appear in the {@code osm_aeroway_point} table generated by imposm3. */
public static record OsmAerowayPoint(
public record OsmAerowayPoint(
@Override String ref, @Override String aeroway, @Override SourceFeature source
) implements Row, WithRef, WithAeroway, WithSource {
@@ -333,18 +359,19 @@ public class Tables {
}
/** An OSM element that would appear in the {@code osm_highway_linestring} table generated by imposm3. */
public static record OsmHighwayLinestring(
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 usage, @Override String publicTransport,
@Override String manMade, @Override String bicycle, @Override String foot, @Override String horse,
@Override String mtbScale, @Override String surface, @Override SourceFeature source
@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, WithUsage, WithPublicTransport, WithManMade, WithBicycle, WithFoot, WithHorse,
WithMtbScale, WithSurface, WithSource {
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"),
@@ -352,18 +379,19 @@ public class Tables {
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.getString("public_transport"),
source.getString("man_made"), source.getString("bicycle"), source.getString("foot"), source.getString("horse"),
source.getString("mtb:scale"), source.getString("surface"), source);
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);
}
/** Imposm3 "mapping" to filter OSM elements that should appear in this "table". */
public static final Expression MAPPING = and(or(
matchAny("highway", "motorway", "motorway_link", "trunk", "trunk_link", "primary", "primary_link", "secondary",
"secondary_link", "tertiary", "tertiary_link", "unclassified", "residential", "living_street", "road",
"pedestrian", "path", "footway", "cycleway", "steps", "bridleway", "corridor", "service", "track", "raceway",
"construction"), matchAny("public_transport", "platform"), matchAny("man_made", "pier")),
matchType("linestring"));
matchAny("highway", "motorway", "motorway_link", "trunk", "trunk_link", "primary", "primary_link", "secondary",
"secondary_link", "tertiary", "tertiary_link", "unclassified", "residential", "living_street", "road",
"pedestrian", "path", "footway", "cycleway", "steps", "bridleway", "corridor", "service", "track", "raceway",
"busway", "construction"), matchAny("public_transport", "platform"), matchAny("man_made", "pier"),
matchAny("service", "driveway", "parking_aisle")), matchType("linestring"));
/**
* Interface for layer implementations to extend to subscribe to OSM elements filtered and parsed as {@link
@@ -376,7 +404,7 @@ public class Tables {
}
/** An OSM element that would appear in the {@code osm_railway_linestring} table generated by imposm3. */
public static record OsmRailwayLinestring(
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,
@@ -411,7 +439,7 @@ public class Tables {
}
/** An OSM element that would appear in the {@code osm_aerialway_linestring} table generated by imposm3. */
public static record OsmAerialwayLinestring(
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,
@@ -442,7 +470,7 @@ public class Tables {
}
/** An OSM element that would appear in the {@code osm_shipway_linestring} table generated by imposm3. */
public static record OsmShipwayLinestring(
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,
@@ -472,17 +500,17 @@ public class Tables {
}
/** An OSM element that would appear in the {@code osm_highway_polygon} table generated by imposm3. */
public static record OsmHighwayPolygon(
public record OsmHighwayPolygon(
@Override String highway, @Override int zOrder, @Override long layer, @Override long level,
@Override boolean indoor, @Override boolean isArea, @Override String publicTransport, @Override String manMade,
@Override SourceFeature source
@Override String service, @Override SourceFeature source
) implements Row, WithHighway, WithZOrder, WithLayer, WithLevel, WithIndoor, WithIsArea, WithPublicTransport,
WithManMade, WithSource {
WithManMade, WithService, WithSource {
public OsmHighwayPolygon(SourceFeature source, String mappingKey) {
this(source.getString("highway"), source.getWayZorder(), source.getLong("layer"), source.getLong("level"),
source.getBoolean("indoor"), source.getBoolean("area"), source.getString("public_transport"),
source.getString("man_made"), source);
source.getString("man_made"), source.getString("service"), source);
}
/** Imposm3 "mapping" to filter OSM elements that should appear in this "table". */
@@ -500,8 +528,34 @@ public class Tables {
}
}
/** An OSM element that would appear in the {@code osm_highway_point} table generated by imposm3. */
public record OsmHighwayPoint(
@Override String highway, @Override int zOrder, @Override long layer, @Override long level, @Override String name,
@Override String nameEn, @Override String nameDe, @Override String ref, @Override SourceFeature source
) implements Row, WithHighway, WithZOrder, WithLayer, WithLevel, WithName, WithNameEn, WithNameDe, WithRef,
WithSource {
public OsmHighwayPoint(SourceFeature source, String mappingKey) {
this(source.getString("highway"), source.getWayZorder(), source.getLong("layer"), source.getLong("level"),
source.getString("name"), source.getString("name:en"), source.getString("name:de"), source.getString("ref"),
source);
}
/** Imposm3 "mapping" to filter OSM elements that should appear in this "table". */
public static final Expression MAPPING = and(matchAny("highway", "motorway_junction"), matchType("point"));
/**
* Interface for layer implementations to extend to subscribe to OSM elements filtered and parsed as {@link
* OsmHighwayPoint}.
*/
public interface Handler {
void process(OsmHighwayPoint element, FeatureCollector features);
}
}
/** An OSM element that would appear in the {@code osm_building_polygon} table generated by imposm3. */
public static record OsmBuildingPolygon(
public record OsmBuildingPolygon(
@Override String material, @Override String colour, @Override String building, @Override String buildingpart,
@Override String buildingheight, @Override String buildingminHeight, @Override String buildinglevels,
@Override String buildingminLevel, @Override String height, @Override String minHeight, @Override String levels,
@@ -518,9 +572,10 @@ public class Tables {
/** Imposm3 "mapping" to filter OSM elements that should appear in this "table". */
public static final Expression MAPPING = and(
or(matchField("building:part"), matchField("building"), matchAny("aeroway", "terminal", "hangar")),
not(matchAny("building", "no", "none", "No")), not(matchAny("building:part", "no", "none", "No")),
not(matchAny("man_made", "bridge")), matchType("polygon"));
or(matchField("building:part"), matchField("building"), matchAny("aeroway", "terminal", "hangar"),
matchAny("location", "underground")), not(matchAny("building", "no", "none", "No")),
not(matchAny("building:part", "no", "none", "No")), not(matchAny("man_made", "bridge")),
not(matchAny("location", "underground")), matchType("polygon"));
/**
* Interface for layer implementations to extend to subscribe to OSM elements filtered and parsed as {@link
@@ -533,7 +588,7 @@ public class Tables {
}
/** An OSM element that would appear in the {@code osm_marine_point} table generated by imposm3. */
public static record OsmMarinePoint(
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 {
@@ -558,7 +613,7 @@ public class Tables {
}
/** An OSM element that would appear in the {@code osm_continent_point} table generated by imposm3. */
public static record OsmContinentPoint(
public record OsmContinentPoint(
@Override String name, @Override String nameEn, @Override String nameDe, @Override SourceFeature source
) implements Row, WithName, WithNameEn, WithNameDe, WithSource {
@@ -581,7 +636,7 @@ public class Tables {
}
/** An OSM element that would appear in the {@code osm_country_point} table generated by imposm3. */
public static record OsmCountryPoint(
public record OsmCountryPoint(
@Override String name, @Override String nameEn, @Override String nameDe, @Override long rank,
@Override String countryCodeIso31661Alpha2, @Override String iso31661Alpha2, @Override String iso31661,
@Override SourceFeature source
@@ -608,7 +663,7 @@ public class Tables {
}
/** An OSM element that would appear in the {@code osm_island_polygon} table generated by imposm3. */
public static record OsmIslandPolygon(
public record OsmIslandPolygon(
@Override String name, @Override String nameEn, @Override String nameDe, @Override long rank,
@Override SourceFeature source
) implements Row, WithName, WithNameEn, WithNameDe, WithRank, WithSource {
@@ -632,7 +687,7 @@ public class Tables {
}
/** An OSM element that would appear in the {@code osm_island_point} table generated by imposm3. */
public static record OsmIslandPoint(
public record OsmIslandPoint(
@Override String name, @Override String nameEn, @Override String nameDe, @Override long rank,
@Override SourceFeature source
) implements Row, WithName, WithNameEn, WithNameDe, WithRank, WithSource {
@@ -656,20 +711,22 @@ public class Tables {
}
/** An OSM element that would appear in the {@code osm_state_point} table generated by imposm3. */
public static record OsmStatePoint(
@Override String name, @Override String nameEn, @Override String nameDe, @Override String isInCountry,
@Override String isInCountryCode, @Override String ref, @Override long rank, @Override SourceFeature source
) implements Row, WithName, WithNameEn, WithNameDe, WithIsInCountry, WithIsInCountryCode, WithRef, WithRank,
WithSource {
public record OsmStatePoint(
@Override String name, @Override String nameEn, @Override String nameDe, @Override String place,
@Override String isInCountry, @Override String isInCountryCode, @Override String ref, @Override long rank,
@Override SourceFeature source
) implements Row, WithName, WithNameEn, WithNameDe, WithPlace, WithIsInCountry, WithIsInCountryCode, WithRef,
WithRank, WithSource {
public OsmStatePoint(SourceFeature source, String mappingKey) {
this(source.getString("name"), source.getString("name:en"), source.getString("name:de"),
source.getString("is_in:country"), source.getString("is_in:country_code"), source.getString("ref"),
source.getLong("rank"), source);
source.getString("place"), source.getString("is_in:country"), source.getString("is_in:country_code"),
source.getString("ref"), source.getLong("rank"), source);
}
/** Imposm3 "mapping" to filter OSM elements that should appear in this "table". */
public static final Expression MAPPING = and(matchAny("place", "state"), matchField("name"), matchType("point"));
public static final Expression MAPPING = and(matchAny("place", "state", "province"), matchField("name"),
matchType("point"));
/**
* Interface for layer implementations to extend to subscribe to OSM elements filtered and parsed as {@link
@@ -682,7 +739,7 @@ public class Tables {
}
/** An OSM element that would appear in the {@code osm_city_point} table generated by imposm3. */
public static record OsmCityPoint(
public record OsmCityPoint(
@Override String name, @Override String nameEn, @Override String nameDe, @Override String place,
@Override long population, @Override String capital, @Override long rank, @Override SourceFeature source
) implements Row, WithName, WithNameEn, WithNameDe, WithPlace, WithPopulation, WithCapital, WithRank, WithSource {
@@ -709,7 +766,7 @@ public class Tables {
}
/** An OSM element that would appear in the {@code osm_housenumber_point} table generated by imposm3. */
public static record OsmHousenumberPoint(@Override String housenumber, @Override SourceFeature source) implements Row,
public record OsmHousenumberPoint(@Override String housenumber, @Override SourceFeature source) implements Row,
WithHousenumber, WithSource {
public OsmHousenumberPoint(SourceFeature source, String mappingKey) {
@@ -731,7 +788,7 @@ public class Tables {
}
/** An OSM element that would appear in the {@code osm_poi_point} table generated by imposm3. */
public static record OsmPoiPoint(
public record OsmPoiPoint(
@Override String name, @Override String nameEn, @Override String nameDe, @Override String subclass,
@Override String mappingKey, @Override String station, @Override String funicular, @Override String information,
@Override String uicRef, @Override String religion, @Override long level, @Override boolean indoor,
@@ -751,9 +808,9 @@ public class Tables {
public static final Expression MAPPING = and(or(matchAny("aerialway", "station"),
matchAny("amenity", "arts_centre", "bank", "bar", "bbq", "bicycle_parking", "bicycle_rental", "biergarten",
"bus_station", "cafe", "cinema", "clinic", "college", "community_centre", "courthouse", "dentist", "doctors",
"drinking_water", "embassy", "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", "post_box", "post_office", "prison", "pub",
"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", "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",
@@ -762,17 +819,18 @@ public class Tables {
matchAny("landuse", "basin", "brownfield", "cemetery", "reservoir", "winter_sports"),
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("railway", "halt", "station", "subway_entrance", "train_station_entrance", "tram_stop"),
"water_park"), matchAny("office", "diplomatic"),
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",
"chemist", "chocolate", "clothes", "coffee", "computer", "confectionery", "convenience", "copyshop",
"cosmetics", "deli", "delicatessen", "department_store", "doityourself", "dry_cleaning", "electronics",
"erotic", "fabric", "florist", "frozen_food", "furniture", "garden_centre", "general", "gift", "greengrocer",
"hairdresser", "hardware", "hearing_aids", "hifi", "ice_cream", "interior_decoration", "jewelry", "kiosk",
"lamps", "laundry", "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"),
"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"),
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",
@@ -798,7 +856,7 @@ public class Tables {
}
/** An OSM element that would appear in the {@code osm_poi_polygon} table generated by imposm3. */
public static record OsmPoiPolygon(
public record OsmPoiPolygon(
@Override String name, @Override String nameEn, @Override String nameDe, @Override String subclass,
@Override String mappingKey, @Override String station, @Override String funicular, @Override String information,
@Override String uicRef, @Override String religion, @Override long level, @Override boolean indoor,
@@ -818,9 +876,9 @@ public class Tables {
public static final Expression MAPPING = and(or(matchAny("aerialway", "station"),
matchAny("amenity", "arts_centre", "bank", "bar", "bbq", "bicycle_parking", "bicycle_rental", "biergarten",
"bus_station", "cafe", "cinema", "clinic", "college", "community_centre", "courthouse", "dentist", "doctors",
"drinking_water", "embassy", "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", "post_box", "post_office", "prison", "pub",
"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", "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",
@@ -829,17 +887,18 @@ public class Tables {
matchAny("landuse", "basin", "brownfield", "cemetery", "reservoir", "winter_sports"),
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("railway", "halt", "station", "subway_entrance", "train_station_entrance", "tram_stop"),
"water_park"), matchAny("office", "diplomatic"),
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",
"chemist", "chocolate", "clothes", "coffee", "computer", "confectionery", "convenience", "copyshop",
"cosmetics", "deli", "delicatessen", "department_store", "doityourself", "dry_cleaning", "electronics",
"erotic", "fabric", "florist", "frozen_food", "furniture", "garden_centre", "general", "gift", "greengrocer",
"hairdresser", "hardware", "hearing_aids", "hifi", "ice_cream", "interior_decoration", "jewelry", "kiosk",
"lamps", "laundry", "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"),
"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"),
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",
@@ -865,7 +924,7 @@ public class Tables {
}
/** An OSM element that would appear in the {@code osm_aerodrome_label_point} table generated by imposm3. */
public static record OsmAerodromeLabelPoint(
public record OsmAerodromeLabelPoint(
@Override String name, @Override String nameEn, @Override String nameDe, @Override String aerodromeType,
@Override String aerodrome, @Override String military, @Override String iata, @Override String icao,
@Override String ele, @Override SourceFeature source
@@ -892,6 +951,12 @@ public class Tables {
}
}
/** Rows with a String access attribute. */
public interface WithAccess {
String access();
}
/** Rows with a long adminLevel attribute. */
public interface WithAdminLevel {
@@ -1006,18 +1071,18 @@ public class Tables {
String countryCodeIso31661Alpha2();
}
/** Rows with a String disputedBy attribute. */
public interface WithDisputedBy {
String disputedBy();
}
/** Rows with a String ele attribute. */
public interface WithEle {
String ele();
}
/** Rows with a boolean expressway attribute. */
public interface WithExpressway {
boolean expressway();
}
/** Rows with a String foot attribute. */
public interface WithFoot {
@@ -1246,6 +1311,12 @@ public class Tables {
String network();
}
/** Rows with a String osmcSymbol attribute. */
public interface WithOsmcSymbol {
String osmcSymbol();
}
/** Rows with a String place attribute. */
public interface WithPlace {
@@ -1342,6 +1413,12 @@ public class Tables {
String relminLevel();
}
/** Rows with a String sacScale attribute. */
public interface WithSacScale {
String sacScale();
}
/** Rows with a String service attribute. */
public interface WithService {
@@ -1390,6 +1467,12 @@ public class Tables {
String surface();
}
/** Rows with a boolean toll attribute. */
public interface WithToll {
boolean toll();
}
/** Rows with a String tourism attribute. */
public interface WithTourism {
@@ -1408,6 +1491,12 @@ public class Tables {
String usage();
}
/** Rows with a String water attribute. */
public interface WithWater {
String water();
}
/** Rows with a String waterway attribute. */
public interface WithWaterway {
@@ -1437,6 +1526,8 @@ public class Tables {
MultiExpression.entry(new RowClassAndConstructor(OsmLandusePolygon.class, OsmLandusePolygon::new),
OsmLandusePolygon.MAPPING),
MultiExpression.entry(new RowClassAndConstructor(OsmPeakPoint.class, OsmPeakPoint::new), OsmPeakPoint.MAPPING),
MultiExpression.entry(new RowClassAndConstructor(OsmMountainLinestring.class, OsmMountainLinestring::new),
OsmMountainLinestring.MAPPING),
MultiExpression.entry(new RowClassAndConstructor(OsmParkPolygon.class, OsmParkPolygon::new),
OsmParkPolygon.MAPPING),
MultiExpression.entry(new RowClassAndConstructor(OsmAerowayPolygon.class, OsmAerowayPolygon::new),
@@ -1455,6 +1546,8 @@ public class Tables {
OsmShipwayLinestring.MAPPING),
MultiExpression.entry(new RowClassAndConstructor(OsmHighwayPolygon.class, OsmHighwayPolygon::new),
OsmHighwayPolygon.MAPPING),
MultiExpression.entry(new RowClassAndConstructor(OsmHighwayPoint.class, OsmHighwayPoint::new),
OsmHighwayPoint.MAPPING),
MultiExpression.entry(new RowClassAndConstructor(OsmBuildingPolygon.class, OsmBuildingPolygon::new),
OsmBuildingPolygon.MAPPING),
MultiExpression.entry(new RowClassAndConstructor(OsmMarinePoint.class, OsmMarinePoint::new),
@@ -1504,6 +1597,10 @@ public class Tables {
result.computeIfAbsent(OsmPeakPoint.class, cls -> new ArrayList<>())
.add(new RowHandlerAndClass<>(typedHandler.getClass(), typedHandler::process));
}
if (handler instanceof OsmMountainLinestring.Handler typedHandler) {
result.computeIfAbsent(OsmMountainLinestring.class, cls -> new ArrayList<>())
.add(new RowHandlerAndClass<>(typedHandler.getClass(), typedHandler::process));
}
if (handler instanceof OsmParkPolygon.Handler typedHandler) {
result.computeIfAbsent(OsmParkPolygon.class, cls -> new ArrayList<>())
.add(new RowHandlerAndClass<>(typedHandler.getClass(), typedHandler::process));
@@ -1540,6 +1637,10 @@ public class Tables {
result.computeIfAbsent(OsmHighwayPolygon.class, cls -> new ArrayList<>())
.add(new RowHandlerAndClass<>(typedHandler.getClass(), typedHandler::process));
}
if (handler instanceof OsmHighwayPoint.Handler typedHandler) {
result.computeIfAbsent(OsmHighwayPoint.class, cls -> new ArrayList<>())
.add(new RowHandlerAndClass<>(typedHandler.getClass(), typedHandler::process));
}
if (handler instanceof OsmBuildingPolygon.Handler typedHandler) {
result.computeIfAbsent(OsmBuildingPolygon.class, cls -> new ArrayList<>())
.add(new RowHandlerAndClass<>(typedHandler.getClass(), typedHandler::process));

View File

@@ -1,5 +1,5 @@
/*
Copyright (c) 2016, KlokanTech.com & OpenMapTiles contributors.
Copyright (c) 2021, MapTiler.com & OpenMapTiles contributors.
All rights reserved.
Code license: BSD 3-Clause License
@@ -36,6 +36,7 @@ See https://github.com/openmaptiles/openmaptiles/blob/master/LICENSE.md for deta
package com.onthegomap.planetiler.basemap.layers;
import static com.onthegomap.planetiler.basemap.util.Utils.nullIfEmpty;
import static com.onthegomap.planetiler.basemap.util.Utils.nullOrEmpty;
import com.onthegomap.planetiler.FeatureCollector;
import com.onthegomap.planetiler.basemap.generated.OpenMapTilesSchema;
@@ -67,13 +68,15 @@ public class AerodromeLabel implements
@Override
public void process(Tables.OsmAerodromeLabelPoint element, FeatureCollector features) {
String clazz = classLookup.getOrElse(element.source(), FieldValues.CLASS_OTHER);
boolean important = !nullOrEmpty(element.iata()) && FieldValues.CLASS_INTERNATIONAL.equals(clazz);
features.centroid(LAYER_NAME)
.setBufferPixels(BUFFER_SIZE)
.setMinZoom(10)
.setMinZoom(important ? 8 : 10)
.putAttrs(LanguageUtils.getNames(element.source().tags(), translations))
.putAttrs(Utils.elevationTags(element.ele()))
.setAttr(Fields.IATA, nullIfEmpty(element.iata()))
.setAttr(Fields.ICAO, nullIfEmpty(element.icao()))
.setAttr(Fields.CLASS, classLookup.getOrElse(element.source(), FieldValues.CLASS_OTHER));
.setAttr(Fields.CLASS, clazz);
}
}

View File

@@ -1,5 +1,5 @@
/*
Copyright (c) 2016, KlokanTech.com & OpenMapTiles contributors.
Copyright (c) 2021, MapTiler.com & OpenMapTiles contributors.
All rights reserved.
Code license: BSD 3-Clause License

View File

@@ -1,5 +1,5 @@
/*
Copyright (c) 2016, KlokanTech.com & OpenMapTiles contributors.
Copyright (c) 2021, MapTiler.com & OpenMapTiles contributors.
All rights reserved.
Code license: BSD 3-Clause License
@@ -168,7 +168,9 @@ public class Boundary implements
: 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) : null;
yield minZoom != null && minZoom <= 7 ? new BoundaryInfo(4, 1, 4) :
minZoom != null && minZoom <= 7.7 ? new BoundaryInfo(4, 4, 4) :
null;
}
default -> null;
};

View File

@@ -1,5 +1,5 @@
/*
Copyright (c) 2016, KlokanTech.com & OpenMapTiles contributors.
Copyright (c) 2021, MapTiler.com & OpenMapTiles contributors.
All rights reserved.
Code license: BSD 3-Clause License

View File

@@ -1,5 +1,5 @@
/*
Copyright (c) 2016, KlokanTech.com & OpenMapTiles contributors.
Copyright (c) 2021, MapTiler.com & OpenMapTiles contributors.
All rights reserved.
Code license: BSD 3-Clause License

View File

@@ -1,5 +1,5 @@
/*
Copyright (c) 2016, KlokanTech.com & OpenMapTiles contributors.
Copyright (c) 2021, MapTiler.com & OpenMapTiles contributors.
All rights reserved.
Code license: BSD 3-Clause License
@@ -152,14 +152,14 @@ public class Landcover implements
long numPoints = num.longValue();
if (zoom >= 10) {
if (WOOD_OR_FOREST.contains(subclass) && numPoints < 300) {
attrs.put(tempGroupKey, numPoints < 50 ? "<50" : "<300");
attrs.put(tempGroupKey, "<300");
toMerge.add(item);
} else { // don't merge
result.add(item);
}
} else if (zoom == 9) {
if (WOOD_OR_FOREST.contains(subclass)) {
attrs.put(tempGroupKey, numPoints < 50 ? "<50" : numPoints < 300 ? "<300" : ">300");
attrs.put(tempGroupKey, numPoints < 300 ? "<300" : ">300");
toMerge.add(item);
} else { // don't merge
result.add(item);

View File

@@ -1,5 +1,5 @@
/*
Copyright (c) 2016, KlokanTech.com & OpenMapTiles contributors.
Copyright (c) 2021, MapTiler.com & OpenMapTiles contributors.
All rights reserved.
Code license: BSD 3-Clause License
@@ -101,6 +101,9 @@ public class Landuse implements
nullIfEmpty(element.waterway())
);
if (clazz != null) {
if ("grave_yard".equals(clazz)) {
clazz = FieldValues.CLASS_CEMETERY;
}
features.polygon(LAYER_NAME).setBufferPixels(BUFFER_SIZE)
.setAttr(Fields.CLASS, clazz)
.setMinPixelSizeOverrides(MIN_PIXEL_SIZE_THRESHOLDS)

View File

@@ -1,5 +1,5 @@
/*
Copyright (c) 2016, KlokanTech.com & OpenMapTiles contributors.
Copyright (c) 2021, MapTiler.com & OpenMapTiles contributors.
All rights reserved.
Code license: BSD 3-Clause License
@@ -48,12 +48,18 @@ import com.onthegomap.planetiler.basemap.generated.Tables;
import com.onthegomap.planetiler.basemap.util.LanguageUtils;
import com.onthegomap.planetiler.config.PlanetilerConfig;
import com.onthegomap.planetiler.geo.GeometryException;
import com.onthegomap.planetiler.reader.SourceFeature;
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.concurrent.atomic.AtomicBoolean;
import org.locationtech.jts.geom.Geometry;
import org.locationtech.jts.geom.Point;
import org.locationtech.jts.geom.prep.PreparedGeometry;
import org.locationtech.jts.geom.prep.PreparedGeometryFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Defines the logic for generating map elements for mountain peak label points in the {@code mountain_peak} layer from
@@ -63,8 +69,10 @@ import org.locationtech.jts.geom.Point;
* mountain_peak sql files</a>.
*/
public class MountainPeak implements
BasemapProfile.NaturalEarthProcessor,
OpenMapTilesSchema.MountainPeak,
Tables.OsmPeakPoint.Handler,
Tables.OsmMountainLinestring.Handler,
BasemapProfile.FeaturePostProcessor {
/*
@@ -73,20 +81,40 @@ public class MountainPeak implements
* label density by only taking the top 5 most important mountain peaks within each 100x100px
* square.
*/
private static final Logger LOGGER = LoggerFactory.getLogger(TransportationName.class);
private final Translations translations;
private final Stats stats;
// keep track of areas that prefer feet to meters to set customary_ft=1 (just U.S.)
private PreparedGeometry unitedStates = null;
private final AtomicBoolean loggedNoUS = new AtomicBoolean(false);
public MountainPeak(Translations translations, PlanetilerConfig config, Stats stats) {
this.translations = translations;
this.stats = stats;
}
@Override
public void processNaturalEarth(String table, SourceFeature feature, FeatureCollector features) {
if ("ne_10m_admin_0_countries".equals(table) && feature.hasTag("iso_a2", "US")) {
// multiple threads call this method concurrently, US polygon *should* only be found
// once, but just to be safe synchronize updates to that field
synchronized (this) {
try {
Geometry boundary = feature.polygon();
unitedStates = PreparedGeometryFactory.prepare(boundary);
} catch (GeometryException e) {
LOGGER.error("Failed to get United States Polygon for mountain_peak layer: " + e);
}
}
}
}
@Override
public void process(Tables.OsmPeakPoint element, FeatureCollector features) {
Integer meters = Parse.parseIntSubstring(element.ele());
if (meters != null && Math.abs(meters) < 10_000) {
features.point(LAYER_NAME)
var feature = features.point(LAYER_NAME)
.setAttr(Fields.CLASS, element.source().getTag("natural"))
.putAttrs(LanguageUtils.getNames(element.source().tags(), translations))
.putAttrs(elevationTags(meters))
@@ -101,9 +129,46 @@ public class MountainPeak implements
// in adjacent tiles. postProcess() will remove anything outside the desired buffer.
.setBufferPixels(100)
.setPointLabelGridSizeAndLimit(13, 100, 5);
if (peakInAreaUsingFeet(element)) {
feature.setAttr(Fields.CUSTOMARY_FT, 1);
}
}
}
@Override
public void process(Tables.OsmMountainLinestring element, FeatureCollector features) {
// TODO rank is approximate to sort important/named ridges before others, should switch to labelgrid for linestrings later
int rank = 3 -
(nullIfEmpty(element.wikipedia()) != null ? 1 : 0) -
(nullIfEmpty(element.name()) != null ? 1 : 0);
features.line(LAYER_NAME)
.setAttr(Fields.CLASS, element.source().getTag("natural"))
.setAttr(Fields.RANK, rank)
.putAttrs(LanguageUtils.getNames(element.source().tags(), translations))
.setSortKey(rank)
.setMinZoom(13)
.setBufferPixels(100);
}
/** Returns true if {@code element} is a point in an area where feet are used insead of meters (the US). */
private boolean peakInAreaUsingFeet(Tables.OsmPeakPoint element) {
if (unitedStates == null) {
if (!loggedNoUS.get() && loggedNoUS.compareAndSet(false, true)) {
LOGGER.warn("No US polygon for inferring mountain_peak customary_ft tag");
}
} else {
try {
Geometry wayGeometry = element.source().worldGeometry();
return unitedStates.intersects(wayGeometry);
} catch (GeometryException e) {
e.log(stats, "omt_mountain_peak_us_test",
"Unable to test mountain_peak against US polygon: " + element.source().id());
}
}
return false;
}
@Override
public List<VectorTile.Feature> postProcess(int zoom, List<VectorTile.Feature> items) {
LongIntMap groupCounts = new LongIntHashMap();

View File

@@ -1,5 +1,5 @@
/*
Copyright (c) 2016, KlokanTech.com & OpenMapTiles contributors.
Copyright (c) 2021, MapTiler.com & OpenMapTiles contributors.
All rights reserved.
Code license: BSD 3-Clause License
@@ -42,6 +42,7 @@ import static com.onthegomap.planetiler.collection.FeatureGroup.SORT_KEY_BITS;
import com.carrotsearch.hppc.LongIntHashMap;
import com.carrotsearch.hppc.LongIntMap;
import com.onthegomap.planetiler.FeatureCollector;
import com.onthegomap.planetiler.FeatureMerge;
import com.onthegomap.planetiler.VectorTile;
import com.onthegomap.planetiler.basemap.BasemapProfile;
import com.onthegomap.planetiler.basemap.generated.OpenMapTilesSchema;
@@ -101,10 +102,10 @@ public class Park implements
);
// park shape
features.polygon(LAYER_NAME).setBufferPixels(BUFFER_SIZE)
.setAttr(Fields.CLASS, clazz)
var outline = features.polygon(LAYER_NAME).setBufferPixels(BUFFER_SIZE)
.setAttrWithMinzoom(Fields.CLASS, clazz, 5)
.setMinPixelSize(2)
.setMinZoom(6);
.setMinZoom(4);
// park name label point (if it has one)
if (element.name() != null) {
@@ -112,8 +113,13 @@ public class Park implements
double area = element.source().area();
int minzoom = getMinZoomForArea(area);
features.centroid(LAYER_NAME).setBufferPixels(256)
var names = LanguageUtils.getNamesWithoutTranslations(element.source().tags());
outline.putAttrsWithMinzoom(names, 5);
features.pointOnSurface(LAYER_NAME).setBufferPixels(256)
.setAttr(Fields.CLASS, clazz)
.putAttrs(names)
.putAttrs(LanguageUtils.getNames(element.source().tags(), translations))
.setPointLabelGridPixelSize(14, 100)
.setSortKey(SortKey
@@ -132,12 +138,12 @@ public class Park implements
// 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(6, minzoom));
minzoom = Math.min(14, Math.max(5, minzoom));
return minzoom;
}
@Override
public List<VectorTile.Feature> postProcess(int zoom, List<VectorTile.Feature> items) {
public List<VectorTile.Feature> postProcess(int zoom, List<VectorTile.Feature> items) throws GeometryException {
// infer the "rank" attribute from point ordering within each label grid square
LongIntMap counts = new LongIntHashMap();
for (VectorTile.Feature feature : items) {
@@ -147,6 +153,9 @@ public class Park implements
counts.put(feature.group(), count);
}
}
if (zoom <= 4) {
items = FeatureMerge.mergeOverlappingPolygons(items, 0);
}
return items;
}
}

View File

@@ -1,5 +1,5 @@
/*
Copyright (c) 2016, KlokanTech.com & OpenMapTiles contributors.
Copyright (c) 2021, MapTiler.com & OpenMapTiles contributors.
All rights reserved.
Code license: BSD 3-Clause License
@@ -171,7 +171,7 @@ public class Place implements
case "ne_10m_admin_1_states_provinces" -> {
Double scalerank = Parse.parseDoubleOrNull(feature.getTag("scalerank"));
Double labelrank = Parse.parseDoubleOrNull(feature.getTag("labelrank"));
if (scalerank != null && scalerank <= 3 && labelrank != null && labelrank <= 2) {
if (scalerank != null && scalerank <= 6 && labelrank != null && labelrank <= 7) {
states.put(feature.worldGeometry(), new NaturalEarthRegion(
feature.getString("name"), 6,
scalerank,
@@ -265,7 +265,7 @@ public class Place implements
features.point(LAYER_NAME).setBufferPixels(BUFFER_SIZE)
.putAttrs(names)
.setAttr(Fields.CLASS, FieldValues.CLASS_STATE)
.setAttr(Fields.CLASS, element.place())
.setAttr(Fields.RANK, rank)
.setMinZoom(2)
.setSortKey(rank);

View File

@@ -1,5 +1,5 @@
/*
Copyright (c) 2016, KlokanTech.com & OpenMapTiles contributors.
Copyright (c) 2021, MapTiler.com & OpenMapTiles contributors.
All rights reserved.
Code license: BSD 3-Clause License
@@ -36,8 +36,8 @@ See https://github.com/openmaptiles/openmaptiles/blob/master/LICENSE.md for deta
package com.onthegomap.planetiler.basemap.layers;
import static com.onthegomap.planetiler.basemap.util.Utils.coalesce;
import static com.onthegomap.planetiler.basemap.util.Utils.nullIf;
import static com.onthegomap.planetiler.basemap.util.Utils.nullIfEmpty;
import static com.onthegomap.planetiler.basemap.util.Utils.nullIfLong;
import static com.onthegomap.planetiler.basemap.util.Utils.nullOrEmpty;
import static java.util.Map.entry;
@@ -170,7 +170,7 @@ public class Poi implements
output.setBufferPixels(BUFFER_SIZE)
.setAttr(Fields.CLASS, poiClass)
.setAttr(Fields.SUBCLASS, subclass)
.setAttr(Fields.LAYER, nullIf(element.layer(), 0))
.setAttr(Fields.LAYER, nullIfLong(element.layer(), 0))
.setAttr(Fields.LEVEL, Parse.parseLongOrNull(element.source().getTag("level")))
.setAttr(Fields.INDOOR, element.indoor() ? 1 : null)
.putAttrs(LanguageUtils.getNames(element.source().tags(), translations))

View File

@@ -1,5 +1,5 @@
/*
Copyright (c) 2016, KlokanTech.com & OpenMapTiles contributors.
Copyright (c) 2021, MapTiler.com & OpenMapTiles contributors.
All rights reserved.
Code license: BSD 3-Clause License
@@ -36,6 +36,10 @@ See https://github.com/openmaptiles/openmaptiles/blob/master/LICENSE.md for deta
package com.onthegomap.planetiler.basemap.layers;
import static com.onthegomap.planetiler.basemap.util.Utils.*;
import static com.onthegomap.planetiler.util.MemoryEstimator.CLASS_HEADER_BYTES;
import static com.onthegomap.planetiler.util.MemoryEstimator.POINTER_BYTES;
import static com.onthegomap.planetiler.util.MemoryEstimator.estimateSize;
import static java.util.Map.entry;
import com.onthegomap.planetiler.FeatureCollector;
import com.onthegomap.planetiler.FeatureMerge;
@@ -45,15 +49,32 @@ import com.onthegomap.planetiler.basemap.generated.OpenMapTilesSchema;
import com.onthegomap.planetiler.basemap.generated.Tables;
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.SourceFeature;
import com.onthegomap.planetiler.reader.osm.OsmElement;
import com.onthegomap.planetiler.reader.osm.OsmReader;
import com.onthegomap.planetiler.reader.osm.OsmRelationInfo;
import com.onthegomap.planetiler.stats.Stats;
import com.onthegomap.planetiler.util.MemoryEstimator;
import com.onthegomap.planetiler.util.Parse;
import com.onthegomap.planetiler.util.Translations;
import com.onthegomap.planetiler.util.ZoomFunction;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.locationtech.jts.geom.Geometry;
import org.locationtech.jts.geom.LineString;
import org.locationtech.jts.geom.prep.PreparedGeometry;
import org.locationtech.jts.geom.prep.PreparedGeometryFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Defines the logic for generating map elements for roads, shipways, railroads, and paths in the {@code transportation}
@@ -69,7 +90,9 @@ public class Transportation implements
Tables.OsmRailwayLinestring.Handler,
Tables.OsmShipwayLinestring.Handler,
Tables.OsmHighwayPolygon.Handler,
BasemapProfile.NaturalEarthProcessor,
BasemapProfile.FeaturePostProcessor,
BasemapProfile.OsmRelationPreprocessor,
BasemapProfile.IgnoreWikidata {
/*
@@ -78,6 +101,8 @@ public class Transportation implements
* layer includes names, but less detailed attributes.
*/
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 MultiExpression.Index<String> classMapping = FieldMappings.Class.index();
private static final Set<String> RAILWAY_RAIL_VALUES = Set.of(
FieldValues.SUBCLASS_RAIL,
@@ -108,11 +133,24 @@ public class Transportation implements
"paved", "asphalt", "cobblestone", "concrete", "concrete:lanes", "concrete:plates", "metal",
"paving_stones", "sett", "unhewn_cobblestone", "wood"
);
private static final Set<String> ACCESS_NO_VALUES = Set.of(
"private", "no"
);
private static final ZoomFunction.MeterToPixelThresholds MIN_LENGTH = ZoomFunction.meterThresholds()
.put(7, 50)
.put(6, 100)
.put(5, 500)
.put(4, 1_000);
// ORDER BY network_type, network, LENGTH(ref), ref)
private static final Comparator<RouteRelation> RELATION_ORDERING = Comparator
.<RouteRelation>comparingInt(
r -> r.networkType() != null ? r.networkType.ordinal() : Integer.MAX_VALUE)
.thenComparing(routeRelation -> coalesce(routeRelation.network(), ""))
.thenComparingInt(r -> r.ref().length())
.thenComparing(RouteRelation::ref);
private final AtomicBoolean loggedNoGb = new AtomicBoolean(false);
private final boolean z13Paths;
private PreparedGeometry greatBritain = null;
private final Map<String, Integer> MINZOOMS;
private final Stats stats;
private final PlanetilerConfig config;
@@ -120,30 +158,54 @@ public class Transportation implements
public Transportation(Translations translations, PlanetilerConfig config, Stats stats) {
this.config = config;
this.stats = stats;
boolean z13Paths = config.arguments().getBoolean(
z13Paths = config.arguments().getBoolean(
"transportation_z13_paths",
"transportation(_name) layer: show paths on z13",
true
"transportation(_name) layer: show all paths on z13",
false
);
MINZOOMS = Map.of(
FieldValues.CLASS_TRACK, 14,
FieldValues.CLASS_PATH, z13Paths ? 13 : 14,
FieldValues.CLASS_MINOR, 13,
FieldValues.CLASS_RACEWAY, 12,
FieldValues.CLASS_TERTIARY, 11,
FieldValues.CLASS_SECONDARY, 9,
FieldValues.CLASS_PRIMARY, 7,
FieldValues.CLASS_TRUNK, 5,
FieldValues.CLASS_MOTORWAY, 4
MINZOOMS = Map.ofEntries(
entry(FieldValues.CLASS_PATH, z13Paths ? 13 : 14),
entry(FieldValues.CLASS_TRACK, 14),
entry(FieldValues.CLASS_SERVICE, 13),
entry(FieldValues.CLASS_MINOR, 13),
entry(FieldValues.CLASS_RACEWAY, 12),
entry(FieldValues.CLASS_TERTIARY, 11),
entry(FieldValues.CLASS_BUSWAY, 11),
entry(FieldValues.CLASS_SECONDARY, 9),
entry(FieldValues.CLASS_PRIMARY, 7),
entry(FieldValues.CLASS_TRUNK, 5),
entry(FieldValues.CLASS_MOTORWAY, 4)
);
}
@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
synchronized (this) {
try {
Geometry boundary = feature.polygon().buffer(GeoUtils.metersToPixelAtEquator(0, 10_000) / 256d);
greatBritain = PreparedGeometryFactory.prepare(boundary);
} catch (GeometryException e) {
LOGGER.error("Failed to get Great Britain Polygon: " + e);
}
}
}
}
/** Returns a value for {@code surface} tag constrained to a small set of known values from raw OSM data. */
private static String surface(String value) {
return value == null ? null : SURFACE_PAVED_VALUES.contains(value) ? FieldValues.SURFACE_PAVED :
SURFACE_UNPAVED_VALUES.contains(value) ? FieldValues.SURFACE_UNPAVED : null;
}
/** Returns a value for {@code access} tag constrained to a small set of known values from raw OSM data. */
private static String access(String value) {
return value == null ? null : ACCESS_NO_VALUES.contains(value) ? "no" : null;
}
/** Returns a value for {@code service} tag constrained to a small set of known values from raw OSM data. */
private static String service(String value) {
return (value == null || !SERVICE_VALUES.contains(value)) ? null : value;
@@ -171,68 +233,160 @@ public class Transportation implements
return "footway".equals(highway) || "steps".equals(highway);
}
static boolean isLink(String highway) {
return highway != null && highway.endsWith("_link");
}
private static boolean isResidentialOrUnclassified(String highway) {
return "residential".equals(highway) || "unclassified".equals(highway);
}
private static boolean isDrivewayOrParkingAisle(String service) {
return FieldValues.SERVICE_PARKING_AISLE.equals(service) || FieldValues.SERVICE_DRIVEWAY.equals(service);
}
private static boolean isBridgeOrPier(String manMade) {
return "bridge".equals(manMade) || "pier".equals(manMade);
}
enum RouteNetwork {
US_INTERSTATE("us-interstate"),
US_HIGHWAY("us-highway"),
US_STATE("us-state"),
CA_TRANSCANADA("ca-transcanada"),
GB_MOTORWAY("gb-motorway"),
GB_TRUNK("gb-trunk");
final String name;
RouteNetwork(String name) {
this.name = name;
}
}
@Override
public List<OsmRelationInfo> preprocessOsmRelation(OsmElement.Relation relation) {
if (relation.hasTag("route", "road", "hiking")) {
RouteNetwork networkType = null;
String network = relation.getString("network");
String ref = relation.getString("ref");
if ("US:I".equals(network)) {
networkType = RouteNetwork.US_INTERSTATE;
} else if ("US:US".equals(network)) {
networkType = RouteNetwork.US_HIGHWAY;
} else if (network != null && network.length() == 5 && network.startsWith("US:")) {
networkType = RouteNetwork.US_STATE;
} else if (network != null && network.startsWith("CA:transcanada")) {
networkType = RouteNetwork.CA_TRANSCANADA;
}
int rank = switch (coalesce(network, "")) {
case "iwn", "nwn", "rwn" -> 1;
case "lwn" -> 2;
default -> (relation.hasTag("osmc:symbol") || relation.hasTag("colour")) ? 2 : 3;
};
if (networkType != null || rank < 3) {
return List.of(new RouteRelation(coalesce(ref, ""), network, networkType, (byte) rank, relation.id()));
}
}
return null;
}
List<RouteRelation> getRouteRelations(Tables.OsmHighwayLinestring element) {
String ref = element.ref();
List<OsmReader.RelationMember<RouteRelation>> relations = element.source().relationInfo(RouteRelation.class);
List<RouteRelation> result = new ArrayList<>(relations.size() + 1);
for (var relationMember : relations) {
var relation = relationMember.relation();
// avoid duplicates - list should be very small and usually only one
if (!result.contains(relation)) {
result.add(relation);
}
}
if (ref != null) {
// GB doesn't use regular relations like everywhere else, so if we are
// in GB then use a naming convention instead.
Matcher refMatcher = GREAT_BRITAIN_REF_NETWORK_PATTERN.matcher(ref);
if (refMatcher.find()) {
if (greatBritain == null) {
if (!loggedNoGb.get() && loggedNoGb.compareAndSet(false, true)) {
LOGGER.warn("No GB polygon for inferring route network types");
}
} else {
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";
result.add(new RouteRelation(refMatcher.group(), network, networkType, (byte) -1,
0));
}
} catch (GeometryException e) {
e.log(stats, "omt_transportation_name_gb_test",
"Unable to test highway against GB route network: " + element.source().id());
}
}
}
}
Collections.sort(result);
return result;
}
RouteRelation getRouteRelation(Tables.OsmHighwayLinestring element) {
List<RouteRelation> all = getRouteRelations(element);
return all.isEmpty() ? null : all.get(0);
}
@Override
public void process(Tables.OsmHighwayLinestring element, FeatureCollector features) {
if (element.isArea()) {
return;
}
RouteRelation routeRelation = getRouteRelation(element);
RouteNetwork networkType = routeRelation != null ? routeRelation.networkType : null;
String highway = element.highway();
String highwayClass = highwayClass(element.highway(), element.publicTransport(), element.construction(),
element.manMade());
String service = service(element.service());
if (highwayClass != null) {
int minzoom;
if ("pier".equals(element.manMade())) {
try {
if (element.source().worldGeometry() instanceof LineString lineString && lineString.isClosed()) {
// ignore this because it's a polygon
return;
}
} catch (GeometryException e) {
e.log(stats, "omt_transportation_pier",
"Unable to decode pier geometry for " + element.source().id());
return;
}
minzoom = 13;
} else if (isResidentialOrUnclassified(highway)) {
minzoom = 12;
} else {
String baseClass = highwayClass.replace("_construction", "");
minzoom = MINZOOMS.getOrDefault(baseClass, 12);
if (isPierPolygon(element)) {
return;
}
boolean highwayIsLink = coalesce(highway, "").endsWith("_link");
int minzoom = getMinzoom(element, highwayClass);
if (highwayIsLink) {
minzoom = Math.max(minzoom, 9);
}
boolean highwayRamp = highwayIsLink || "steps".equals(highway);
int rampAboveZ12 = (highwayRamp || element.isRamp()) ? 1 : 0;
int rampBelowZ12 = highwayRamp ? 1 : 0;
boolean highwayRamp = isLink(highway);
Integer rampAboveZ12 = (highwayRamp || element.isRamp()) ? 1 : null;
Integer rampBelowZ12 = highwayRamp ? 1 : null;
FeatureCollector.Feature feature = features.line(LAYER_NAME).setBufferPixels(BUFFER_SIZE)
// main attributes at all zoom levels (used for grouping <= z8)
.setAttr(Fields.CLASS, highwayClass)
.setAttr(Fields.SUBCLASS, highwaySubclass(highwayClass, element.publicTransport(), highway))
.setAttr(Fields.BRUNNEL, brunnel(element.isBridge(), element.isTunnel(), element.isFord()))
// rest at z9+
.setAttrWithMinzoom(Fields.SERVICE, service(element.service()), 12)
.setAttrWithMinzoom(Fields.ONEWAY, element.isOneway(), 12)
.setAttr(Fields.RAMP, minzoom >= 12 ? rampAboveZ12 :
((ZoomFunction<Integer>) z -> z < 9 ? null : z >= 12 ? rampAboveZ12 : rampBelowZ12))
.setAttrWithMinzoom(Fields.LAYER, nullIf(element.layer(), 0), 9)
.setAttr(Fields.NETWORK, networkType != null ? networkType.name : null)
// z8+
.setAttrWithMinzoom(Fields.EXPRESSWAY, element.expressway() && !"motorway".equals(highway) ? 1 : null, 8)
// z9+
.setAttrWithMinzoom(Fields.LAYER, nullIfLong(element.layer(), 0), 9)
.setAttrWithMinzoom(Fields.BICYCLE, nullIfEmpty(element.bicycle()), 9)
.setAttrWithMinzoom(Fields.FOOT, nullIfEmpty(element.foot()), 9)
.setAttrWithMinzoom(Fields.HORSE, nullIfEmpty(element.horse()), 9)
.setAttrWithMinzoom(Fields.MTB_SCALE, nullIfEmpty(element.mtbScale()), 9)
.setAttrWithMinzoom(Fields.ACCESS, access(element.access()), 9)
.setAttrWithMinzoom(Fields.TOLL, element.toll() ? 1 : null, 9)
// sometimes z9+, sometimes z12+
.setAttr(Fields.RAMP, minzoom >= 12 ? rampAboveZ12 :
((ZoomFunction<Integer>) z -> z < 9 ? null : z >= 12 ? rampAboveZ12 : rampBelowZ12))
// z12+
.setAttrWithMinzoom(Fields.SERVICE, service, 12)
.setAttrWithMinzoom(Fields.ONEWAY, nullIfInt(element.isOneway(), 0), 12)
.setAttrWithMinzoom(Fields.SURFACE, surface(element.surface()), 12)
.setMinPixelSize(0) // merge during post-processing, then limit by size
.setSortKey(element.zOrder())
@@ -246,6 +400,53 @@ public class Transportation implements
}
}
int getMinzoom(Tables.OsmHighwayLinestring element, String highwayClass) {
List<RouteRelation> routeRelations = getRouteRelations(element);
int routeRank = 3;
for (var rel : routeRelations) {
if (rel.intRank() < routeRank) {
routeRank = rel.intRank();
}
}
String highway = element.highway();
int minzoom;
if ("pier".equals(element.manMade())) {
minzoom = 13;
} else if (isResidentialOrUnclassified(highway)) {
minzoom = 12;
} else {
String baseClass = highwayClass.replace("_construction", "");
minzoom = switch (baseClass) {
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;
default -> MINZOOMS.get(baseClass);
};
}
if (isLink(highway)) {
minzoom = Math.max(minzoom, 9);
}
return minzoom;
}
private boolean isPierPolygon(Tables.OsmHighwayLinestring element) {
if ("pier".equals(element.manMade())) {
try {
if (element.source().worldGeometry() instanceof LineString lineString && lineString.isClosed()) {
// ignore this because it's a polygon
return true;
}
} catch (GeometryException e) {
e.log(stats, "omt_transportation_pier",
"Unable to decode pier geometry for " + element.source().id());
return true;
}
}
return false;
}
@Override
public void process(Tables.OsmRailwayLinestring element, FeatureCollector features) {
String railway = element.railway();
@@ -268,10 +469,10 @@ public class Transportation implements
.setAttr(Fields.CLASS, clazz)
.setAttr(Fields.SUBCLASS, railway)
.setAttr(Fields.SERVICE, service(service))
.setAttr(Fields.ONEWAY, element.isOneway())
.setAttr(Fields.RAMP, element.isRamp() ? 1 : 0)
.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, nullIf(element.layer(), 0), 9)
.setAttrWithMinzoom(Fields.LAYER, nullIfLong(element.layer(), 0), 9)
.setSortKey(element.zOrder())
.setMinPixelSize(0) // merge during post-processing, then limit by size
.setMinZoom(minzoom);
@@ -284,10 +485,10 @@ public class Transportation implements
.setAttr(Fields.CLASS, "aerialway")
.setAttr(Fields.SUBCLASS, element.aerialway())
.setAttr(Fields.SERVICE, service(element.service()))
.setAttr(Fields.ONEWAY, element.isOneway())
.setAttr(Fields.RAMP, element.isRamp() ? 1 : 0)
.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, nullIf(element.layer(), 0))
.setAttr(Fields.LAYER, nullIfLong(element.layer(), 0))
.setSortKey(element.zOrder())
.setMinPixelSize(0) // merge during post-processing, then limit by size
.setMinZoom(12);
@@ -299,10 +500,10 @@ public class Transportation implements
.setAttr(Fields.CLASS, element.shipway()) // "ferry"
// no subclass
.setAttr(Fields.SERVICE, service(element.service()))
.setAttr(Fields.ONEWAY, element.isOneway())
.setAttr(Fields.RAMP, element.isRamp() ? 1 : 0)
.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, nullIf(element.layer(), 0))
.setAttr(Fields.LAYER, nullIfLong(element.layer(), 0))
.setSortKey(element.zOrder())
.setMinPixelSize(0) // merge during post-processing, then limit by size
.setMinZoom(11);
@@ -320,7 +521,7 @@ public class Transportation implements
.setAttr(Fields.CLASS, highwayClass)
.setAttr(Fields.SUBCLASS, highwaySubclass(highwayClass, element.publicTransport(), element.highway()))
.setAttr(Fields.BRUNNEL, brunnel("bridge".equals(manMade), false, false))
.setAttr(Fields.LAYER, nullIf(element.layer(), 0))
.setAttr(Fields.LAYER, nullIfLong(element.layer(), 0))
.setSortKey(element.zOrder())
.setMinZoom(13);
}
@@ -330,7 +531,37 @@ public class Transportation implements
@Override
public List<VectorTile.Feature> postProcess(int zoom, List<VectorTile.Feature> items) {
double tolerance = config.tolerance(zoom);
double minLength = coalesce(MIN_LENGTH.apply(zoom), config.minFeatureSize(zoom)).doubleValue();
double minLength = coalesce(MIN_LENGTH.apply(zoom), 0).doubleValue();
// TODO preserve direction for one-way?
return FeatureMerge.mergeLineStrings(items, minLength, tolerance, BUFFER_SIZE);
}
/** Information extracted from route relations to use when processing ways in that relation. */
record RouteRelation(
String ref,
String network,
RouteNetwork networkType,
byte rank,
@Override long id
) implements OsmRelationInfo, Comparable<RouteRelation> {
@Override
public long estimateMemoryUsageBytes() {
return CLASS_HEADER_BYTES +
MemoryEstimator.estimateSize(rank) +
POINTER_BYTES + estimateSize(ref) +
POINTER_BYTES + estimateSize(network) +
POINTER_BYTES + // networkType
MemoryEstimator.estimateSizeLong(id);
}
public int intRank() {
return rank;
}
@Override
public int compareTo(RouteRelation o) {
return RELATION_ORDERING.compare(this, o);
}
}
}

View File

@@ -1,5 +1,5 @@
/*
Copyright (c) 2016, KlokanTech.com & OpenMapTiles contributors.
Copyright (c) 2021, MapTiler.com & OpenMapTiles contributors.
All rights reserved.
Code license: BSD 3-Clause License
@@ -38,45 +38,30 @@ package com.onthegomap.planetiler.basemap.layers;
import static com.onthegomap.planetiler.basemap.layers.Transportation.highwayClass;
import static com.onthegomap.planetiler.basemap.layers.Transportation.highwaySubclass;
import static com.onthegomap.planetiler.basemap.layers.Transportation.isFootwayOrSteps;
import static com.onthegomap.planetiler.basemap.util.Utils.brunnel;
import static com.onthegomap.planetiler.basemap.util.Utils.coalesce;
import static com.onthegomap.planetiler.basemap.util.Utils.nullIf;
import static com.onthegomap.planetiler.basemap.util.Utils.nullIfEmpty;
import static com.onthegomap.planetiler.util.MemoryEstimator.CLASS_HEADER_BYTES;
import static com.onthegomap.planetiler.util.MemoryEstimator.POINTER_BYTES;
import static com.onthegomap.planetiler.util.MemoryEstimator.estimateSize;
import static com.onthegomap.planetiler.basemap.util.Utils.*;
import com.carrotsearch.hppc.LongArrayList;
import com.carrotsearch.hppc.LongByteHashMap;
import com.carrotsearch.hppc.LongByteMap;
import com.onthegomap.planetiler.FeatureCollector;
import com.onthegomap.planetiler.FeatureMerge;
import com.onthegomap.planetiler.ForwardingProfile;
import com.onthegomap.planetiler.VectorTile;
import com.onthegomap.planetiler.basemap.BasemapProfile;
import com.onthegomap.planetiler.basemap.generated.OpenMapTilesSchema;
import com.onthegomap.planetiler.basemap.generated.Tables;
import com.onthegomap.planetiler.basemap.util.LanguageUtils;
import com.onthegomap.planetiler.config.PlanetilerConfig;
import com.onthegomap.planetiler.geo.GeoUtils;
import com.onthegomap.planetiler.geo.GeometryException;
import com.onthegomap.planetiler.reader.SourceFeature;
import com.onthegomap.planetiler.reader.osm.OsmElement;
import com.onthegomap.planetiler.reader.osm.OsmReader;
import com.onthegomap.planetiler.reader.osm.OsmRelationInfo;
import com.onthegomap.planetiler.stats.Stats;
import com.onthegomap.planetiler.util.MemoryEstimator;
import com.onthegomap.planetiler.util.Parse;
import com.onthegomap.planetiler.util.Translations;
import com.onthegomap.planetiler.util.ZoomFunction;
import java.util.Comparator;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Function;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.locationtech.jts.geom.Geometry;
import org.locationtech.jts.geom.prep.PreparedGeometry;
import org.locationtech.jts.geom.prep.PreparedGeometryFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Defines the logic for generating map elements for road, shipway, rail, and path names in the {@code
@@ -87,14 +72,17 @@ import org.slf4j.LoggerFactory;
*/
public class TransportationName implements
OpenMapTilesSchema.TransportationName,
Tables.OsmHighwayPoint.Handler,
Tables.OsmHighwayLinestring.Handler,
BasemapProfile.NaturalEarthProcessor,
Tables.OsmAerialwayLinestring.Handler,
Tables.OsmShipwayLinestring.Handler,
BasemapProfile.FeaturePostProcessor,
BasemapProfile.OsmRelationPreprocessor,
BasemapProfile.IgnoreWikidata {
BasemapProfile.IgnoreWikidata,
ForwardingProfile.OsmNodePreprocessor,
ForwardingProfile.OsmWayPreprocessor {
/*
* Generate road names from OSM data. Route network and ref are copied
* Generate road names from OSM data. Route networkType and ref are copied
* from relations that roads are a part of - except in Great Britain which
* uses a naming convention instead of relations.
*
@@ -113,8 +101,6 @@ public class TransportationName implements
private static final String LINK_TEMP_KEY = "__islink";
private static final String RELATION_ID_TEMP_KEY = "__relid";
private static final Logger LOGGER = LoggerFactory.getLogger(TransportationName.class);
private static final Pattern GREAT_BRITAIN_REF_NETWORK_PATTERN = Pattern.compile("^[AM][0-9AM()]+");
private static final ZoomFunction.MeterToPixelThresholds MIN_LENGTH = ZoomFunction.meterThresholds()
.put(6, 20_000)
.put(7, 20_000)
@@ -122,23 +108,23 @@ public class TransportationName implements
.put(9, 8_000)
.put(10, 8_000)
.put(11, 8_000);
private static final Comparator<RouteRelation> RELATION_ORDERING = Comparator
.<RouteRelation>comparingInt(r -> r.network.ordinal())
// TODO also compare network string?
.thenComparingInt(r -> r.ref.length())
.thenComparing(RouteRelation::ref);
private final Map<String, Integer> MINZOOMS;
private static final List<String> CONCURRENT_ROUTE_KEYS = List.of(
Fields.ROUTE_1,
Fields.ROUTE_2,
Fields.ROUTE_3,
Fields.ROUTE_4,
Fields.ROUTE_5,
Fields.ROUTE_6
);
private final boolean brunnel;
private final boolean sizeForShield;
private final boolean limitMerge;
private final Stats stats;
private final PlanetilerConfig config;
private final AtomicBoolean loggedNoGb = new AtomicBoolean(false);
private PreparedGeometry greatBritain = null;
private Transportation transportation;
private final LongByteMap motorwayJunctionHighwayClasses = new LongByteHashMap();
public TransportationName(Translations translations, PlanetilerConfig config, Stats stats) {
this.config = config;
this.stats = stats;
this.brunnel = config.arguments().getBoolean(
"transportation_name_brunnel",
"transportation_name layer: set to false to omit brunnel and help merge long highways",
@@ -154,71 +140,72 @@ public class TransportationName implements
"transportation_name layer: limit merge so we don't combine different relations to help merge long highways",
false
);
boolean z13Paths = config.arguments().getBoolean(
"transportation_z13_paths",
"transportation(_name) layer: show paths on z13",
true
);
MINZOOMS = Map.of(
FieldValues.CLASS_TRACK, 14,
FieldValues.CLASS_PATH, z13Paths ? 13 : 14,
FieldValues.CLASS_MINOR, 13,
FieldValues.CLASS_TRUNK, 8,
FieldValues.CLASS_MOTORWAY, 6
// default: 12
);
}
public void needsTransportationLayer(Transportation transportation) {
this.transportation = transportation;
}
@Override
public void preprocessOsmNode(OsmElement.Node node) {
if (node.hasTag("highway", "motorway_junction")) {
motorwayJunctionHighwayClasses.put(node.id(), HighwayClass.UNKNOWN.value);
}
}
@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
synchronized (this) {
try {
Geometry boundary = feature.polygon().buffer(GeoUtils.metersToPixelAtEquator(0, 10_000) / 256d);
greatBritain = PreparedGeometryFactory.prepare(boundary);
} catch (GeometryException e) {
LOGGER.error("Failed to get Great Britain Polygon: " + e);
public void preprocessOsmWay(OsmElement.Way way) {
String highway = way.getString("highway");
if (highway != null) {
HighwayClass cls = HighwayClass.from(highway);
if (cls != HighwayClass.UNKNOWN) {
LongArrayList nodes = way.nodes();
for (int i = 0; i < nodes.size(); i++) {
long node = nodes.get(i);
if (motorwayJunctionHighwayClasses.containsKey(node)) {
byte oldValue = motorwayJunctionHighwayClasses.get(node);
byte newValue = cls.value;
if (newValue > oldValue) {
motorwayJunctionHighwayClasses.put(node, newValue);
}
}
}
}
}
}
@Override
public List<OsmRelationInfo> preprocessOsmRelation(OsmElement.Relation relation) {
if (relation.hasTag("route", "road")) {
RouteNetwork networkType = null;
String network = relation.getString("network");
String ref = relation.getString("ref");
public void process(Tables.OsmHighwayPoint element, FeatureCollector features) {
long id = element.source().id();
byte value = motorwayJunctionHighwayClasses.getOrDefault(id, (byte) -1);
if (value > 0) {
HighwayClass cls = HighwayClass.from(value);
if (cls != HighwayClass.UNKNOWN) {
String subclass = FieldValues.SUBCLASS_JUNCTION;
String ref = element.ref();
if ("US:I".equals(network)) {
networkType = RouteNetwork.US_INTERSTATE;
} else if ("US:US".equals(network)) {
networkType = RouteNetwork.US_HIGHWAY;
} else if (network != null && network.length() == 5 && network.startsWith("US:")) {
networkType = RouteNetwork.US_STATE;
} else if (network != null && network.startsWith("CA:transcanada")) {
networkType = RouteNetwork.CA_TRANSCANADA;
}
if (networkType != null) {
return List.of(new RouteRelation(coalesce(ref, ""), networkType, relation.id()));
features.point(LAYER_NAME)
.setBufferPixels(BUFFER_SIZE)
.putAttrs(LanguageUtils.getNamesWithoutTranslations(element.source().tags()))
.setAttr(Fields.REF, ref)
.setAttr(Fields.REF_LENGTH, ref != null ? ref.length() : null)
.setAttr(Fields.CLASS, highwayClass(cls.highwayValue, null, null, null))
.setAttr(Fields.SUBCLASS, subclass)
.setAttr(Fields.LAYER, nullIfLong(element.layer(), 0))
.setSortKeyDescending(element.zOrder())
.setMinZoom(10);
}
}
return null;
}
@Override
public void process(Tables.OsmHighwayLinestring element, FeatureCollector features) {
List<OsmReader.RelationMember<RouteRelation>> relations = element.source()
.relationInfo(RouteRelation.class);
String ref = element.ref();
RouteRelation relation = getRouteRelation(element, relations, ref);
if (relation != null && nullIfEmpty(relation.ref) != null) {
ref = relation.ref;
List<Transportation.RouteRelation> relations = transportation.getRouteRelations(element);
Transportation.RouteRelation relation = relations.isEmpty() ? null : relations.get(0);
if (relation != null && nullIfEmpty(relation.ref()) != null) {
ref = relation.ref();
}
String name = nullIfEmpty(element.name());
@@ -230,13 +217,15 @@ public class TransportationName implements
return;
}
boolean isLink = Transportation.isLink(highway);
String baseClass = highwayClass.replace("_construction", "");
int minzoom = MINZOOMS.getOrDefault(baseClass, 12);
boolean isLink = highway.endsWith("_link");
if (isLink) {
minzoom = Math.max(13, minzoom);
}
int minzoom = FieldValues.CLASS_TRUNK.equals(baseClass) ? 8 :
FieldValues.CLASS_MOTORWAY.equals(baseClass) ? 6 :
isLink ? 13 : 12; // fallback - get from line minzoom, but floor at 12
// inherit min zoom threshold from visible road, and ensure we never show a label on a road that's not visible yet.
minzoom = Math.max(minzoom, transportation.getMinzoom(element, highwayClass));
FeatureCollector.Feature feature = features.line(LAYER_NAME)
.setBufferPixels(BUFFER_SIZE)
@@ -246,13 +235,21 @@ public class TransportationName implements
.setAttr(Fields.REF, ref)
.setAttr(Fields.REF_LENGTH, ref != null ? ref.length() : null)
.setAttr(Fields.NETWORK,
(relation != null && relation.network != null) ? relation.network.name : ref != null ? "road" : null)
(relation != null && relation.networkType() != null) ? relation.networkType().name
: !nullOrEmpty(ref) ? "road" : null)
.setAttr(Fields.CLASS, highwayClass)
.setAttr(Fields.SUBCLASS, highwaySubclass(highwayClass, null, highway))
.setMinPixelSize(0)
.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(), ""));
}
if (brunnel) {
feature.setAttr(Fields.BRUNNEL, brunnel(element.isBridge(), element.isTunnel(), element.isFord()));
}
@@ -265,48 +262,44 @@ public class TransportationName implements
if (limitMerge) {
feature
.setAttr(LINK_TEMP_KEY, isLink ? 1 : 0)
.setAttr(RELATION_ID_TEMP_KEY, relation == null ? null : relation.id);
.setAttr(RELATION_ID_TEMP_KEY, relation == null ? null : relation.id());
}
if (isFootwayOrSteps(highway)) {
feature
.setAttrWithMinzoom(Fields.LAYER, nullIf(element.layer(), 0), 12)
.setAttrWithMinzoom(Fields.LAYER, nullIfLong(element.layer(), 0), 12)
.setAttrWithMinzoom(Fields.LEVEL, Parse.parseLongOrNull(element.source().getTag("level")), 12)
.setAttrWithMinzoom(Fields.INDOOR, element.indoor() ? 1 : null, 12);
}
}
private RouteRelation getRouteRelation(Tables.OsmHighwayLinestring element,
List<OsmReader.RelationMember<RouteRelation>> relations, String ref) {
RouteRelation relation = relations.stream()
.map(OsmReader.RelationMember::relation)
.min(RELATION_ORDERING)
.orElse(null);
if (relation == null && ref != null) {
// GB doesn't use regular relations like everywhere else, so if we are
// in GB then use a naming convention instead.
Matcher refMatcher = GREAT_BRITAIN_REF_NETWORK_PATTERN.matcher(ref);
if (refMatcher.find()) {
if (greatBritain == null) {
if (!loggedNoGb.get() && loggedNoGb.compareAndSet(false, true)) {
LOGGER.warn("No GB polygon for inferring route network types");
}
} else {
try {
Geometry wayGeometry = element.source().worldGeometry();
if (greatBritain.intersects(wayGeometry)) {
RouteNetwork networkType =
"motorway".equals(element.highway()) ? RouteNetwork.GB_MOTORWAY : RouteNetwork.GB_TRUNK;
relation = new RouteRelation(refMatcher.group(), networkType, 0);
}
} catch (GeometryException e) {
e.log(stats, "omt_transportation_name_gb_test",
"Unable to test highway against GB route network: " + element.source().id());
}
}
}
@Override
public void process(Tables.OsmAerialwayLinestring element, FeatureCollector features) {
if (!nullOrEmpty(element.name())) {
features.line(LAYER_NAME)
.setBufferPixels(BUFFER_SIZE)
.setBufferPixelOverrides(MIN_LENGTH)
.putAttrs(LanguageUtils.getNamesWithoutTranslations(element.source().tags()))
.setAttr(Fields.CLASS, "aerialway")
.setAttr(Fields.SUBCLASS, element.aerialway())
.setMinPixelSize(0)
.setSortKey(element.zOrder())
.setMinZoom(12);
}
}
@Override
public void process(Tables.OsmShipwayLinestring element, FeatureCollector features) {
if (!nullOrEmpty(element.name())) {
features.line(LAYER_NAME)
.setBufferPixels(BUFFER_SIZE)
.setBufferPixelOverrides(MIN_LENGTH)
.putAttrs(LanguageUtils.getNamesWithoutTranslations(element.source().tags()))
.setAttr(Fields.CLASS, element.shipway())
.setMinPixelSize(0)
.setSortKey(element.zOrder())
.setMinZoom(12);
}
return relation;
}
@Override
@@ -341,35 +334,38 @@ public class TransportationName implements
name instanceof String str ? str.length() * 6 : Double.MAX_VALUE;
}
private enum RouteNetwork {
private enum HighwayClass {
MOTORWAY("motorway", 6),
TRUNK("trunk", 5),
PRIMARY("primary", 4),
SECONDARY("secondary", 3),
TERTIARY("tertiary", 2),
UNCLASSIFIED("unclassified", 1),
UNKNOWN("", 0);
US_INTERSTATE("us-interstate"),
US_HIGHWAY("us-highway"),
US_STATE("us-state"),
CA_TRANSCANADA("ca-transcanada"),
GB_MOTORWAY("gb-motorway"),
GB_TRUNK("gb-trunk");
private static final Map<String, HighwayClass> indexByString = new HashMap<>();
private static final Map<Byte, HighwayClass> indexByByte = new HashMap<>();
final byte value;
final String highwayValue;
final String name;
RouteNetwork(String name) {
this.name = name;
HighwayClass(String highwayValue, int id) {
this.highwayValue = highwayValue;
this.value = (byte) id;
}
}
/** Information extracted from route relations to use when processing ways in that relation. */
private static record RouteRelation(
String ref,
RouteNetwork network,
@Override long id
) implements OsmRelationInfo {
static {
Arrays.stream(values()).forEach(cls -> {
indexByString.put(cls.highwayValue, cls);
indexByByte.put(cls.value, cls);
});
}
@Override
public long estimateMemoryUsageBytes() {
return CLASS_HEADER_BYTES +
POINTER_BYTES + estimateSize(ref) +
POINTER_BYTES + // network
MemoryEstimator.estimateSizeLong(id);
static HighwayClass from(String highway) {
return indexByString.getOrDefault(highway, UNKNOWN);
}
static HighwayClass from(byte value) {
return indexByByte.getOrDefault(value, UNKNOWN);
}
}
}

View File

@@ -1,5 +1,5 @@
/*
Copyright (c) 2016, KlokanTech.com & OpenMapTiles contributors.
Copyright (c) 2021, MapTiler.com & OpenMapTiles contributors.
All rights reserved.
Code license: BSD 3-Clause License
@@ -103,13 +103,15 @@ public class Water implements
@Override
public void process(Tables.OsmWaterPolygon element, FeatureCollector features) {
if (!"bay".equals(element.natural())) {
String clazz = "riverbank".equals(element.waterway()) ? FieldValues.CLASS_RIVER :
classMapping.getOrElse(element.source(), FieldValues.CLASS_LAKE);
features.polygon(LAYER_NAME)
.setBufferPixels(BUFFER_SIZE)
.setMinPixelSizeBelowZoom(11, 2)
.setMinZoom(6)
.setAttr(Fields.INTERMITTENT, element.isIntermittent() ? 1 : 0)
.setAttrWithMinzoom(Fields.BRUNNEL, Utils.brunnel(element.isBridge(), element.isTunnel()), 12)
.setAttr(Fields.CLASS, classMapping.getOrElse(element.source(), FieldValues.CLASS_RIVER));
.setAttr(Fields.CLASS, clazz);
}
}
}

View File

@@ -1,5 +1,5 @@
/*
Copyright (c) 2016, KlokanTech.com & OpenMapTiles contributors.
Copyright (c) 2021, MapTiler.com & OpenMapTiles contributors.
All rights reserved.
Code license: BSD 3-Clause License

View File

@@ -1,5 +1,5 @@
/*
Copyright (c) 2016, KlokanTech.com & OpenMapTiles contributors.
Copyright (c) 2021, MapTiler.com & OpenMapTiles contributors.
All rights reserved.
Code license: BSD 3-Clause License
@@ -37,6 +37,9 @@ package com.onthegomap.planetiler.basemap.layers;
import static com.onthegomap.planetiler.basemap.util.Utils.nullIfEmpty;
import com.carrotsearch.hppc.LongObjectHashMap;
import com.google.common.util.concurrent.AtomicDouble;
import com.graphhopper.coll.GHLongObjectHashMap;
import com.onthegomap.planetiler.FeatureCollector;
import com.onthegomap.planetiler.FeatureMerge;
import com.onthegomap.planetiler.VectorTile;
@@ -46,7 +49,11 @@ import com.onthegomap.planetiler.basemap.generated.Tables;
import com.onthegomap.planetiler.basemap.util.LanguageUtils;
import com.onthegomap.planetiler.basemap.util.Utils;
import com.onthegomap.planetiler.config.PlanetilerConfig;
import com.onthegomap.planetiler.geo.GeometryException;
import com.onthegomap.planetiler.reader.SourceFeature;
import com.onthegomap.planetiler.reader.osm.OsmElement;
import com.onthegomap.planetiler.reader.osm.OsmReader;
import com.onthegomap.planetiler.reader.osm.OsmRelationInfo;
import com.onthegomap.planetiler.stats.Stats;
import com.onthegomap.planetiler.util.Translations;
import com.onthegomap.planetiler.util.ZoomFunction;
@@ -63,7 +70,9 @@ public class Waterway implements
OpenMapTilesSchema.Waterway,
Tables.OsmWaterwayLinestring.Handler,
BasemapProfile.FeaturePostProcessor,
BasemapProfile.NaturalEarthProcessor {
BasemapProfile.NaturalEarthProcessor,
BasemapProfile.OsmRelationPreprocessor,
BasemapProfile.OsmAllProcessor {
/*
* Uses Natural Earth at lower zoom-levels and OpenStreetMap at higher zoom levels.
@@ -76,14 +85,6 @@ public class Waterway implements
* short segment of it goes through this tile.
*/
private final Translations translations;
private final PlanetilerConfig config;
public Waterway(Translations translations, PlanetilerConfig config, Stats stats) {
this.config = config;
this.translations = translations;
}
private static final Map<String, Integer> CLASS_MINZOOM = Map.of(
"river", 12,
"canal", 12,
@@ -92,12 +93,29 @@ public class Waterway implements
"drain", 13,
"ditch", 13
);
private static final String TEMP_REL_ID_ADDR = "_relid";
private final Translations translations;
private final PlanetilerConfig config;
private final Stats stats;
private final LongObjectHashMap<AtomicDouble> riverRelationLengths = new GHLongObjectHashMap<>();
public Waterway(Translations translations, PlanetilerConfig config, Stats stats) {
this.config = config;
this.translations = translations;
this.stats = stats;
}
private static final ZoomFunction.MeterToPixelThresholds MIN_PIXEL_LENGTHS = ZoomFunction.meterThresholds()
.put(6, 500_000)
.put(7, 400_000)
.put(8, 300_000)
.put(9, 8_000)
.put(10, 4_000)
.put(11, 1_000);
// zoom-level 3-5 come from natural earth
@Override
public void processNaturalEarth(String table, SourceFeature feature, FeatureCollector features) {
if (feature.hasTag("featurecla", "River")) {
@@ -105,7 +123,6 @@ public class Waterway implements
ZoomRange zoom = switch (table) {
case "ne_110m_rivers_lake_centerlines" -> new ZoomRange(3, 3);
case "ne_50m_rivers_lake_centerlines" -> new ZoomRange(4, 5);
case "ne_10m_rivers_lake_centerlines" -> new ZoomRange(6, 8);
default -> null;
};
if (zoom != null) {
@@ -117,6 +134,52 @@ public class Waterway implements
}
}
// zoom-level 6-8 come from OSM river relations
private record WaterwayRelation(
long id,
Map<String, Object> names
) implements OsmRelationInfo {}
@Override
public List<OsmRelationInfo> preprocessOsmRelation(OsmElement.Relation relation) {
if (relation.hasTag("waterway", "river") && !Utils.nullOrEmpty(relation.getString("name"))) {
riverRelationLengths.put(relation.id(), new AtomicDouble());
return List.of(new WaterwayRelation(relation.id(), LanguageUtils.getNames(relation.tags(), translations)));
}
return null;
}
@Override
public void processAllOsm(SourceFeature feature, FeatureCollector features) {
List<OsmReader.RelationMember<WaterwayRelation>> waterways = feature.relationInfo(WaterwayRelation.class);
if (waterways != null && !waterways.isEmpty() && feature.canBeLine()) {
for (var waterway : waterways) {
String role = waterway.role();
if (Utils.nullOrEmpty(role) || "main_stream".equals(role)) {
long relId = waterway.relation().id();
try {
AtomicDouble counter = riverRelationLengths.get(relId);
if (counter != null) {
counter.addAndGet(feature.length());
}
} catch (GeometryException e) {
e.log(stats, "waterway_decode", "Unable to get waterway length for " + feature.id());
}
features.line(LAYER_NAME)
.setAttr(TEMP_REL_ID_ADDR, relId)
.setBufferPixels(BUFFER_SIZE)
.setAttr(Fields.CLASS, FieldValues.CLASS_RIVER)
.putAttrs(waterway.relation().names())
.setZoomRange(6, 8)
.setMinPixelSize(0);
}
}
}
}
// zoom-level 9+ come from OSM river ways
@Override
public void process(Tables.OsmWaterwayLinestring element, FeatureCollector features) {
String waterway = element.waterway();
@@ -137,7 +200,22 @@ public class Waterway implements
@Override
public List<VectorTile.Feature> postProcess(int zoom, List<VectorTile.Feature> items) {
if (zoom >= 9 && zoom <= 11) {
if (zoom >= 6 && zoom <= 8) {
// remove ways for river relations if relation is not long enough
double minSizeAtZoom = MIN_PIXEL_LENGTHS.apply(zoom).doubleValue() / Math.pow(2, zoom) / 256d;
for (int i = 0; i < items.size(); i++) {
Object relIdObj = items.get(i).attrs().remove(TEMP_REL_ID_ADDR);
if (relIdObj instanceof Long relId && riverRelationLengths.get(relId).get() < minSizeAtZoom) {
items.set(i, null);
}
}
return FeatureMerge.mergeLineStrings(
items,
config.minFeatureSize(zoom),
config.tolerance(zoom),
BUFFER_SIZE
);
} else if (zoom >= 9 && zoom <= 11) {
return FeatureMerge.mergeLineStrings(
items,
MIN_PIXEL_LENGTHS.apply(zoom).doubleValue(),

View File

@@ -1,5 +1,5 @@
/*
Copyright (c) 2016, KlokanTech.com & OpenMapTiles contributors.
Copyright (c) 2021, MapTiler.com & OpenMapTiles contributors.
All rights reserved.
Code license: BSD 3-Clause License

View File

@@ -28,9 +28,14 @@ public class Utils {
return a != null ? a : b != null ? b : c != null ? c : d != null ? d : e != null ? e : f;
}
/** Returns {@code a} or {@code nullValue} if {@code a} is null. */
public static <T> T nullIf(T a, T nullValue) {
return nullValue.equals(a) ? null : a;
/** Boxes {@code a} into an {@link Integer}, or {@code null} if {@code a} is {@code nullValue}. */
public static Long nullIfLong(long a, long nullValue) {
return a == nullValue ? null : a;
}
/** Boxes {@code a} into a {@link Long}, or {@code null} if {@code a} is {@code nullValue}. */
public static Integer nullIfInt(int a, int nullValue) {
return a == nullValue ? null : a;
}
/** Returns {@code a}, or null if {@code a} is "". */

View File

@@ -211,7 +211,8 @@ public class GenerateTest {
List.of(),
null,
null,
props
props,
List.of()
));
assertEquals(or(
and(

View File

@@ -211,4 +211,15 @@ public abstract class AbstractLayerTest {
profile.postProcessLayerFeatures(layer, zoom, List.of(line1, line2))
);
}
public static Map<String, Object> mapOf(Object... args) {
assert args.length % 2 == 0;
Map<String, Object> result = new HashMap<>();
for (int i = 0; i < args.length; i += 2) {
String key = args[i].toString();
Object value = args[i + 1];
result.put(key, value == null ? "<null>" : value);
}
return result;
}
}

View File

@@ -13,7 +13,7 @@ public class AerodromeLabelTest extends AbstractLayerTest {
}
@Test
public void testHappyPathPoint() {
public void testIntlWithIata() {
assertFeatures(14, List.of(Map.of(
"class", "international",
"ele", 100,
@@ -23,7 +23,7 @@ public class AerodromeLabelTest extends AbstractLayerTest {
"_layer", "aerodrome_label",
"_type", "point",
"_minzoom", 10,
"_minzoom", 8,
"_maxzoom", 14,
"_buffer", 64d
)), process(pointFeature(Map.of(
@@ -41,7 +41,8 @@ public class AerodromeLabelTest extends AbstractLayerTest {
public void testInternational() {
assertFeatures(14, List.of(Map.of(
"class", "international",
"_layer", "aerodrome_label"
"_layer", "aerodrome_label",
"_minzoom", 10 // no IATA
)), process(pointFeature(Map.of(
"aeroway", "aerodrome",
"aerodrome_type", "international"
@@ -52,7 +53,8 @@ public class AerodromeLabelTest extends AbstractLayerTest {
public void testPublic() {
assertFeatures(14, List.of(Map.of(
"class", "public",
"_layer", "aerodrome_label"
"_layer", "aerodrome_label",
"_minzoom", 10
)), process(pointFeature(Map.of(
"aeroway", "aerodrome",
"aerodrome_type", "public airport"
@@ -70,7 +72,8 @@ public class AerodromeLabelTest extends AbstractLayerTest {
public void testMilitary() {
assertFeatures(14, List.of(Map.of(
"class", "military",
"_layer", "aerodrome_label"
"_layer", "aerodrome_label",
"_minzoom", 10
)), process(pointFeature(Map.of(
"aeroway", "aerodrome",
"aerodrome_type", "military airport"
@@ -88,7 +91,8 @@ public class AerodromeLabelTest extends AbstractLayerTest {
public void testPrivate() {
assertFeatures(14, List.of(Map.of(
"class", "private",
"_layer", "aerodrome_label"
"_layer", "aerodrome_label",
"_minzoom", 10
)), process(pointFeature(Map.of(
"aeroway", "aerodrome",
"aerodrome_type", "private"
@@ -106,7 +110,8 @@ public class AerodromeLabelTest extends AbstractLayerTest {
public void testOther() {
assertFeatures(14, List.of(Map.of(
"class", "other",
"_layer", "aerodrome_label"
"_layer", "aerodrome_label",
"_minzoom", 10
)), process(pointFeature(Map.of(
"aeroway", "aerodrome"
))));

View File

@@ -144,11 +144,30 @@ public class BoundaryTest extends AbstractLayerTest {
"ne_10m_admin_1_states_provinces_lines",
0
)));
assertFeatures(0, List.of(Map.of(
"_layer", "boundary",
"_type", "line",
"disputed", 0,
"maritime", 0,
"admin_level", 4,
"_minzoom", 4,
"_maxzoom", 4,
"_buffer", 4d
)), process(SimpleFeature.create(
newLineString(0, 0, 1, 1),
Map.of(
"min_zoom", 7.6d
),
NATURAL_EARTH_SOURCE,
"ne_10m_admin_1_states_provinces_lines",
0
)));
assertFeatures(0, List.of(), process(SimpleFeature.create(
newLineString(0, 0, 1, 1),
Map.of(
"min_zoom", 7.1d
"min_zoom", 7.9d
),
NATURAL_EARTH_SOURCE,
"ne_10m_admin_1_states_provinces_lines",

View File

@@ -41,6 +41,14 @@ public class BuildingTest extends AbstractLayerTest {
))));
}
@Test
public void testIgnoreUndergroundBuilding() {
assertFeatures(14, List.of(), process(polygonFeature(Map.of(
"building", "yes",
"location", "underground"
))));
}
@Test
public void testAirportBuildings() {
assertFeatures(13, List.of(Map.of(

View File

@@ -130,6 +130,9 @@ public class LandcoverTest extends AbstractLayerTest {
)), process(polygonFeature(Map.of(
"natural", "dune"
))));
assertFeatures(10, List.of(), process(polygonFeature(Map.of(
"landuse", "park"
))));
}
@Test
@@ -137,6 +140,7 @@ public class LandcoverTest extends AbstractLayerTest {
Map<String, Object> map = Map.of("subclass", "wood");
assertMerges(List.of(map, map, map, map, map, map), List.of(
// don't merge any
feature(rectangle(10, 20), Map.of("_numpoints", 48, "subclass", "wood")),
feature(rectangle(10, 20), Map.of("_numpoints", 49, "subclass", "wood")),
feature(rectangle(12, 18), Map.of("_numpoints", 50, "subclass", "wood")),
@@ -144,19 +148,23 @@ public class LandcoverTest extends AbstractLayerTest {
feature(rectangle(12, 18), Map.of("_numpoints", 300, "subclass", "wood")),
feature(rectangle(12, 18), Map.of("_numpoints", 301, "subclass", "wood"))
), 14);
assertMerges(List.of(map, map, map, map), List.of(
assertMerges(List.of(map, map, map), List.of(
// < 300 - merge
feature(rectangle(10, 20), Map.of("_numpoints", 48, "subclass", "wood")),
feature(rectangle(10, 20), Map.of("_numpoints", 49, "subclass", "wood")),
feature(rectangle(12, 18), Map.of("_numpoints", 50, "subclass", "wood")),
feature(rectangle(12, 18), Map.of("_numpoints", 299, "subclass", "wood")),
// >= 300 - don't merge
feature(rectangle(12, 18), Map.of("_numpoints", 300, "subclass", "wood")),
feature(rectangle(12, 18), Map.of("_numpoints", 301, "subclass", "wood"))
), 13);
assertMerges(List.of(map, map, map), List.of(
assertMerges(List.of(map, map), List.of(
// < 300 - merge
feature(rectangle(10, 20), Map.of("_numpoints", 48, "subclass", "wood")),
feature(rectangle(10, 20), Map.of("_numpoints", 49, "subclass", "wood")),
feature(rectangle(12, 18), Map.of("_numpoints", 50, "subclass", "wood")),
feature(rectangle(12, 18), Map.of("_numpoints", 299, "subclass", "wood")),
// >= 300 - merge
feature(rectangle(12, 18), Map.of("_numpoints", 300, "subclass", "wood")),
feature(rectangle(12, 18), Map.of("_numpoints", 301, "subclass", "wood"))
), 9);

View File

@@ -58,6 +58,18 @@ public class LanduseTest extends AbstractLayerTest {
))));
}
@Test
public void testGraveYardBecomesCemetery() {
assertFeatures(14, List.of(
Map.of("_layer", "poi"),
Map.of(
"_layer", "landuse",
"class", "cemetery"
)), process(polygonFeature(Map.of(
"amenity", "grave_yard"
))));
}
@Test
public void testOsmLanduseLowerZoom() {
assertFeatures(6, List.of(Map.of(

View File

@@ -1,11 +1,15 @@
package com.onthegomap.planetiler.basemap.layers;
import static com.onthegomap.planetiler.TestUtils.newPoint;
import static com.onthegomap.planetiler.TestUtils.rectangle;
import static com.onthegomap.planetiler.basemap.BasemapProfile.NATURAL_EARTH_SOURCE;
import static com.onthegomap.planetiler.basemap.BasemapProfile.OSM_SOURCE;
import static org.junit.jupiter.api.Assertions.assertEquals;
import com.google.common.collect.Lists;
import com.onthegomap.planetiler.VectorTile;
import com.onthegomap.planetiler.geo.GeometryException;
import com.onthegomap.planetiler.reader.SimpleFeature;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -31,6 +35,7 @@ public class MountainPeakTest extends AbstractLayerTest {
"class", "peak",
"ele", 100,
"ele_ft", 328,
"customary_ft", "<null>",
"_layer", "mountain_peak",
"_type", "point",
@@ -70,6 +75,17 @@ public class MountainPeakTest extends AbstractLayerTest {
))));
}
@Test
public void testSaddle() {
assertFeatures(14, List.of(Map.of(
"class", "saddle"
)), process(pointFeature(Map.of(
"natural", "saddle",
"ele", "100"
))));
}
@Test
public void testNoElevation() {
assertFeatures(14, List.of(), process(pointFeature(Map.of(
@@ -86,7 +102,7 @@ public class MountainPeakTest extends AbstractLayerTest {
}
@Test
public void testIgnoreLines() {
public void testIgnorePeakLines() {
assertFeatures(14, List.of(), process(lineFeature(Map.of(
"natural", "peak",
"name", "name",
@@ -94,6 +110,68 @@ public class MountainPeakTest extends AbstractLayerTest {
))));
}
@Test
public void testMountainLinestring() {
assertFeatures(14, List.of(Map.of(
"class", "ridge",
"name", "Ridge",
"_layer", "mountain_peak",
"_type", "line",
"_minzoom", 13,
"_maxzoom", 14,
"_buffer", 100d
)), process(lineFeature(Map.of(
"natural", "ridge",
"name", "Ridge"
))));
}
@Test
public void testCustomaryFt() {
process(SimpleFeature.create(
rectangle(0, 0.1),
Map.of("iso_a2", "US"),
NATURAL_EARTH_SOURCE,
"ne_10m_admin_0_countries",
0
));
// inside US - customary_ft=1
assertFeatures(14, List.of(Map.of(
"class", "volcano",
"customary_ft", 1,
"ele", 100,
"ele_ft", 328
)), process(SimpleFeature.create(
newPoint(0, 0),
new HashMap<>(Map.<String, Object>of(
"natural", "volcano",
"ele", "100"
)),
OSM_SOURCE,
null,
0
)));
// outside US - customary_ft omitted
assertFeatures(14, List.of(Map.of(
"class", "volcano",
"customary_ft", "<null>",
"ele", 100,
"ele_ft", 328
)), process(SimpleFeature.create(
newPoint(1, 1),
new HashMap<>(Map.<String, Object>of(
"natural", "volcano",
"ele", "100"
)),
OSM_SOURCE,
null,
0
)));
}
private int getSortKey(Map<String, Object> tags) {
return process(pointFeature(Map.of(
"natural", "peak",

View File

@@ -13,9 +13,9 @@ public class ParkTest extends AbstractLayerTest {
"_layer", "park",
"_type", "polygon",
"class", "national_park",
"name", "<null>",
"name", "Grand Canyon National Park",
"_minpixelsize", 2d,
"_minzoom", 6,
"_minzoom", 4,
"_maxzoom", 14
), Map.of(
"_layer", "park",
@@ -24,8 +24,8 @@ public class ParkTest extends AbstractLayerTest {
"name", "Grand Canyon National Park",
"name_int", "Grand Canyon National Park",
"name:latin", "Grand Canyon National Park",
"name:es", "es name",
"_minzoom", 6,
// "name:es", "es name", // don't include all translations
"_minzoom", 5,
"_maxzoom", 14
)), process(polygonFeature(Map.of(
"boundary", "national_park",
@@ -52,9 +52,9 @@ public class ParkTest extends AbstractLayerTest {
"_layer", "park",
"_type", "polygon",
"class", "protected_area",
"name", "<null>",
"name", "Small park",
"_minpixelsize", 2d,
"_minzoom", 6,
"_minzoom", 4,
"_maxzoom", 14
), Map.of(
"_layer", "park",
@@ -75,7 +75,7 @@ public class ParkTest extends AbstractLayerTest {
), Map.of(
"_layer", "park",
"_type", "point",
"_minzoom", 6,
"_minzoom", 5,
"_maxzoom", 14
)), process(polygonFeatureWithArea(1, Map.of(
"boundary", "protected_area",

View File

@@ -131,8 +131,8 @@ public class PlaceTest extends AbstractLayerTest {
rectangle(0.4, 0.6),
Map.of(
"name", "Massachusetts - not important",
"scalerank", 4,
"labelrank", 4,
"scalerank", 8,
"labelrank", 8,
"datarank", 1
),
NATURAL_EARTH_SOURCE,
@@ -194,6 +194,44 @@ public class PlaceTest extends AbstractLayerTest {
)));
}
@Test
public void testProvince() {
wikidataTranslations.put(95027, "es", "provincia de Lugo");
process(SimpleFeature.create(
rectangle(0, 0.25),
Map.of(
"name", "Nova Scotia",
"scalerank", 3,
"labelrank", 3,
"datarank", 3
),
NATURAL_EARTH_SOURCE,
"ne_10m_admin_1_states_provinces",
0
));
assertFeatures(4, List.of(Map.of(
"_layer", "place",
"class", "province",
"name", "Lugo",
"name:es", "provincia de Lugo",
"rank", 3,
"_type", "point",
"_minzoom", 2
)), process(SimpleFeature.create(
newPoint(0.1, 0.1),
Map.of(
"place", "province",
"wikidata", "Q95027",
"name", "Lugo"
),
OSM_SOURCE,
null,
0
)));
}
@Test
public void testIslandPoint() {
assertFeatures(0, List.of(Map.of(

View File

@@ -181,4 +181,30 @@ public class PoiTest extends AbstractLayerTest {
)
)));
}
@Test
public void testEmbassy() {
assertFeatures(7, List.of(Map.of(
"_layer", "poi",
"class", "diplomatic",
"subclass", "diplomatic",
"name", "The Embassy"
)), process(pointFeature(Map.of(
"office", "diplomatic",
"name", "The Embassy"
))));
}
@Test
public void testLocksmith() {
assertFeatures(7, List.of(Map.of(
"_layer", "poi",
"class", "shop",
"subclass", "locksmith",
"name", "The Locksmith"
)), process(pointFeature(Map.of(
"shop", "locksmith",
"name", "The Locksmith"
))));
}
}

View File

@@ -1,14 +1,20 @@
package com.onthegomap.planetiler.basemap.layers;
import static com.onthegomap.planetiler.TestUtils.newLineString;
import static com.onthegomap.planetiler.TestUtils.newPoint;
import static com.onthegomap.planetiler.TestUtils.rectangle;
import static com.onthegomap.planetiler.basemap.BasemapProfile.NATURAL_EARTH_SOURCE;
import static com.onthegomap.planetiler.basemap.BasemapProfile.OSM_SOURCE;
import com.onthegomap.planetiler.FeatureCollector;
import com.onthegomap.planetiler.basemap.BasemapProfile;
import com.onthegomap.planetiler.config.Arguments;
import com.onthegomap.planetiler.config.PlanetilerConfig;
import com.onthegomap.planetiler.geo.GeometryException;
import com.onthegomap.planetiler.reader.SimpleFeature;
import com.onthegomap.planetiler.reader.SourceFeature;
import com.onthegomap.planetiler.reader.osm.OsmElement;
import com.onthegomap.planetiler.stats.Stats;
import java.util.List;
import java.util.Map;
import java.util.stream.Stream;
@@ -33,12 +39,12 @@ public class TransportationTest extends AbstractLayerTest {
"_type", "line",
"class", "path",
"subclass", "footway",
"oneway", 0,
"oneway", "<null>",
"name", "<null>",
"layer", "<null>",
"_buffer", 4d,
"_minpixelsize", 0d,
"_minzoom", 13,
"_maxzoom", 14
"_minzoom", 13
), Map.of(
"_layer", "transportation_name",
"_type", "line",
@@ -54,9 +60,10 @@ public class TransportationTest extends AbstractLayerTest {
assertFeatures(13, List.of(Map.of(
"_layer", "transportation",
"surface", "paved",
"oneway", 0,
"oneway", "<null>",
"layer", "<null>",
"level", 0L,
"ramp", 0,
"ramp", "<null>",
"bicycle", "dismount",
"foot", "designated"
), Map.of(
@@ -70,20 +77,154 @@ public class TransportationTest extends AbstractLayerTest {
)), result);
}
@Test
public void testImportantPath() {
var rel = new OsmElement.Relation(1);
rel.setTag("colour", "white");
rel.setTag("name", "Appalachian Trail - 11 MA");
rel.setTag("network", "nwn");
rel.setTag("osmc", "symbol white::white_stripe");
rel.setTag("ref", "AT");
rel.setTag("route", "hiking");
rel.setTag("short_name", "AT 11 MA");
rel.setTag("symbol", "white-paint blazes");
rel.setTag("type", "route");
rel.setTag("wikidata", "Q620648");
rel.setTag("wikipedia", "en:Appalachian Trail");
FeatureCollector features = process(lineFeatureWithRelation(
profile.preprocessOsmRelation(rel),
Map.of(
"bicycle", "no",
"highway", "path",
"horse", "no",
"name", "Appalachian Trail",
"ref", "AT",
"surface", "ground"
)));
assertFeatures(12, List.of(Map.of(
"_layer", "transportation",
"_type", "line",
"class", "path",
"subclass", "path",
"oneway", "<null>",
"name", "<null>",
"layer", "<null>",
"_buffer", 4d,
"_minpixelsize", 0d,
"_minzoom", 12
), Map.of(
"_layer", "transportation_name",
"_type", "line",
"class", "path",
"subclass", "path",
"name", "Appalachian Trail",
"name_int", "Appalachian Trail",
"name:latin", "Appalachian Trail",
"_minpixelsize", 0d,
"_minzoom", 12
)), features);
}
@Test
public void testUnnamedPath() {
assertFeatures(13, List.of(Map.of(
assertFeatures(14, List.of(Map.of(
"_layer", "transportation",
"class", "path",
"subclass", "path",
"surface", "unpaved",
"oneway", 0
"oneway", "<null>",
"_minzoom", 14
)), process(lineFeature(Map.of(
"surface", "dirt",
"highway", "path"
))));
}
@Test
public void testPrivatePath() {
assertFeatures(9, List.of(Map.of(
"_layer", "transportation",
"class", "path",
"access", "no"
)), process(lineFeature(Map.of(
"access", "private",
"highway", "path"
))));
assertFeatures(9, List.of(Map.of(
"_layer", "transportation",
"class", "path",
"access", "no"
)), process(lineFeature(Map.of(
"access", "no",
"highway", "path"
))));
assertFeatures(8, List.of(Map.of(
"_layer", "transportation",
"class", "path",
"access", "<null>"
)), process(lineFeature(Map.of(
"access", "no",
"highway", "path"
))));
}
@Test
public void testExpressway() {
assertFeatures(8, List.of(Map.of(
"_layer", "transportation",
"class", "motorway",
"expressway", "<null>"
)), process(lineFeature(Map.of(
"highway", "motorway",
"expressway", "yes"
))));
assertFeatures(8, List.of(Map.of(
"_layer", "transportation",
"class", "primary",
"expressway", 1
)), process(lineFeature(Map.of(
"highway", "primary",
"expressway", "yes"
))));
assertFeatures(7, List.of(Map.of(
"_layer", "transportation",
"class", "primary",
"expressway", "<null>"
)), process(lineFeature(Map.of(
"highway", "primary",
"expressway", "yes"
))));
}
@Test
public void testToll() {
assertFeatures(9, List.of(Map.of(
"_layer", "transportation",
"class", "motorway",
"toll", "<null>"
)), process(lineFeature(Map.of(
"highway", "motorway"
))));
assertFeatures(9, List.of(Map.of(
"_layer", "transportation",
"class", "motorway",
"toll", 1
)), process(lineFeature(Map.of(
"highway", "motorway",
"toll", "yes"
))));
assertFeatures(8, List.of(Map.of(
"_layer", "transportation",
"class", "motorway",
"toll", "<null>"
)), process(lineFeature(Map.of(
"highway", "motorway",
"toll", "yes"
))));
}
@Test
public void testIndoorTunnelSteps() {
assertFeatures(13, List.of(Map.of(
@@ -93,7 +234,7 @@ public class TransportationTest extends AbstractLayerTest {
"brunnel", "tunnel",
"indoor", 1,
"oneway", 1,
"ramp", 1
"ramp", "<null>"
)), process(lineFeature(Map.of(
"highway", "steps",
"tunnel", "building_passage",
@@ -124,12 +265,37 @@ public class TransportationTest extends AbstractLayerTest {
"bridge", "yes"
)));
assertFeatures(13, List.of(mapOf(
"_layer", "transportation",
"class", "motorway",
"surface", "paved",
"oneway", 1,
"ramp", "<null>",
"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", "<null>",
"route_1", "US:I=90",
"_minzoom", 6
)), features);
assertFeatures(13, List.of(Map.of(
"_layer", "transportation",
"class", "motorway",
"surface", "paved",
"oneway", 1,
"ramp", 0,
"ramp", "<null>",
"bicycle", "no",
"foot", "no",
"horse", "no",
@@ -143,6 +309,7 @@ public class TransportationTest extends AbstractLayerTest {
"ref", "90",
"ref_length", 2,
"network", "us-interstate",
"route_1", "US:I=90",
"brunnel", "<null>",
"_minzoom", 6
)), features);
@@ -166,11 +333,92 @@ public class TransportationTest extends AbstractLayerTest {
"ref", "90",
"ref_length", 2,
"network", "us-interstate",
"route_1", "US:I=90",
"brunnel", "<null>",
"_minzoom", 6
)), features);
}
@Test
public void testMotorwayJunction() {
var otherNode1 = new OsmElement.Node(1, 1, 1);
var junctionNode = new OsmElement.Node(2, 1, 2);
var otherNode2 = new OsmElement.Node(3, 1, 3);
var otherNode3 = new OsmElement.Node(4, 2, 3);
junctionNode.setTag("highway", "motorway_junction");
junctionNode.setTag("name", "exit 1");
junctionNode.setTag("layer", "1");
junctionNode.setTag("ref", "12");
// 2 ways meet at junctionNode (id=2) - use most important class of a highway intersecting it (motorway)
var way1 = new OsmElement.Way(5);
way1.setTag("highway", "motorway");
way1.nodes().add(otherNode1.id(), junctionNode.id(), otherNode2.id());
var way2 = new OsmElement.Way(6);
way2.setTag("highway", "primary");
way2.nodes().add(junctionNode.id(), otherNode3.id());
profile.preprocessOsmNode(otherNode1);
profile.preprocessOsmNode(junctionNode);
profile.preprocessOsmNode(otherNode2);
profile.preprocessOsmNode(otherNode3);
profile.preprocessOsmWay(way1);
profile.preprocessOsmWay(way2);
FeatureCollector features = process(SimpleFeature.create(
newPoint(1, 2),
junctionNode.tags(),
OSM_SOURCE,
null,
junctionNode.id()
));
assertFeatures(13, List.of(mapOf(
"_layer", "transportation_name",
"class", "motorway",
"subclass", "junction",
"name", "exit 1",
"ref", "12",
"ref_length", 2,
"layer", 1L,
"_type", "point",
"_minzoom", 10
)), features);
}
@Test
public void testInterstateMotorwayWithoutWayInfo() {
var rel = new OsmElement.Relation(1);
rel.setTag("type", "route");
rel.setTag("route", "road");
rel.setTag("network", "US:I");
rel.setTag("ref", "90");
FeatureCollector features = process(lineFeatureWithRelation(
profile.preprocessOsmRelation(rel),
Map.of(
"highway", "motorway"
)));
assertFeatures(13, List.of(mapOf(
"_layer", "transportation",
"class", "motorway",
"network", "us-interstate",
"_minzoom", 4
), Map.of(
"_layer", "transportation_name",
"class", "motorway",
"ref", "90",
"ref_length", 2,
"network", "us-interstate",
"brunnel", "<null>",
"route_1", "US:I=90",
"_minzoom", 6
)), features);
}
@Test
public void testPrimaryRoadConstruction() {
assertFeatures(13, List.of(Map.of(
@@ -223,7 +471,7 @@ public class TransportationTest extends AbstractLayerTest {
"_layer", "transportation",
"class", "service",
"service", "driveway",
"_minzoom", 12
"_minzoom", 14
)), process(lineFeature(Map.of(
"highway", "service",
"service", "driveway"
@@ -256,7 +504,28 @@ public class TransportationTest extends AbstractLayerTest {
}
@Test
public void testTrack() {
public void testNamedTrack() {
assertFeatures(13, List.of(Map.of(
"_layer", "transportation",
"class", "track",
"surface", "unpaved",
"horse", "yes",
"_minzoom", 13
), Map.of(
"_layer", "transportation_name",
"class", "track",
"name", "name",
"_minzoom", 13
)), process(lineFeature(Map.of(
"highway", "track",
"surface", "dirt",
"horse", "yes",
"name", "name"
))));
}
@Test
public void testUnnamedTrack() {
assertFeatures(13, List.of(Map.of(
"_layer", "transportation",
"class", "track",
@@ -270,6 +539,29 @@ public class TransportationTest extends AbstractLayerTest {
))));
}
@Test
public void testBusway() {
assertFeatures(13, List.of(Map.of(
"_layer", "transportation",
"class", "busway",
"brunnel", "tunnel",
"_minzoom", 11
), Map.of(
"_layer", "transportation_name",
"class", "busway",
"name", "Silver Line",
"_minzoom", 12
)), process(lineFeature(Map.of(
"access", "no",
"bus", "yes",
"highway", "busway",
"layer", "-1",
"name", "Silver Line",
"trolley_wire", "yes",
"tunnel", "yes"
))));
}
final OsmElement.Relation relUS = new OsmElement.Relation(1);
{
@@ -294,8 +586,9 @@ public class TransportationTest extends AbstractLayerTest {
"_layer", "transportation",
"class", "primary",
"surface", "paved",
"oneway", 0,
"ramp", 0,
"oneway", "<null>",
"ramp", "<null>",
"network", "us-highway",
"_minzoom", 7
), Map.of(
"_layer", "transportation_name",
@@ -305,6 +598,8 @@ public class TransportationTest extends AbstractLayerTest {
"ref", "3",
"ref_length", 1,
"network", "us-highway",
"route_1", "US:US=3",
"route_2", "US:MA=2",
"_minzoom", 12
)), process(lineFeatureWithRelation(
Stream.concat(
@@ -325,12 +620,17 @@ public class TransportationTest extends AbstractLayerTest {
), Map.of(
"_layer", "transportation_name",
"class", "primary",
"route_1", "US:US=3",
"route_2", "US:MA=2",
"ref", "3",
"network", "us-highway"
)), process(lineFeatureWithRelation(
Stream.concat(
profile.preprocessOsmRelation(relMA).stream(),
profile.preprocessOsmRelation(relUS).stream()
Stream.concat( // ignore duplicates
profile.preprocessOsmRelation(relUS).stream(),
profile.preprocessOsmRelation(relUS).stream()
)
).toList(),
Map.of(
"highway", "primary",
@@ -368,7 +668,8 @@ public class TransportationTest extends AbstractLayerTest {
public void testCompoundRef() {
assertFeatures(13, List.of(Map.of(
"_layer", "transportation",
"class", "primary"
"class", "primary",
"network", "<null>"
), Map.of(
"_layer", "transportation_name",
"class", "primary",
@@ -409,7 +710,7 @@ public class TransportationTest extends AbstractLayerTest {
"class", "motorway",
"surface", "paved",
"oneway", 1,
"ramp", 0,
"ramp", "<null>",
"_minzoom", 4
), Map.of(
"_layer", "transportation_name",
@@ -438,7 +739,7 @@ public class TransportationTest extends AbstractLayerTest {
"_layer", "transportation",
"class", "motorway",
"oneway", 1,
"ramp", 0,
"ramp", "<null>",
"_minzoom", 4
), Map.of(
"_layer", "transportation_name",
@@ -464,7 +765,7 @@ public class TransportationTest extends AbstractLayerTest {
"_layer", "transportation",
"class", "motorway",
"oneway", 1,
"ramp", 0,
"ramp", "<null>",
"_minzoom", 4
), Map.of(
"_layer", "transportation_name",
@@ -504,8 +805,8 @@ public class TransportationTest extends AbstractLayerTest {
"subclass", "light_rail",
"brunnel", "tunnel",
"layer", -1L,
"oneway", 0,
"ramp", 0,
"oneway", "<null>",
"ramp", "<null>",
"_minzoom", 11,
"_maxzoom", 14,
@@ -526,8 +827,8 @@ public class TransportationTest extends AbstractLayerTest {
"subclass", "subway",
"brunnel", "tunnel",
"layer", -2L,
"oneway", 0,
"ramp", 0,
"oneway", "<null>",
"ramp", "<null>",
"_minzoom", 14,
"_maxzoom", 14,
@@ -561,6 +862,7 @@ public class TransportationTest extends AbstractLayerTest {
"layer", "-2"
))));
assertFeatures(13, List.of(Map.of(
"layer", "<null>",
"_minzoom", 10
)), process(lineFeature(Map.of(
"railway", "rail",
@@ -599,11 +901,20 @@ public class TransportationTest extends AbstractLayerTest {
@Test
public void testAerialway() {
assertFeatures(10, List.of(Map.of(
assertFeatures(12, List.of(Map.of(
"_layer", "transportation",
"class", "aerialway",
"subclass", "gondola",
"_minzoom", 12,
"_maxzoom", 14,
"_type", "line"
), Map.of(
"_layer", "transportation_name",
"class", "aerialway",
"subclass", "gondola",
"name", "Summit Gondola",
"_minzoom", 12,
"_maxzoom", 14,
"_type", "line"
@@ -627,6 +938,14 @@ public class TransportationTest extends AbstractLayerTest {
"_minzoom", 11,
"_maxzoom", 14,
"_type", "line"
), Map.of(
"_layer", "transportation_name",
"class", "ferry",
"name", "Boston - Provincetown Ferry",
"_minzoom", 12,
"_maxzoom", 14,
"_type", "line"
)), process(lineFeature(Map.of(
"route", "ferry",
"name", "Boston - Provincetown Ferry",
@@ -718,4 +1037,25 @@ public class TransportationTest extends AbstractLayerTest {
getWaySortKey(Map.of("highway", "motorway", "layer", "-2"))
);
}
@Test
public void testTransportationNameLayerRequiresTransportationLayer() {
var profile = new BasemapProfile(translations, PlanetilerConfig.from(Arguments.of(
"only_layers", "transportation_name"
)), Stats.inMemory());
SourceFeature feature = lineFeature(Map.of(
"highway", "path",
"name", "test"
));
var collector = featureCollectorFactory.get(feature);
profile.processFeature(feature, collector);
assertFeatures(14, List.of(Map.of(
"_layer", "transportation_name",
"class", "path",
"name", "test"
), Map.of(
"_layer", "transportation",
"class", "path"
)), collector);
}
}

View File

@@ -132,7 +132,7 @@ public class WaterTest extends AbstractLayerTest {
"_minzoom", 6,
"_maxzoom", 14
)), process(polygonFeature(Map.of(
"waterway", "stream",
"waterway", "riverbank",
"bridge", "1",
"intermittent", "1"
))));
@@ -152,6 +152,39 @@ public class WaterTest extends AbstractLayerTest {
))));
}
@Test
public void testRiverbank() {
assertFeatures(11, List.of(Map.of(
"class", "river",
"_layer", "water",
"_type", "polygon"
)), process(polygonFeature(Map.of(
"waterway", "riverbank"
))));
}
@Test
public void testRiverk() {
assertFeatures(11, List.of(Map.of(
"class", "river",
"_layer", "water",
"_type", "polygon"
)), process(polygonFeature(Map.of(
"water", "river"
))));
}
@Test
public void testSpring() {
assertFeatures(11, List.of(Map.of(
"class", "lake",
"_layer", "water",
"_type", "polygon"
)), process(polygonFeature(Map.of(
"natural", "spring"
))));
}
@Test
public void testOceanZoomLevels() {
assertCoversZoomRange(0, 14, "water",

View File

@@ -2,17 +2,86 @@ package com.onthegomap.planetiler.basemap.layers;
import static com.onthegomap.planetiler.TestUtils.newLineString;
import static com.onthegomap.planetiler.basemap.BasemapProfile.NATURAL_EARTH_SOURCE;
import static com.onthegomap.planetiler.basemap.BasemapProfile.OSM_SOURCE;
import static org.junit.jupiter.api.Assertions.assertEquals;
import com.onthegomap.planetiler.FeatureCollector;
import com.onthegomap.planetiler.VectorTile;
import com.onthegomap.planetiler.geo.GeometryException;
import com.onthegomap.planetiler.reader.SimpleFeature;
import com.onthegomap.planetiler.reader.osm.OsmElement;
import com.onthegomap.planetiler.reader.osm.OsmReader;
import com.onthegomap.planetiler.reader.osm.OsmRelationInfo;
import java.util.ArrayList;
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.ValueSource;
public class WaterwayTest extends AbstractLayerTest {
@ParameterizedTest
@ValueSource(booleans = {false, true})
public void testOsmWaterwayRelation(boolean isLongEnough) throws GeometryException {
var rel = new OsmElement.Relation(1);
rel.setTag("name", "River Relation");
rel.setTag("name:es", "ES name");
rel.setTag("waterway", "river");
List<OsmRelationInfo> relationInfos = profile.preprocessOsmRelation(rel);
FeatureCollector features = process(SimpleFeature.createFakeOsmFeature(
newLineString(0, 0, 0, isLongEnough ? 3 : 1),
Map.of(),
OSM_SOURCE,
null,
0,
(relationInfos == null ? List.<OsmRelationInfo>of() : relationInfos).stream()
.map(r -> new OsmReader.RelationMember<>("", r)).toList()
));
assertFeatures(14, List.of(Map.of(
"class", "river",
"name", "River Relation",
"name:es", "ES name",
"_relid", 1L,
"_layer", "waterway",
"_type", "line",
"_minzoom", 6,
"_maxzoom", 8,
"_buffer", 4d
)), features);
// ensure that post-processing combines waterways, and filters out ones that
// belong to rivers that are not long enough to be shown
var line1 = new VectorTile.Feature(
Waterway.LAYER_NAME,
1,
VectorTile.encodeGeometry(newLineString(0, 0, 10, 0)),
mapOf("name", "river", "_relid", 1L),
0
);
var line2 = new VectorTile.Feature(
Waterway.LAYER_NAME,
1,
VectorTile.encodeGeometry(newLineString(10, 0, 20, 0)),
mapOf("name", "river", "_relid", 1L),
0
);
var connected = new VectorTile.Feature(
Waterway.LAYER_NAME,
1,
VectorTile.encodeGeometry(newLineString(0, 0, 20, 0)),
mapOf("name", "river"),
0
);
assertEquals(
isLongEnough ? List.of(connected) : List.of(),
profile.postProcessLayerFeatures(Waterway.LAYER_NAME, 8, new ArrayList<>(List.of(line1, line2)))
);
}
@Test
public void testWaterwayImportantRiverProcess() {
var charlesRiver = process(lineFeature(Map.of(
@@ -163,24 +232,5 @@ public class WaterwayTest extends AbstractLayerTest {
"ne_50m_rivers_lake_centerlines",
0
)));
assertFeatures(6, List.of(Map.of(
"class", "river",
"intermittent", "<null>",
"_layer", "waterway",
"_type", "line",
"_minzoom", 6,
"_maxzoom", 8
)), process(SimpleFeature.create(
newLineString(0, 0, 1, 1),
Map.of(
"featurecla", "River",
"name", "name"
),
NATURAL_EARTH_SOURCE,
"ne_10m_rivers_lake_centerlines",
0
)));
}
}