maps/marteen/build/resources/process-openmaptiles.lua

941 lines
28 KiB
Lua
Raw Permalink Normal View History

2023-04-18 23:54:59 -04:00
-- Data processing based on openmaptiles.org schema
-- https://openmaptiles.org/schema/
-- Copyright (c) 2016, KlokanTech.com & OpenMapTiles contributors.
-- Used under CC-BY 4.0
--------
-- Alter these lines to control which languages are written for place/streetnames
--
-- Preferred language can be (for example) "en" for English, "de" for German, or nil to use OSM's name tag:
2023-04-19 02:13:14 -04:00
preferred_language = en
2023-04-18 23:54:59 -04:00
-- This is written into the following vector tile attribute (usually "name:latin"):
preferred_language_attribute = "name:latin"
-- If OSM's name tag differs, then write it into this attribute (usually "name_int"):
default_language_attribute = "name_int"
-- Also write these languages if they differ - for example, { "de", "fr" }
2023-04-19 02:13:14 -04:00
additional_languages = {}
2023-04-18 23:54:59 -04:00
--------
-- Enter/exit Tilemaker
function init_function()
end
2023-04-19 02:13:14 -04:00
2023-04-18 23:54:59 -04:00
function exit_function()
end
-- Implement Sets in tables
function Set(list)
local set = {}
for _, l in ipairs(list) do set[l] = true end
return set
end
-- Meters per pixel if tile is 256x256
2023-04-19 02:13:14 -04:00
ZRES5 = 4891.97
ZRES6 = 2445.98
ZRES7 = 1222.99
ZRES8 = 611.5
ZRES9 = 305.7
ZRES10 = 152.9
ZRES11 = 76.4
ZRES12 = 38.2
ZRES13 = 19.1
2023-04-18 23:54:59 -04:00
-- The height of one floor, in meters
BUILDING_FLOOR_HEIGHT = 3.66
-- Process node/way tags
2023-04-19 02:13:14 -04:00
aerodromeValues = Set { "international", "public", "regional", "military", "private" }
2023-04-18 23:54:59 -04:00
-- Process node tags
2023-04-19 02:13:14 -04:00
node_keys = { "addr:housenumber", "aerialway", "aeroway", "amenity", "barrier", "highway", "historic",
"leisure", "natural", "office", "place", "railway", "shop", "sport", "tourism", "waterway" }
2023-04-18 23:54:59 -04:00
function node_function(node)
-- Write 'aerodrome_label'
local aeroway = node:Find("aeroway")
if aeroway == "aerodrome" then
node:Layer("aerodrome_label", false)
SetNameAttributes(node)
node:Attribute("iata", node:Find("iata"))
SetEleAttributes(node)
node:Attribute("icao", node:Find("icao"))
local aerodrome_value = node:Find("aerodrome")
local class
if aerodromeValues[aerodrome_value] then class = aerodrome_value else class = "other" end
node:Attribute("class", class)
end
-- Write 'housenumber'
local housenumber = node:Find("addr:housenumber")
2023-04-19 02:13:14 -04:00
if housenumber ~= "" then
2023-04-18 23:54:59 -04:00
node:Layer("housenumber", false)
node:Attribute("housenumber", housenumber)
end
-- Write 'place'
-- note that OpenMapTiles has a rank for countries (1-3), states (1-6) and cities (1-10+);
-- we could potentially approximate it for cities based on the population tag
local place = node:Find("place")
if place ~= "" then
local rank = nil
local mz = 13
local pop = tonumber(node:Find("population")) or 0
2023-04-19 02:13:14 -04:00
if place == "continent" then
mz = 0
elseif place == "country" then
if pop > 50000000 then
rank = 1;
mz = 1
elseif pop > 20000000 then
rank = 2;
mz = 2
else
rank = 3;
mz = 3
end
elseif place == "state" then
mz = 4
elseif place == "city" then
mz = 5
elseif place == "town" and pop > 8000 then
mz = 7
elseif place == "town" then
mz = 8
elseif place == "village" and pop > 2000 then
mz = 9
elseif place == "village" then
mz = 10
elseif place == "suburb" then
mz = 11
elseif place == "hamlet" then
mz = 12
elseif place == "neighbourhood" then
mz = 13
elseif place == "locality" then
mz = 13
2023-04-18 23:54:59 -04:00
end
node:Layer("place", false)
node:Attribute("class", place)
node:MinZoom(mz)
if rank then node:AttributeNumeric("rank", rank) end
2023-04-19 02:13:14 -04:00
if place == "country" then node:Attribute("iso_a2", node:Find("ISO3166-1:alpha2")) end
2023-04-18 23:54:59 -04:00
SetNameAttributes(node)
return
end
-- Write 'poi'
local rank, class, subclass = GetPOIRank(node)
2023-04-19 02:13:14 -04:00
if rank then WritePOI(node, class, subclass, rank) end
2023-04-18 23:54:59 -04:00
-- Write 'mountain_peak' and 'water_name'
local natural = node:Find("natural")
if natural == "peak" or natural == "volcano" then
node:Layer("mountain_peak", false)
SetEleAttributes(node)
node:AttributeNumeric("rank", 1)
node:Attribute("class", natural)
SetNameAttributes(node)
return
end
if natural == "bay" then
node:Layer("water_name", false)
SetNameAttributes(node)
return
end
end
-- Process way tags
2023-04-19 02:13:14 -04:00
majorRoadValues = Set { "motorway", "trunk", "primary" }
mainRoadValues = Set { "secondary", "motorway_link", "trunk_link", "primary_link", "secondary_link" }
midRoadValues = Set { "tertiary", "tertiary_link" }
minorRoadValues = Set { "unclassified", "residential", "road", "living_street" }
trackValues = Set { "track" }
pathValues = Set { "footway", "cycleway", "bridleway", "path", "steps", "pedestrian" }
linkValues = Set { "motorway_link", "trunk_link", "primary_link", "secondary_link", "tertiary_link" }
2023-04-18 23:54:59 -04:00
constructionValues = Set { "primary", "secondary", "tertiary", "motorway", "service", "trunk", "track" }
2023-04-19 02:13:14 -04:00
pavedValues = Set { "paved", "asphalt", "cobblestone", "concrete", "concrete:lanes", "concrete:plates", "metal",
"paving_stones", "sett", "unhewn_cobblestone", "wood" }
unpavedValues = Set { "unpaved", "compacted", "dirt", "earth", "fine_gravel", "grass", "grass_paver", "gravel",
"gravel_turf", "ground", "ice", "mud", "pebblestone", "salt", "sand", "snow", "woodchips" }
aerowayBuildings = Set { "terminal", "gate", "tower" }
landuseKeys = Set { "school", "university", "kindergarten", "college", "library", "hospital",
"railway", "cemetery", "military", "residential", "commercial", "industrial",
"retail", "stadium", "pitch", "playground", "theme_park", "bus_station", "zoo" }
landcoverKeys = {
wood = "wood",
forest = "wood",
wetland = "wetland",
beach = "sand",
sand = "sand",
farmland = "farmland",
farm = "farmland",
orchard = "farmland",
vineyard = "farmland",
plant_nursery = "farmland",
glacier = "ice",
ice_shelf = "ice",
grassland = "grass",
grass = "grass",
meadow = "grass",
allotments = "grass",
park = "grass",
village_green = "grass",
recreation_ground = "grass",
garden = "grass",
golf_course = "grass"
}
2023-04-18 23:54:59 -04:00
-- POI key/value pairs: based on https://github.com/openmaptiles/openmaptiles/blob/master/layers/poi/mapping.yaml
2023-04-19 02:13:14 -04:00
poiTags = {
aerialway = Set { "station" },
amenity = Set { "arts_centre", "bank", "bar", "bbq", "bicycle_parking", "bicycle_rental", "biergarten", "bus_station",
"cafe", "cinema", "clinic", "college", "community_centre", "courthouse", "dentist", "doctors", "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", "public_building", "recycling",
"restaurant", "school", "shelter", "swimming_pool", "taxi", "telephone", "theatre", "toilets", "townhall",
"university", "veterinary", "waste_basket" },
barrier = Set { "bollard", "border_control", "cycle_barrier", "gate", "lift_gate", "sally_port", "stile", "toll_booth" },
building = Set { "dormitory" },
highway = Set { "bus_stop" },
historic = Set { "monument", "castle", "ruins" },
landuse = Set { "basin", "brownfield", "cemetery", "reservoir", "winter_sports" },
leisure = Set { "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" },
railway = Set { "halt", "station", "subway_entrance", "train_station_entrance", "tram_stop" },
shop = Set { "accessories", "alcohol", "antiques", "art", "bag", "bakery", "beauty", "bed", "beverages", "bicycle",
"books", "boutique", "butcher", "camera", "car", "car_repair", "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" },
sport = Set { "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", "disc_golf", "diving", "dog_racing",
"equestrian", "fatsal", "field_hockey", "free_flying", "gaelic_games", "golf", "gymnastics", "handball", "hockey",
"horse_racing", "horseshoes", "ice_hockey", "ice_stock", "judo", "karting", "korfball", "long_jump",
"model_aerodrome", "motocross", "motor", "multi", "netball", "orienteering", "paddle_tennis", "paintball",
"paragliding", "pelota", "racquet", "rc_car", "rowing", "rugby", "rugby_league", "rugby_union", "running", "sailing",
"scuba_diving", "shooting", "shooting_range", "skateboard", "skating", "skiing", "soccer", "surfing", "swimming",
"table_soccer", "table_tennis", "team_handball", "tennis", "toboggan", "volleyball", "water_ski", "yoga" },
tourism = Set { "alpine_hut", "aquarium", "artwork", "attraction", "bed_and_breakfast", "camp_site", "caravan_site",
"chalet", "gallery", "guest_house", "hostel", "hotel", "information", "motel", "museum", "picnic_site", "theme_park",
"viewpoint", "zoo" },
waterway = Set { "dock" }
}
2023-04-18 23:54:59 -04:00
-- POI "class" values: based on https://github.com/openmaptiles/openmaptiles/blob/master/layers/poi/poi.yaml
2023-04-19 02:13:14 -04:00
poiClasses = {
townhall = "town_hall",
public_building = "town_hall",
courthouse = "town_hall",
community_centre = "town_hall",
golf = "golf",
golf_course = "golf",
miniature_golf = "golf",
fast_food = "fast_food",
food_court = "fast_food",
park = "park",
bbq = "park",
bus_stop = "bus",
bus_station = "bus",
subway_entrance = "entrance",
train_station_entrance = "entrance",
camp_site = "campsite",
caravan_site = "campsite",
laundry = "laundry",
dry_cleaning = "laundry",
supermarket = "grocery",
deli = "grocery",
delicatessen = "grocery",
department_store = "grocery",
greengrocer = "grocery",
marketplace = "grocery",
books = "library",
library = "library",
university = "college",
college = "college",
hotel = "lodging",
motel = "lodging",
bed_and_breakfast = "lodging",
guest_house = "lodging",
hostel = "lodging",
chalet = "lodging",
alpine_hut = "lodging",
dormitory = "lodging",
chocolate = "ice_cream",
confectionery = "ice_cream",
post_box = "post",
post_office = "post",
cafe = "cafe",
school = "school",
kindergarten = "school",
alcohol = "alcohol_shop",
beverages = "alcohol_shop",
wine = "alcohol_shop",
bar = "bar",
nightclub = "bar",
marina = "harbor",
dock = "harbor",
car = "car",
car_repair = "car",
taxi = "car",
hospital = "hospital",
nursing_home = "hospital",
clinic = "hospital",
grave_yard = "cemetery",
cemetery = "cemetery",
attraction = "attraction",
viewpoint = "attraction",
biergarten = "beer",
pub = "beer",
music = "music",
musical_instrument = "music",
american_football = "stadium",
stadium = "stadium",
soccer = "stadium",
art = "art_gallery",
artwork = "art_gallery",
gallery = "art_gallery",
arts_centre = "art_gallery",
bag = "clothing_store",
clothes = "clothing_store",
swimming_area = "swimming",
swimming = "swimming",
castle = "castle",
ruins = "castle"
}
poiClassRanks = {
hospital = 1,
railway = 2,
bus = 3,
attraction = 4,
harbor = 5,
college = 6,
school = 7,
stadium = 8,
zoo = 9,
town_hall = 10,
campsite = 11,
cemetery = 12,
park = 13,
library = 14,
police = 15,
post = 16,
golf = 17,
shop = 18,
grocery = 19,
fast_food = 20,
clothing_store = 21,
bar = 22
}
waterClasses = Set { "river", "riverbank", "stream", "canal", "drain", "ditch", "dock" }
waterwayClasses = Set { "stream", "river", "canal", "drain", "ditch" }
2023-04-18 23:54:59 -04:00
-- Scan relations for use in ways
function relation_scan_function(relation)
2023-04-19 02:13:14 -04:00
if relation:Find("type") == "boundary" and relation:Find("boundary") == "administrative" then
2023-04-18 23:54:59 -04:00
relation:Accept()
end
end
-- Process way tags
function way_function(way)
2023-04-19 02:13:14 -04:00
local route = way:Find("route")
local highway = way:Find("highway")
local waterway = way:Find("waterway")
local water = way:Find("water")
local building = way:Find("building")
local natural = way:Find("natural")
local historic = way:Find("historic")
local landuse = way:Find("landuse")
local leisure = way:Find("leisure")
local amenity = way:Find("amenity")
local aeroway = way:Find("aeroway")
local railway = way:Find("railway")
local service = way:Find("service")
local sport = way:Find("sport")
local shop = way:Find("shop")
local tourism = way:Find("tourism")
local man_made = way:Find("man_made")
local boundary = way:Find("boundary")
local isClosed = way:IsClosed()
local housenumber = way:Find("addr:housenumber")
local write_name = false
2023-04-18 23:54:59 -04:00
local construction = way:Find("construction")
-- Miscellaneous preprocessing
if way:Find("disused") == "yes" then return end
2023-04-19 02:13:14 -04:00
if boundary ~= "" and way:Find("protection_title") == "National Forest" and way:Find("operator") == "United States Forest Service" then return end
2023-04-18 23:54:59 -04:00
if highway == "proposed" then return end
2023-04-19 02:13:14 -04:00
if aerowayBuildings[aeroway] then
building = "yes";
aeroway = ""
end
2023-04-18 23:54:59 -04:00
if landuse == "field" then landuse = "farmland" end
2023-04-19 02:13:14 -04:00
if landuse == "meadow" and way:Find("meadow") == "agricultural" then landuse = "farmland" end
2023-04-18 23:54:59 -04:00
-- Boundaries within relations
local admin_level = 11
local isBoundary = false
while true do
local rel = way:NextRelation()
if not rel then break end
isBoundary = true
admin_level = math.min(admin_level, tonumber(way:FindInRelation("admin_level")) or 11)
end
-- Boundaries in ways
2023-04-19 02:13:14 -04:00
if boundary == "administrative" then
2023-04-18 23:54:59 -04:00
admin_level = math.min(admin_level, tonumber(way:Find("admin_level")) or 11)
isBoundary = true
end
2023-04-19 02:13:14 -04:00
2023-04-18 23:54:59 -04:00
-- Administrative boundaries
-- https://openmaptiles.org/schema/#boundary
2023-04-19 02:13:14 -04:00
if isBoundary and not (way:Find("maritime") == "yes") then
2023-04-18 23:54:59 -04:00
local mz = 0
2023-04-19 02:13:14 -04:00
if admin_level >= 3 and admin_level < 5 then
mz = 4
elseif admin_level >= 5 and admin_level < 7 then
mz = 8
elseif admin_level == 7 then
mz = 10
elseif admin_level >= 8 then
mz = 12
2023-04-18 23:54:59 -04:00
end
2023-04-19 02:13:14 -04:00
way:Layer("boundary", false)
2023-04-18 23:54:59 -04:00
way:AttributeNumeric("admin_level", admin_level)
way:MinZoom(mz)
-- disputed status (0 or 1). some styles need to have the 0 to show it.
local disputed = way:Find("disputed")
2023-04-19 02:13:14 -04:00
if disputed == "yes" then
2023-04-18 23:54:59 -04:00
way:AttributeNumeric("disputed", 1)
else
way:AttributeNumeric("disputed", 0)
end
end
-- Roads ('transportation' and 'transportation_name', plus 'transportation_name_detail')
2023-04-19 02:13:14 -04:00
if highway ~= "" then
2023-04-18 23:54:59 -04:00
local access = way:Find("access")
local surface = way:Find("surface")
local h = highway
local minzoom = 99
local layer = "transportation"
2023-04-19 02:13:14 -04:00
if majorRoadValues[highway] then minzoom = 4 end
if highway == "trunk" then
minzoom = 5
elseif highway == "primary" then
minzoom = 7
end
if mainRoadValues[highway] then minzoom = 9 end
if midRoadValues[highway] then minzoom = 11 end
if minorRoadValues[highway] then
h = "minor";
minzoom = 12
end
if trackValues[highway] then
h = "track";
minzoom = 14
end
if pathValues[highway] then
h = "path";
minzoom = 14
end
if h == "service" then minzoom = 12 end
2023-04-18 23:54:59 -04:00
-- Links (ramp)
2023-04-19 02:13:14 -04:00
local ramp = false
2023-04-18 23:54:59 -04:00
if linkValues[highway] then
splitHighway = split(highway, "_")
2023-04-19 02:13:14 -04:00
highway = splitHighway[1];
h = highway
2023-04-18 23:54:59 -04:00
ramp = true
minzoom = 11
end
-- Construction
if highway == "construction" then
if constructionValues[construction] then
h = construction .. "_construction"
if construction ~= "service" and construction ~= "track" then
minzoom = 11
else
minzoom = 12
end
else
h = "minor_construction"
minzoom = 14
end
end
-- Write to layer
if minzoom <= 14 then
way:Layer(layer, false)
way:MinZoom(minzoom)
SetZOrder(way)
way:Attribute("class", h)
SetBrunnelAttributes(way)
2023-04-19 02:13:14 -04:00
if ramp then way:AttributeNumeric("ramp", 1) end
if access == "private" or access == "no" then way:Attribute("access", "no") end
2023-04-18 23:54:59 -04:00
if pavedValues[surface] then way:Attribute("surface", "paved") end
if unpavedValues[surface] then way:Attribute("surface", "unpaved") end
-- Service
2023-04-19 02:13:14 -04:00
if highway == "service" and service ~= "" then way:Attribute("service", service) end
2023-04-18 23:54:59 -04:00
local oneway = way:Find("oneway")
if oneway == "yes" or oneway == "1" then
2023-04-19 02:13:14 -04:00
way:AttributeNumeric("oneway", 1)
2023-04-18 23:54:59 -04:00
end
if oneway == "-1" then
-- **** TODO
end
-- Write names
if minzoom < 8 then
minzoom = 8
end
if highway == "motorway" or highway == "trunk" then
way:Layer("transportation_name", false)
way:MinZoom(minzoom)
elseif h == "minor" or h == "track" or h == "path" or h == "service" then
way:Layer("transportation_name_detail", false)
way:MinZoom(minzoom)
else
way:Layer("transportation_name_mid", false)
way:MinZoom(minzoom)
end
SetNameAttributes(way)
2023-04-19 02:13:14 -04:00
way:Attribute("class", h)
way:Attribute("network", "road") -- **** could also be us-interstate, us-highway, us-state
if h ~= highway then way:Attribute("subclass", highway) end
2023-04-18 23:54:59 -04:00
local ref = way:Find("ref")
2023-04-19 02:13:14 -04:00
if ref ~= "" then
way:Attribute("ref", ref)
way:AttributeNumeric("ref_length", ref:len())
2023-04-18 23:54:59 -04:00
end
end
end
-- Railways ('transportation' and 'transportation_name', plus 'transportation_name_detail')
2023-04-19 02:13:14 -04:00
if railway ~= "" then
2023-04-18 23:54:59 -04:00
way:Layer("transportation", false)
way:Attribute("class", railway)
SetZOrder(way)
SetBrunnelAttributes(way)
2023-04-19 02:13:14 -04:00
if service ~= "" then
2023-04-18 23:54:59 -04:00
way:Attribute("service", service)
way:MinZoom(12)
else
way:MinZoom(9)
end
way:Layer("transportation_name", false)
SetNameAttributes(way)
way:MinZoom(14)
way:Attribute("class", "rail")
end
-- Pier
2023-04-19 02:13:14 -04:00
if man_made == "pier" then
2023-04-18 23:54:59 -04:00
way:Layer("transportation", isClosed)
SetZOrder(way)
way:Attribute("class", "pier")
SetMinZoomByArea(way)
end
-- 'Ferry'
2023-04-19 02:13:14 -04:00
if route == "ferry" then
2023-04-18 23:54:59 -04:00
way:Layer("transportation", false)
way:Attribute("class", "ferry")
SetZOrder(way)
way:MinZoom(9)
SetBrunnelAttributes(way)
way:Layer("transportation_name", false)
SetNameAttributes(way)
way:MinZoom(12)
way:Attribute("class", "ferry")
end
-- 'Aeroway'
2023-04-19 02:13:14 -04:00
if aeroway ~= "" then
2023-04-18 23:54:59 -04:00
way:Layer("aeroway", isClosed)
2023-04-19 02:13:14 -04:00
way:Attribute("class", aeroway)
way:Attribute("ref", way:Find("ref"))
2023-04-18 23:54:59 -04:00
write_name = true
end
-- 'aerodrome_label'
2023-04-19 02:13:14 -04:00
if aeroway == "aerodrome" then
way:LayerAsCentroid("aerodrome_label")
SetNameAttributes(way)
way:Attribute("iata", way:Find("iata"))
SetEleAttributes(way)
way:Attribute("icao", way:Find("icao"))
2023-04-18 23:54:59 -04:00
2023-04-19 02:13:14 -04:00
local aerodrome = way:Find(aeroway)
local class
if aerodromeValues[aerodrome] then class = aerodrome else class = "other" end
way:Attribute("class", class)
2023-04-18 23:54:59 -04:00
end
-- Set 'waterway' and associated
if waterwayClasses[waterway] and not isClosed then
if waterway == "river" and way:Holds("name") then
way:Layer("waterway", false)
else
way:Layer("waterway_detail", false)
end
2023-04-19 02:13:14 -04:00
if way:Find("intermittent") == "yes" then
way:AttributeNumeric("intermittent", 1)
else
way:AttributeNumeric(
"intermittent", 0)
end
2023-04-18 23:54:59 -04:00
way:Attribute("class", waterway)
SetNameAttributes(way)
SetBrunnelAttributes(way)
2023-04-19 02:13:14 -04:00
elseif waterway == "boatyard" then
way:Layer("landuse", isClosed);
way:Attribute("class", "industrial");
way:MinZoom(12)
elseif waterway == "dam" then
way:Layer("building", isClosed)
elseif waterway == "fuel" then
way:Layer("landuse", isClosed);
way:Attribute("class", "industrial");
way:MinZoom(14)
2023-04-18 23:54:59 -04:00
end
-- Set names on rivers
if waterwayClasses[waterway] and not isClosed then
if waterway == "river" and way:Holds("name") then
way:Layer("water_name", false)
else
way:Layer("water_name_detail", false)
way:MinZoom(14)
end
way:Attribute("class", waterway)
SetNameAttributes(way)
end
-- Set 'building' and associated
2023-04-19 02:13:14 -04:00
if building ~= "" then
2023-04-18 23:54:59 -04:00
way:Layer("building", true)
SetBuildingHeightAttributes(way)
SetMinZoomByArea(way)
end
-- Set 'housenumber'
2023-04-19 02:13:14 -04:00
if housenumber ~= "" then
2023-04-18 23:54:59 -04:00
way:LayerAsCentroid("housenumber", false)
way:Attribute("housenumber", housenumber)
end
-- Set 'water'
2023-04-19 02:13:14 -04:00
if natural == "water" or natural == "bay" or leisure == "swimming_pool" or landuse == "reservoir" or landuse == "basin" or waterClasses[waterway] then
if way:Find("covered") == "yes" or not isClosed then return end
local class = "lake";
if natural == "bay" then class = "ocean" elseif waterway ~= "" then class = "river" end
if class == "lake" and way:Find("wikidata") == "Q192770" then return end
if class == "ocean" and isClosed and (way:AreaIntersecting("ocean") / way:Area() > 0.98) then return end
way:Layer("water", true)
2023-04-18 23:54:59 -04:00
SetMinZoomByArea(way)
2023-04-19 02:13:14 -04:00
way:Attribute("class", class)
2023-04-18 23:54:59 -04:00
2023-04-19 02:13:14 -04:00
if way:Find("intermittent") == "yes" then way:Attribute("intermittent", 1) end
2023-04-18 23:54:59 -04:00
-- we only want to show the names of actual lakes not every man-made basin that probably doesn't even have a name other than "basin"
-- examples for which we don't want to show a name:
-- https://www.openstreetmap.org/way/25958687
-- https://www.openstreetmap.org/way/27201902
-- https://www.openstreetmap.org/way/25309134
-- https://www.openstreetmap.org/way/24579306
2023-04-19 02:13:14 -04:00
if way:Holds("name") and natural == "water" and water ~= "basin" and water ~= "wastewater" then
2023-04-18 23:54:59 -04:00
way:LayerAsCentroid("water_name_detail")
SetNameAttributes(way)
SetMinZoomByArea(way)
way:Attribute("class", class)
end
return -- in case we get any landuse processing
end
-- Set 'landcover' (from landuse, natural, leisure)
local l = landuse
2023-04-19 02:13:14 -04:00
if l == "" then l = natural end
if l == "" then l = leisure end
2023-04-18 23:54:59 -04:00
if landcoverKeys[l] then
way:Layer("landcover", true)
SetMinZoomByArea(way)
way:Attribute("class", landcoverKeys[l])
2023-04-19 02:13:14 -04:00
if l == "wetland" then
way:Attribute("subclass", way:Find("wetland"))
else
way:Attribute("subclass", l)
end
2023-04-18 23:54:59 -04:00
write_name = true
2023-04-19 02:13:14 -04:00
-- Set 'landuse'
2023-04-18 23:54:59 -04:00
else
2023-04-19 02:13:14 -04:00
if l == "" then l = amenity end
if l == "" then l = tourism end
2023-04-18 23:54:59 -04:00
if landuseKeys[l] then
way:Layer("landuse", true)
way:Attribute("class", l)
2023-04-19 02:13:14 -04:00
if l == "residential" then
if way:Area() < ZRES8 ^ 2 then
way:MinZoom(8)
else
SetMinZoomByArea(way)
end
else
way:MinZoom(11)
end
2023-04-18 23:54:59 -04:00
write_name = true
end
end
-- Parks
-- **** name?
2023-04-19 02:13:14 -04:00
if boundary == "national_park" then
way:Layer("park", true);
way:Attribute("class", boundary);
SetNameAttributes(way)
elseif leisure == "nature_reserve" then
way:Layer("park", true);
way:Attribute("class", leisure);
SetNameAttributes(way)
end
2023-04-18 23:54:59 -04:00
-- POIs ('poi' and 'poi_detail')
local rank, class, subclass = GetPOIRank(way)
2023-04-19 02:13:14 -04:00
if rank then
WritePOI(way, class, subclass, rank);
return
end
2023-04-18 23:54:59 -04:00
-- Catch-all
2023-04-19 02:13:14 -04:00
if (building ~= "" or write_name) and way:Holds("name") then
2023-04-18 23:54:59 -04:00
way:LayerAsCentroid("poi_detail")
SetNameAttributes(way)
2023-04-19 02:13:14 -04:00
if write_name then rank = 6 else rank = 25 end
2023-04-18 23:54:59 -04:00
way:AttributeNumeric("rank", rank)
end
end
-- Remap coastlines
2023-04-19 02:13:14 -04:00
function attribute_function(attr, layer)
if attr["featurecla"] == "Glaciated areas" then
return { subclass = "glacier" }
elseif attr["featurecla"] == "Antarctic Ice Shelf" then
return { subclass = "ice_shelf" }
elseif attr["featurecla"] == "Urban area" then
return { class = "residential" }
2023-04-18 23:54:59 -04:00
else
2023-04-19 02:13:14 -04:00
return { class = "ocean" }
2023-04-18 23:54:59 -04:00
end
end
-- ==========================================================
-- Common functions
-- Write a way centroid to POI layer
2023-04-19 02:13:14 -04:00
function WritePOI(obj, class, subclass, rank)
2023-04-18 23:54:59 -04:00
local layer = "poi"
2023-04-19 02:13:14 -04:00
if rank > 4 then layer = "poi_detail" end
2023-04-18 23:54:59 -04:00
obj:LayerAsCentroid(layer)
SetNameAttributes(obj)
obj:AttributeNumeric("rank", rank)
obj:Attribute("class", class)
obj:Attribute("subclass", subclass)
end
-- Set name attributes on any object
function SetNameAttributes(obj)
local name = obj:Find("name"), iname
local main_written = name
-- if we have a preferred language, then write that (if available), and additionally write the base name tag
2023-04-19 02:13:14 -04:00
if preferred_language and obj:Holds("name:" .. preferred_language) then
iname = obj:Find("name:" .. preferred_language)
2023-04-18 23:54:59 -04:00
obj:Attribute(preferred_language_attribute, iname)
2023-04-19 02:13:14 -04:00
if iname ~= name and default_language_attribute then
2023-04-18 23:54:59 -04:00
obj:Attribute(default_language_attribute, name)
2023-04-19 02:13:14 -04:00
else
main_written = iname
end
2023-04-18 23:54:59 -04:00
else
obj:Attribute(preferred_language_attribute, name)
end
-- then set any additional languages
2023-04-19 02:13:14 -04:00
for i, lang in ipairs(additional_languages) do
iname = obj:Find("name:" .. lang)
if iname == "" then iname = name end
if iname ~= main_written then obj:Attribute("name:" .. lang, iname) end
2023-04-18 23:54:59 -04:00
end
end
-- Set ele and ele_ft on any object
function SetEleAttributes(obj)
2023-04-19 02:13:14 -04:00
local ele = obj:Find("ele")
2023-04-18 23:54:59 -04:00
if ele ~= "" then
local meter = math.floor(tonumber(ele) or 0)
local feet = math.floor(meter * 3.2808399)
obj:AttributeNumeric("ele", meter)
obj:AttributeNumeric("ele_ft", feet)
2023-04-19 02:13:14 -04:00
end
2023-04-18 23:54:59 -04:00
end
function SetBrunnelAttributes(obj)
2023-04-19 02:13:14 -04:00
if obj:Find("bridge") == "yes" then
obj:Attribute("brunnel", "bridge")
elseif obj:Find("tunnel") == "yes" then
obj:Attribute("brunnel", "tunnel")
elseif obj:Find("ford") == "yes" then
obj:Attribute("brunnel", "ford")
2023-04-18 23:54:59 -04:00
end
end
-- Set minimum zoom level by area
function SetMinZoomByArea(way)
2023-04-19 02:13:14 -04:00
local area = way:Area()
if area > ZRES5 ^ 2 then
way:MinZoom(6)
elseif area > ZRES6 ^ 2 then
way:MinZoom(7)
elseif area > ZRES7 ^ 2 then
way:MinZoom(8)
elseif area > ZRES8 ^ 2 then
way:MinZoom(9)
elseif area > ZRES9 ^ 2 then
way:MinZoom(10)
elseif area > ZRES10 ^ 2 then
way:MinZoom(11)
elseif area > ZRES11 ^ 2 then
way:MinZoom(12)
elseif area > ZRES12 ^ 2 then
way:MinZoom(13)
else
way:MinZoom(14)
end
2023-04-18 23:54:59 -04:00
end
-- Calculate POIs (typically rank 1-4 go to 'poi' z12-14, rank 5+ to 'poi_detail' z14)
-- returns rank, class, subclass
function GetPOIRank(obj)
2023-04-19 02:13:14 -04:00
local k, list, v, class, rank
2023-04-18 23:54:59 -04:00
-- Can we find the tag?
2023-04-19 02:13:14 -04:00
for k, list in pairs(poiTags) do
2023-04-18 23:54:59 -04:00
if list[obj:Find(k)] then
2023-04-19 02:13:14 -04:00
v = obj:Find(k) -- k/v are the OSM tag pair
2023-04-18 23:54:59 -04:00
class = poiClasses[v] or k
rank = poiClassRanks[class] or 25
return rank, class, v
end
end
-- Catch-all for shops
local shop = obj:Find("shop")
2023-04-19 02:13:14 -04:00
if shop ~= "" then return poiClassRanks['shop'], "shop", shop end
2023-04-18 23:54:59 -04:00
-- Nothing found
2023-04-19 02:13:14 -04:00
return nil, nil, nil
2023-04-18 23:54:59 -04:00
end
function SetBuildingHeightAttributes(way)
local height = tonumber(way:Find("height"), 10)
local minHeight = tonumber(way:Find("min_height"), 10)
local levels = tonumber(way:Find("building:levels"), 10)
local minLevel = tonumber(way:Find("building:min_level"), 10)
local renderHeight = BUILDING_FLOOR_HEIGHT
if height or levels then
renderHeight = height or (levels * BUILDING_FLOOR_HEIGHT)
end
local renderMinHeight = 0
if minHeight or minLevel then
renderMinHeight = minHeight or (minLevel * BUILDING_FLOOR_HEIGHT)
end
-- Fix upside-down buildings
if renderHeight < renderMinHeight then
renderHeight = renderHeight + renderMinHeight
end
way:AttributeNumeric("render_height", renderHeight)
way:AttributeNumeric("render_min_height", renderMinHeight)
end
-- Implement z_order as calculated by Imposm
-- See https://imposm.org/docs/imposm3/latest/mapping.html#wayzorder for details.
function SetZOrder(way)
local highway = way:Find("highway")
local layer = tonumber(way:Find("layer"))
local bridge = way:Find("bridge")
local tunnel = way:Find("tunnel")
local zOrder = 0
if bridge ~= "" and bridge ~= "no" then
zOrder = zOrder + 10
elseif tunnel ~= "" and tunnel ~= "no" then
zOrder = zOrder - 10
end
if not (layer == nil) then
if layer > 7 then
layer = 7
elseif layer < -7 then
layer = -7
end
zOrder = zOrder + layer * 10
end
local hwClass = 0
-- See https://github.com/omniscale/imposm3/blob/53bb80726ca9456e4a0857b38803f9ccfe8e33fd/mapping/columns.go#L251
if highway == "motorway" then
hwClass = 9
elseif highway == "trunk" then
hwClass = 8
elseif highway == "primary" then
hwClass = 6
elseif highway == "secondary" then
hwClass = 5
elseif highway == "tertiary" then
hwClass = 4
else
hwClass = 3
end
zOrder = zOrder + hwClass
way:ZOrder(zOrder)
end
-- ==========================================================
-- Lua utility functions
function split(inputstr, sep) -- https://stackoverflow.com/a/7615129/4288232
if sep == nil then
sep = "%s"
end
2023-04-19 02:13:14 -04:00
local t = {};
i = 1
for str in string.gmatch(inputstr, "([^" .. sep .. "]+)") do
2023-04-18 23:54:59 -04:00
t[i] = str
i = i + 1
end
return t
end
-- vim: tabstop=2 shiftwidth=2 noexpandtab