···88- User can download regional maps via the app
99- Downloaded maps are displayed offline, and overlay the world basemap
10101111-
1212-1311## Technologies for App
14121513- **[civility](https://jsr.io/@civility)**: PWA build system and service worker framework used to bundle the app and manage offline caching
···11-# Docs
22-33-This dir holds some auxillary information and scripts for the project.
44-55-## geofabrik
66-77-Basically, to fully realize a project like this, we will need scripts to organize the different downloadable regions, and also to find the urls for us to download. This doesn't include region naming/localization, so maybe need to figure that out later.
88-99-The files we need are:
1010-1111-1. `[insert-region]-latest.osm.pbf`: map data
1212-2. `[insert-region].poly`: the borders of each region
1313-1414-## tilemaker
1515-1616-We want tilemaker to create tiles that are compatible with our protomaps styles. Basically, you just need these files in the directory when you need to process your pbf:
1717-1818-```
1919-tilemaker monaco-latest.osm.pbf monaco.pmtiles
2020-```
-73
docs/geofabrik/get-geofabrik-regions.ts
···11-/**
22- * All of these regions should have a `.poly` and a `-latest.osm.pbf` file
33- * associated to them.
44- */
55-import * as cheerio from 'npm:cheerio'
66-77-const baseURL = 'https://download.geofabrik.de/'
88-const regions = new Set()
99-1010-// antarctica has no children, so not in baseRegions, but add to regions
1111-const baseRegions = [
1212- 'australia-oceania',
1313- 'asia',
1414- 'africa',
1515- 'central-america',
1616- 'europe',
1717- 'north-america',
1818- 'south-america',
1919-]
2020-2121-baseRegions.concat(['antarctica'])
2222- .forEach((region) => regions.add(region))
2323-2424-const responses = baseRegions.map(fetchRegion)
2525-2626-await Promise.all(responses)
2727-const regionsArray = [...regions]
2828-Deno.writeTextFile('regions.txt', regionsArray.sort().join('\n') + '\n')
2929-3030-async function fetchRegion(region: string) {
3131- console.log('fetching: ', region)
3232- const url = baseURL + '/' + region
3333- const resp = await fetch(url)
3434- const html = await resp.text()
3535- const { directories, polyFiles } = await parseGeofabrikDirectory(html)
3636- polyFiles
3737- .forEach((file) => regions.add(region + '/' + file.replace('.poly', '')))
3838-3939- const childrenResp = directories.map(
4040- (name) => {
4141- const childName = name.replace(/^\//, '').replace(/\/$/, '')
4242- if (!regions.has(childName)) {
4343- return fetchRegion(region + '/' + childName)
4444- }
4545- },
4646- )
4747- await Promise.all(childrenResp)
4848-}
4949-5050-async function parseGeofabrikDirectory(html: string) {
5151- const $ = cheerio.load(html)
5252- const rows = $('tr')
5353-5454- const directories: string[] = []
5555- const polyFiles: string[] = []
5656-5757- rows.each((_, row) => {
5858- const href = $(row).find('td a').first().attr('href')
5959-6060- if (!href) return
6161-6262- if (href.endsWith('/') && (href !== '/') && !href.endsWith('-updates/')) {
6363- directories.push(href)
6464- } else if (href.endsWith('.poly')) {
6565- polyFiles.push(href.replace('/', ''))
6666- }
6767- })
6868-6969- return {
7070- directories,
7171- polyFiles,
7272- }
7373-}
···11-# `osmium tags-filter monaco.osm.pbf -R local.txt -o monaco.osm.pbf`
22-33-# Most notable landmarks: must have both Wikipedia and Wikidata entries
44-# AND be a significant feature type
55-(
66- node["wikipedia"]["wikidata"]
77- (
88- ["historic"="monument"] |
99- ["historic"="castle"] |
1010- ["historic"="fort"] |
1111- ["tourism"="attraction"] |
1212- ["tourism"="museum"] |
1313- ["amenity"="place_of_worship"] |
1414- ["leisure"="park"]
1515- )
1616-) |
1717-(
1818- way["wikipedia"]["wikidata"]
1919- (
2020- ["historic"="monument"] |
2121- ["historic"="castle"] |
2222- ["historic"="fort"] |
2323- ["tourism"="attraction"] |
2424- ["tourism"="museum"] |
2525- ["amenity"="place_of_worship"] |
2626- ["leisure"="park"]
2727- )
2828-)
2929-3030-# Super notable landmarks: has Wikipedia entries in multiple languages
3131-(
3232- node["wikipedia:en"]["wikipedia:de"]["wikipedia:fr"] |
3333- way["wikipedia:en"]["wikipedia:de"]["wikipedia:fr"]
3434-)
3535-3636-# Cross-reference with other importance indicators
3737-(
3838- node["wikipedia"]
3939- (
4040- ["tourism:visitors">100000] | # High visitor count
4141- ["protection_title"] | # Protected status
4242- ["heritage"] # Heritage listing
4343- )
4444-) |
4545-(
4646- way["wikipedia"]
4747- (
4848- ["tourism:visitors">100000] |
4949- ["protection_title"] |
5050- ["heritage"]
5151- )
5252-)
5353-5454-# Your base map features...
5555-way["highway"="motorway"]
5656-way["highway"="trunk"]
5757-way["highway"="primary"]
5858-way["highway"="secondary"]
5959-way["highway"="footway"]
6060-way["building"]
6161-way["natural"="water"]
6262-way["natural"="coastline"]
6363-6464-# Keep nodes that are part of kept ways
6565-node(w)
6666-6767-# Remove unnecessary tags but keep important landmark info
6868---remove-tags
6969- *,
7070- ~highway,
7171- ~building,
7272- ~natural,
7373- ~waterway,
7474- ~name,
7575- ~wikipedia,
7676- ~wikidata,
7777- ~historic,
7878- ~tourism,
7979- ~amenity,
8080- ~leisure
-947
docs/tilemaker/process.lua
···11--- Data processing based on openmaptiles.org schema
22--- https://openmaptiles.org/schema/
33--- Copyright (c) 2016, KlokanTech.com & OpenMapTiles contributors.
44--- Used under CC-BY 4.0
55-66---------
77--- Alter these lines to control which languages are written for place/streetnames
88---
99--- Preferred language can be (for example) "en" for English, "de" for German, or nil to use OSM's name tag:
1010-preferred_language = nil
1111--- This is written into the following vector tile attribute (usually "name:latin"):
1212-preferred_language_attribute = "name:latin"
1313--- If OSM's name tag differs, then write it into this attribute (usually "name_int"):
1414-default_language_attribute = "name_int"
1515--- Also write these languages if they differ - for example, { "de", "fr" }
1616-additional_languages = { }
1717---------
1818-1919--- Enter/exit Tilemaker
2020-function init_function()
2121-end
2222-function exit_function()
2323-end
2424-2525--- Implement Sets in tables
2626-function Set(list)
2727- local set = {}
2828- for _, l in ipairs(list) do set[l] = true end
2929- return set
3030-end
3131-3232--- Meters per pixel if tile is 256x256
3333-ZRES5 = 4891.97
3434-ZRES6 = 2445.98
3535-ZRES7 = 1222.99
3636-ZRES8 = 611.5
3737-ZRES9 = 305.7
3838-ZRES10 = 152.9
3939-ZRES11 = 76.4
4040-ZRES12 = 38.2
4141-ZRES13 = 19.1
4242-4343--- The height of one floor, in meters
4444-BUILDING_FLOOR_HEIGHT = 3.66
4545--- Used to express that a feature should not end up the vector tiles
4646-INVALID_ZOOM = 99
4747-4848--- Process node/way tags
4949-aerodromeValues = Set { "international", "public", "regional", "military", "private" }
5050-pavedValues = Set { "paved", "asphalt", "cobblestone", "concrete", "concrete:lanes", "concrete:plates", "metal", "paving_stones", "sett", "unhewn_cobblestone", "wood" }
5151-unpavedValues = Set { "unpaved", "compacted", "dirt", "earth", "fine_gravel", "grass", "grass_paver", "gravel", "gravel_turf", "ground", "ice", "mud", "pebblestone", "salt", "sand", "snow", "woodchips" }
5252-5353--- Process node tags
5454-5555-node_keys = { "addr:housenumber","aerialway","aeroway","amenity","barrier","highway","historic","leisure","natural","office","place","railway","shop","sport","tourism","waterway" }
5656-5757--- Get admin level which the place node is capital of.
5858--- Returns nil in case of invalid capital and for places which are not capitals.
5959-function capitalLevel(capital)
6060- local capital_al = tonumber(capital) or 0
6161- if capital == "yes" then
6262- capital_al = 2
6363- end
6464- if capital_al == 0 then
6565- return nil
6666- end
6767- return capital_al
6868-end
6969-7070--- Calculate rank for place nodes
7171--- place: value of place=*
7272--- popuplation: population as number
7373--- capital_al: result of capitalLevel()
7474-function calcRank(place, population, capital_al)
7575- local rank = 0
7676- if capital_al and capital_al >= 2 and capital_al <= 4 then
7777- rank = capital_al
7878- if population > 3 * 10^6 then
7979- rank = rank - 2
8080- elseif population > 1 * 10^6 then
8181- rank = rank - 1
8282- elseif population < 100000 then
8383- rank = rank + 2
8484- elseif population < 50000 then
8585- rank = rank + 3
8686- end
8787- -- Safety measure to avoid place=village/farm/... appear early (as important capital) because a mapper added capital=yes/2/3/4
8888- if place ~= "city" then
8989- rank = rank + 3
9090- -- Decrease rank further if it is not even a town.
9191- if place ~= "town" then
9292- rank = rank + 2
9393- end
9494- end
9595- return rank
9696- end
9797- if place ~= "city" and place ~= "town" then
9898- return nil
9999- end
100100- if population > 3 * 10^6 then
101101- return 1
102102- elseif population > 1 * 10^6 then
103103- return 2
104104- elseif population > 500000 then
105105- return 3
106106- elseif population > 200000 then
107107- return 4
108108- elseif population > 100000 then
109109- return 5
110110- elseif population > 75000 then
111111- return 6
112112- elseif population > 50000 then
113113- return 7
114114- elseif population > 25000 then
115115- return 8
116116- elseif population > 10000 then
117117- return 9
118118- end
119119- return 10
120120-end
121121-122122-123123-function node_function()
124124- -- Write 'aerodrome_label'
125125- local aeroway = Find("aeroway")
126126- if aeroway == "aerodrome" then
127127- Layer("aerodrome_label", false)
128128- SetNameAttributes()
129129- Attribute("iata", Find("iata"))
130130- SetEleAttributes()
131131- Attribute("icao", Find("icao"))
132132-133133- local aerodrome_value = Find("aerodrome")
134134- local class
135135- if aerodromeValues[aerodrome_value] then class = aerodrome_value else class = "other" end
136136- Attribute("class", class)
137137- end
138138- -- Write 'housenumber'
139139- local housenumber = Find("addr:housenumber")
140140- if housenumber~="" then
141141- Layer("housenumber", false)
142142- Attribute("housenumber", housenumber)
143143- end
144144-145145- -- Write 'place'
146146- -- note that OpenMapTiles has a rank for countries (1-3), states (1-6) and cities (1-10+);
147147- -- we could potentially approximate it for cities based on the population tag
148148- local place = Find("place")
149149- if place ~= "" then
150150- local mz = 13
151151- local pop = tonumber(Find("population")) or 0
152152- local capital = capitalLevel(Find("capital"))
153153- local rank = calcRank(place, pop, capital)
154154-155155- if place == "continent" then mz=0
156156- elseif place == "country" then
157157- if pop>50000000 then rank=1; mz=1
158158- elseif pop>20000000 then rank=2; mz=2
159159- else rank=3; mz=3 end
160160- elseif place == "state" then mz=4
161161- elseif place == "province" then mz=5
162162- elseif place == "city" then mz=5
163163- elseif place == "town" and pop>8000 then mz=7
164164- elseif place == "town" then mz=8
165165- elseif place == "village" and pop>2000 then mz=9
166166- elseif place == "village" then mz=10
167167- elseif place == "borough" then mz=10
168168- elseif place == "suburb" then mz=11
169169- elseif place == "quarter" then mz=12
170170- elseif place == "hamlet" then mz=12
171171- elseif place == "neighbourhood" then mz=13
172172- elseif place == "isolated_dwelling" then mz=13
173173- elseif place == "locality" then mz=13
174174- elseif place == "island" then mz=12
175175- end
176176-177177- Layer("place", false)
178178- Attribute("class", place)
179179- MinZoom(mz)
180180- if rank then AttributeNumeric("rank", rank) end
181181- if capital then AttributeNumeric("capital", capital) end
182182- if place=="country" then
183183- local iso_a2 = Find("ISO3166-1:alpha2")
184184- while iso_a2 == "" do
185185- local rel, role = NextRelation()
186186- if not rel then break end
187187- if role == 'label' then
188188- iso_a2 = FindInRelation("ISO3166-1:alpha2")
189189- end
190190- end
191191- Attribute("iso_a2", iso_a2)
192192- end
193193- SetNameAttributes()
194194- return
195195- end
196196-197197- -- Write 'poi'
198198- local rank, class, subclass = GetPOIRank()
199199- if rank then WritePOI(class,subclass,rank) end
200200-201201- -- Write 'mountain_peak' and 'water_name'
202202- local natural = Find("natural")
203203- if natural == "peak" or natural == "volcano" then
204204- Layer("mountain_peak", false)
205205- SetEleAttributes()
206206- AttributeNumeric("rank", 1)
207207- Attribute("class", natural)
208208- SetNameAttributes()
209209- return
210210- end
211211- if natural == "bay" then
212212- Layer("water_name", false)
213213- SetNameAttributes()
214214- return
215215- end
216216-end
217217-218218--- Process way tags
219219-220220-majorRoadValues = Set { "motorway", "trunk", "primary" }
221221-z9RoadValues = Set { "secondary", "motorway_link", "trunk_link" }
222222-z10RoadValues = Set { "primary_link", "secondary_link" }
223223-z11RoadValues = Set { "tertiary", "tertiary_link", "busway", "bus_guideway" }
224224--- On zoom 12, various road classes are merged into "minor"
225225-z12MinorRoadValues = Set { "unclassified", "residential", "road", "living_street" }
226226-z12OtherRoadValues = Set { "raceway" }
227227-z13RoadValues = Set { "track", "service" }
228228-manMadeRoadValues = Set { "pier", "bridge" }
229229-pathValues = Set { "footway", "cycleway", "bridleway", "path", "steps", "pedestrian", "platform" }
230230-linkValues = Set { "motorway_link", "trunk_link", "primary_link", "secondary_link", "tertiary_link" }
231231-pavedValues = Set { "paved", "asphalt", "cobblestone", "concrete", "concrete:lanes", "concrete:plates", "metal", "paving_stones", "sett", "unhewn_cobblestone", "wood" }
232232-unpavedValues = Set { "unpaved", "compacted", "dirt", "earth", "fine_gravel", "grass", "grass_paver", "gravel", "gravel_turf", "ground", "ice", "mud", "pebblestone", "salt", "sand", "snow", "woodchips" }
233233-railwayClasses = { rail="rail", narrow_gauge="rail", preserved="rail", funicular="rail", subway="transit", light_rail="transit", monorail="transit", tram="transit" }
234234-235235-aerowayBuildings= Set { "terminal", "gate", "tower" }
236236-landuseKeys = Set { "school", "university", "kindergarten", "college", "library", "hospital",
237237- "railway", "cemetery", "military", "residential", "commercial", "industrial",
238238- "retail", "stadium", "pitch", "playground", "theme_park", "bus_station", "zoo" }
239239-landcoverKeys = { wood="wood", forest="wood",
240240- wetland="wetland",
241241- beach="sand", sand="sand", dune="sand",
242242- farmland="farmland", farm="farmland", orchard="farmland", vineyard="farmland", plant_nursery="farmland",
243243- glacier="ice", ice_shelf="ice",
244244- bare_rock="rock", scree="rock",
245245- fell="grass", grassland="grass", grass="grass", heath="grass", meadow="grass", allotments="grass", park="grass", village_green="grass", recreation_ground="grass", scrub="grass", shrubbery="grass", tundra="grass", garden="grass", golf_course="grass", park="grass" }
246246-247247--- POI key/value pairs: based on https://github.com/openmaptiles/openmaptiles/blob/master/layers/poi/mapping.yaml
248248-poiTags = { aerialway = Set { "station" },
249249- 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" },
250250- barrier = Set { "bollard", "border_control", "cycle_barrier", "gate", "lift_gate", "sally_port", "stile", "toll_booth" },
251251- building = Set { "dormitory" },
252252- highway = Set { "bus_stop" },
253253- historic = Set { "monument", "castle", "ruins" },
254254- landuse = Set { "basin", "brownfield", "cemetery", "reservoir", "winter_sports" },
255255- 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" },
256256- railway = Set { "halt", "station", "subway_entrance", "train_station_entrance", "tram_stop" },
257257- 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" },
258258- 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" },
259259- 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" },
260260- waterway = Set { "dock" } }
261261-262262--- POI "class" values: based on https://github.com/openmaptiles/openmaptiles/blob/master/layers/poi/poi.yaml
263263-poiClasses = { townhall="town_hall", public_building="town_hall", courthouse="town_hall", community_centre="town_hall",
264264- golf="golf", golf_course="golf", miniature_golf="golf",
265265- fast_food="fast_food", food_court="fast_food",
266266- park="park", bbq="park",
267267- bus_stop="bus", bus_station="bus",
268268- subway_entrance="entrance", train_station_entrance="entrance",
269269- camp_site="campsite", caravan_site="campsite",
270270- laundry="laundry", dry_cleaning="laundry",
271271- supermarket="grocery", deli="grocery", delicatessen="grocery", department_store="grocery", greengrocer="grocery", marketplace="grocery",
272272- books="library", library="library",
273273- university="college", college="college",
274274- hotel="lodging", motel="lodging", bed_and_breakfast="lodging", guest_house="lodging", hostel="lodging", chalet="lodging", alpine_hut="lodging", dormitory="lodging",
275275- chocolate="ice_cream", confectionery="ice_cream",
276276- post_box="post", post_office="post",
277277- cafe="cafe",
278278- school="school", kindergarten="school",
279279- alcohol="alcohol_shop", beverages="alcohol_shop", wine="alcohol_shop",
280280- bar="bar", nightclub="bar",
281281- marina="harbor", dock="harbor",
282282- car="car", car_repair="car", taxi="car",
283283- hospital="hospital", nursing_home="hospital", clinic="hospital",
284284- grave_yard="cemetery", cemetery="cemetery",
285285- attraction="attraction", viewpoint="attraction",
286286- biergarten="beer", pub="beer",
287287- music="music", musical_instrument="music",
288288- american_football="stadium", stadium="stadium", soccer="stadium",
289289- art="art_gallery", artwork="art_gallery", gallery="art_gallery", arts_centre="art_gallery",
290290- bag="clothing_store", clothes="clothing_store",
291291- swimming_area="swimming", swimming="swimming",
292292- castle="castle", ruins="castle" }
293293--- POI classes where class is the matching value and subclass is the value of a separate key
294294-poiSubClasses = { information="information", place_of_worship="religion", pitch="sport" }
295295-poiClassRanks = { hospital=1, railway=2, bus=3, attraction=4, harbor=5, college=6,
296296- school=7, stadium=8, zoo=9, town_hall=10, campsite=11, cemetery=12,
297297- park=13, library=14, police=15, post=16, golf=17, shop=18, grocery=19,
298298- fast_food=20, clothing_store=21, bar=22 }
299299-waterClasses = Set { "river", "riverbank", "stream", "canal", "drain", "ditch", "dock" }
300300-waterwayClasses = Set { "stream", "river", "canal", "drain", "ditch" }
301301-302302--- Scan relations for use in ways
303303-304304-function relation_scan_function()
305305- if Find("type")=="boundary" and Find("boundary")=="administrative" then
306306- Accept()
307307- end
308308-end
309309-310310-function write_to_transportation_layer(minzoom, highway_class, subclass, ramp, service, is_rail, is_road, is_area)
311311- Layer("transportation", is_area)
312312- SetZOrder()
313313- Attribute("class", highway_class)
314314- if subclass and subclass ~= "" then
315315- Attribute("subclass", subclass)
316316- end
317317- AttributeNumeric("layer", tonumber(Find("layer")) or 0, accessMinzoom)
318318- SetBrunnelAttributes()
319319- -- We do not write any other attributes for areas.
320320- if is_area then
321321- SetMinZoomByAreaWithLimit(minzoom)
322322- return
323323- end
324324- MinZoom(minzoom)
325325- if ramp then AttributeNumeric("ramp",1) end
326326-327327- -- Service
328328- if (is_rail or highway_class == "service") and (service and service ~="") then Attribute("service", service) end
329329-330330- local accessMinzoom = 9
331331- if is_road then
332332- local oneway = Find("oneway")
333333- if oneway == "yes" or oneway == "1" then
334334- AttributeNumeric("oneway",1)
335335- end
336336- if oneway == "-1" then
337337- -- **** TODO
338338- end
339339- local surface = Find("surface")
340340- local surfaceMinzoom = 12
341341- if pavedValues[surface] then
342342- Attribute("surface", "paved", surfaceMinzoom)
343343- elseif unpavedValues[surface] then
344344- Attribute("surface", "unpaved", surfaceMinzoom)
345345- end
346346- if Holds("access") then Attribute("access", Find("access"), accessMinzoom) end
347347- if Holds("bicycle") then Attribute("bicycle", Find("bicycle"), accessMinzoom) end
348348- if Holds("foot") then Attribute("foot", Find("foot"), accessMinzoom) end
349349- if Holds("horse") then Attribute("horse", Find("horse"), accessMinzoom) end
350350- AttributeBoolean("toll", Find("toll") == "yes", accessMinzoom)
351351- if Find("expressway") == "yes" then AttributeBoolean("expressway", true, 7) end
352352- if Holds("mtb_scale") then Attribute("mtb_scale", Find("mtb:scale"), 10) end
353353- end
354354-end
355355-356356--- Process way tags
357357-358358-function way_function()
359359- local route = Find("route")
360360- local highway = Find("highway")
361361- local waterway = Find("waterway")
362362- local water = Find("water")
363363- local building = Find("building")
364364- local natural = Find("natural")
365365- local historic = Find("historic")
366366- local landuse = Find("landuse")
367367- local leisure = Find("leisure")
368368- local amenity = Find("amenity")
369369- local aeroway = Find("aeroway")
370370- local railway = Find("railway")
371371- local service = Find("service")
372372- local sport = Find("sport")
373373- local shop = Find("shop")
374374- local tourism = Find("tourism")
375375- local man_made = Find("man_made")
376376- local boundary = Find("boundary")
377377- local aerialway = Find("aerialway")
378378- local public_transport = Find("public_transport")
379379- local place = Find("place")
380380- local is_closed = IsClosed()
381381- local housenumber = Find("addr:housenumber")
382382- local write_name = false
383383- local construction = Find("construction")
384384- local is_highway_area = highway~="" and Find("area")=="yes" and is_closed
385385-386386- -- Miscellaneous preprocessing
387387- if Find("disused") == "yes" then return end
388388- if boundary~="" and Find("protection_title")=="National Forest" and Find("operator")=="United States Forest Service" then return end
389389- if highway == "proposed" then return end
390390- if aerowayBuildings[aeroway] then building="yes"; aeroway="" end
391391- if landuse == "field" then landuse = "farmland" end
392392- if landuse == "meadow" and Find("meadow")=="agricultural" then landuse="farmland" end
393393-394394- if place == "island" then
395395- LayerAsCentroid("place")
396396- Attribute("class", place)
397397- MinZoom(10)
398398- local pop = tonumber(Find("population")) or 0
399399- local capital = capitalLevel(Find("capital"))
400400- local rank = calcRank(place, pop, nil)
401401- if rank then AttributeNumeric("rank", rank) end
402402- SetNameAttributes()
403403- end
404404-405405- -- Boundaries within relations
406406- -- note that we process administrative boundaries as properties on ways, rather than as single relation geometries,
407407- -- because otherwise we get multiple renderings where boundaries are coterminous
408408- local admin_level = 11
409409- local isBoundary = false
410410- while true do
411411- local rel = NextRelation()
412412- if not rel then break end
413413- isBoundary = true
414414- admin_level = math.min(admin_level, tonumber(FindInRelation("admin_level")) or 11)
415415- end
416416-417417- -- Boundaries in ways
418418- if boundary=="administrative" then
419419- admin_level = math.min(admin_level, tonumber(Find("admin_level")) or 11)
420420- isBoundary = true
421421- end
422422-423423- -- Administrative boundaries
424424- -- https://openmaptiles.org/schema/#boundary
425425- if isBoundary and not (Find("maritime")=="yes") then
426426- local mz = 0
427427- if admin_level>=3 and admin_level<5 then mz=4
428428- elseif admin_level>=5 and admin_level<7 then mz=8
429429- elseif admin_level==7 then mz=10
430430- elseif admin_level>=8 then mz=12
431431- end
432432-433433- Layer("boundary",false)
434434- AttributeNumeric("admin_level", admin_level)
435435- MinZoom(mz)
436436- -- disputed status (0 or 1). some styles need to have the 0 to show it.
437437- local disputed = Find("disputed")
438438- if disputed=="yes" then
439439- AttributeNumeric("disputed", 1)
440440- else
441441- AttributeNumeric("disputed", 0)
442442- end
443443- end
444444-445445- -- Aerialways ('transportation' and 'transportation_name')
446446- if aerialway ~= "" then
447447- write_to_transportation_layer(12, "aerialway", aerialway, false, nil, false, false, is_closed)
448448- if HasNames() then
449449- Layer("transportation_name", false)
450450- MinZoom(12)
451451- SetNameAttributes()
452452- Attribute("class", "aerialway")
453453- Attribute("subclass", aerialway)
454454- end
455455- end
456456-457457- -- Roads ('transportation' and 'transportation_name')
458458- if highway ~= "" or public_transport == "platform" then
459459- local access = Find("access")
460460- local surface = Find("surface")
461461-462462- local h = highway
463463- local is_road = true
464464- if h == "" then
465465- h = public_transport
466466- is_road = false
467467- end
468468- local subclass = nil
469469- local under_construction = false
470470- if highway == "construction" and construction ~= "" then
471471- h = construction
472472- under_construction = true
473473- end
474474- local minzoom = INVALID_ZOOM
475475- if majorRoadValues[h] then minzoom = 4
476476- elseif h == "trunk" then minzoom = 5
477477- elseif highway == "primary" then minzoom = 7
478478- elseif z9RoadValues[h] then minzoom = 9
479479- elseif z10RoadValues[h] then minzoom = 10
480480- elseif z11RoadValues[h] then minzoom = 11
481481- elseif z12MinorRoadValues[h] then
482482- minzoom = 12
483483- subclass = h
484484- h = "minor"
485485- elseif z12OtherRoadValues[h] then minzoom = 12
486486- elseif z13RoadValues[h] then minzoom = 13
487487- elseif pathValues[h] then
488488- minzoom = 14
489489- subclass = h
490490- h = "path"
491491- end
492492-493493- -- Links (ramp)
494494- local ramp=false
495495- if linkValues[h] then
496496- splitHighway = split(highway, "_")
497497- highway = splitHighway[1]; h = highway
498498- ramp = true
499499- end
500500-501501- -- Construction
502502- if under_construction then
503503- h = h .. "_construction"
504504- end
505505-506506- -- Drop underground platforms
507507- local layer = Find("layer")
508508- local layerNumeric = tonumber(layer)
509509- if not is_road and layerNumeric ~= nil and layerNumeric < 0 then
510510- minzoom = INVALID_ZOOM
511511- end
512512-513513- -- Drop all areas except infrastructure for pedestrians handled above
514514- if is_highway_area and h ~= "path" then
515515- minzoom = INVALID_ZOOM
516516- end
517517-518518- -- Write to layer
519519- if minzoom <= 14 then
520520- write_to_transportation_layer(minzoom, h, subclass, ramp, service, false, is_road, is_highway_area)
521521-522522- -- Write names
523523- if not is_closed and (HasNames() or Holds("ref")) then
524524- if h == "motorway" then
525525- minzoom = 7
526526- elseif h == "trunk" then
527527- minzoom = 8
528528- elseif h == "primary" then
529529- minzoom = 10
530530- elseif h == "secondary" then
531531- minzoom = 11
532532- elseif h == "minor" or h == "track" or h == "tertiary" then
533533- minzoom = 13
534534- else
535535- minzoom = 14
536536- end
537537- Layer("transportation_name", false)
538538- MinZoom(minzoom)
539539- SetNameAttributes()
540540- Attribute("class",h)
541541- Attribute("network","road") -- **** could also be us-interstate, us-highway, us-state
542542- if subclass then Attribute("subclass", highway) end
543543- local ref = Find("ref")
544544- if ref~="" then
545545- Attribute("ref",ref)
546546- AttributeNumeric("ref_length",ref:len())
547547- end
548548- end
549549- end
550550- end
551551-552552- -- Railways ('transportation' and 'transportation_name')
553553- if railway~="" then
554554- local class = railwayClasses[railway]
555555- if class then
556556- local minzoom = 14
557557- local usage = Find("usage")
558558- if railway == "rail" and service == "" then
559559- if usage == "main" then
560560- minzoom = 8
561561- else
562562- minzoom = 10
563563- end
564564- elseif railway == "narrow_gauge" and service == "" then
565565- minzoom = 10
566566- elseif railway == "light_rail" and service == "" then
567567- minzoom = 11
568568- end
569569- write_to_transportation_layer(minzoom, class, railway, false, service, true, false, is_closed)
570570-571571- if HasNames() then
572572- Layer("transportation_name", false)
573573- SetNameAttributes()
574574- MinZoom(14)
575575- Attribute("class", class)
576576- end
577577- end
578578- end
579579-580580- -- Pier
581581- if manMadeRoadValues[man_made] then
582582- write_to_transportation_layer(13, man_made, nil, false, nil, false, false, is_closed)
583583- end
584584-585585- -- 'Ferry'
586586- if route=="ferry" then
587587- write_to_transportation_layer(9, "ferry", nil, false, nil, false, false, is_closed)
588588-589589- if HasNames() then
590590- Layer("transportation_name", false)
591591- SetNameAttributes()
592592- MinZoom(12)
593593- Attribute("class", "ferry")
594594- end
595595- end
596596-597597- -- 'Aeroway'
598598- if aeroway~="" then
599599- Layer("aeroway", is_closed)
600600- Attribute("class",aeroway)
601601- Attribute("ref",Find("ref"))
602602- write_name = true
603603- end
604604-605605- -- 'aerodrome_label'
606606- if aeroway=="aerodrome" then
607607- LayerAsCentroid("aerodrome_label")
608608- SetNameAttributes()
609609- Attribute("iata", Find("iata"))
610610- SetEleAttributes()
611611- Attribute("icao", Find("icao"))
612612-613613- local aerodrome = Find(aeroway)
614614- local class
615615- if aerodromeValues[aerodrome] then class = aerodrome else class = "other" end
616616- Attribute("class", class)
617617- end
618618-619619- -- Set 'waterway' and associated
620620- if waterwayClasses[waterway] and not is_closed then
621621- if waterway == "river" and Holds("name") then
622622- Layer("waterway", false)
623623- else
624624- Layer("waterway_detail", false)
625625- end
626626- if Find("intermittent")=="yes" then AttributeNumeric("intermittent", 1) else AttributeNumeric("intermittent", 0) end
627627- Attribute("class", waterway)
628628- SetNameAttributes()
629629- SetBrunnelAttributes()
630630- elseif waterway == "boatyard" then Layer("landuse", is_closed); Attribute("class", "industrial"); MinZoom(12)
631631- elseif waterway == "dam" then Layer("building",is_closed)
632632- elseif waterway == "fuel" then Layer("landuse", is_closed); Attribute("class", "industrial"); MinZoom(14)
633633- end
634634- -- Set names on rivers
635635- if waterwayClasses[waterway] and not is_closed then
636636- if waterway == "river" and Holds("name") then
637637- Layer("water_name", false)
638638- else
639639- Layer("water_name_detail", false)
640640- MinZoom(14)
641641- end
642642- Attribute("class", waterway)
643643- SetNameAttributes()
644644- end
645645-646646- -- Set 'building' and associated
647647- if building~="" then
648648- Layer("building", true)
649649- SetBuildingHeightAttributes()
650650- SetMinZoomByArea()
651651- end
652652-653653- -- Set 'housenumber'
654654- if housenumber~="" then
655655- LayerAsCentroid("housenumber")
656656- Attribute("housenumber", housenumber)
657657- end
658658-659659- -- Set 'water'
660660- if natural=="water" or leisure=="swimming_pool" or landuse=="reservoir" or landuse=="basin" or waterClasses[waterway] then
661661- if Find("covered")=="yes" or not is_closed then return end
662662- local class="lake"; if waterway~="" then class="river" end
663663- if class=="lake" and Find("wikidata")=="Q192770" then return end
664664- Layer("water",true)
665665- SetMinZoomByArea(way)
666666- Attribute("class",class)
667667-668668- if Find("intermittent")=="yes" then Attribute("intermittent",1) end
669669- -- 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"
670670- -- examples for which we don't want to show a name:
671671- -- https://www.openstreetmap.org/way/25958687
672672- -- https://www.openstreetmap.org/way/27201902
673673- -- https://www.openstreetmap.org/way/25309134
674674- -- https://www.openstreetmap.org/way/24579306
675675- if Holds("name") and natural=="water" and water ~= "basin" and water ~= "wastewater" then
676676- LayerAsCentroid("water_name_detail")
677677- SetNameAttributes()
678678- SetMinZoomByArea()
679679- Attribute("class", class)
680680- end
681681-682682- return -- in case we get any landuse processing
683683- end
684684-685685- -- Set 'landcover' (from landuse, natural, leisure)
686686- local l = landuse
687687- if l=="" then l=natural end
688688- if l=="" then l=leisure end
689689- if landcoverKeys[l] then
690690- Layer("landcover", true)
691691- SetMinZoomByArea()
692692- Attribute("class", landcoverKeys[l])
693693- if l=="wetland" then Attribute("subclass", Find("wetland"))
694694- else Attribute("subclass", l) end
695695- write_name = true
696696-697697- -- Set 'landuse'
698698- else
699699- if l=="" then l=amenity end
700700- if l=="" then l=tourism end
701701- if landuseKeys[l] then
702702- Layer("landuse", true)
703703- Attribute("class", l)
704704- if l=="residential" then
705705- if Area()<ZRES8^2 then MinZoom(8)
706706- else SetMinZoomByArea() end
707707- else MinZoom(11) end
708708- write_name = true
709709- end
710710- end
711711-712712- -- Parks
713713- -- **** name?
714714- if boundary=="national_park" then Layer("park",true); Attribute("class",boundary); SetNameAttributes()
715715- elseif leisure=="nature_reserve" then Layer("park",true); Attribute("class",leisure ); SetNameAttributes() end
716716-717717- -- POIs ('poi' and 'poi_detail')
718718- local rank, class, subclass = GetPOIRank()
719719- if rank then WritePOI(class,subclass,rank); return end
720720-721721- -- Catch-all
722722- if (building~="" or write_name) and Holds("name") then
723723- LayerAsCentroid("poi_detail")
724724- SetNameAttributes()
725725- if write_name then rank=6 else rank=25 end
726726- AttributeNumeric("rank", rank)
727727- end
728728-end
729729-730730--- Remap coastlines
731731-function attribute_function(attr,layer)
732732- if attr["featurecla"]=="Glaciated areas" then
733733- return { subclass="glacier" }
734734- elseif attr["featurecla"]=="Antarctic Ice Shelf" then
735735- return { subclass="ice_shelf" }
736736- elseif attr["featurecla"]=="Urban area" then
737737- return { class="residential" }
738738- elseif layer=="ocean" then
739739- return { class="ocean" }
740740- else
741741- return attr
742742- end
743743-end
744744-745745--- ==========================================================
746746--- Common functions
747747-748748--- Write a way centroid to POI layer
749749-function WritePOI(class,subclass,rank)
750750- local layer = "poi"
751751- if rank>4 then layer="poi_detail" end
752752- LayerAsCentroid(layer)
753753- SetNameAttributes()
754754- AttributeNumeric("rank", rank)
755755- Attribute("class", class)
756756- Attribute("subclass", subclass)
757757- -- layer defaults to 0
758758- AttributeNumeric("layer", tonumber(Find("layer")) or 0)
759759- -- indoor defaults to false
760760- AttributeBoolean("indoor", (Find("indoor") == "yes"))
761761- -- level has no default
762762- local level = tonumber(Find("level"))
763763- if level then
764764- AttributeNumeric("level", level)
765765- end
766766-end
767767-768768--- Check if there are name tags on the object
769769-function HasNames()
770770- if Holds("name") then return true end
771771- local iname
772772- local main_written = name
773773- if preferred_language and Holds("name:"..preferred_language) then return true end
774774- -- then set any additional languages
775775- for i,lang in ipairs(additional_languages) do
776776- if Holds("name:"..lang) then return true end
777777- end
778778- return false
779779-end
780780-781781--- Set name attributes on any object
782782-function SetNameAttributes()
783783- local name = Find("name"), iname
784784- local main_written = name
785785- -- if we have a preferred language, then write that (if available), and additionally write the base name tag
786786- if preferred_language and Holds("name:"..preferred_language) then
787787- iname = Find("name:"..preferred_language)
788788- Attribute(preferred_language_attribute, iname)
789789- if iname~=name and default_language_attribute then
790790- Attribute(default_language_attribute, name)
791791- else main_written = iname end
792792- else
793793- Attribute(preferred_language_attribute, name)
794794- end
795795- -- then set any additional languages
796796- for i,lang in ipairs(additional_languages) do
797797- iname = Find("name:"..lang)
798798- if iname=="" then iname=name end
799799- if iname~=main_written then Attribute("name:"..lang, iname) end
800800- end
801801-end
802802-803803--- Set ele and ele_ft on any object
804804-function SetEleAttributes()
805805- local ele = Find("ele")
806806- if ele ~= "" then
807807- local meter = math.floor(tonumber(ele) or 0)
808808- local feet = math.floor(meter * 3.2808399)
809809- AttributeNumeric("ele", meter)
810810- AttributeNumeric("ele_ft", feet)
811811- end
812812-end
813813-814814-function SetBrunnelAttributes()
815815- if Find("bridge") == "yes" or Find("man_made") == "bridge" then Attribute("brunnel", "bridge")
816816- elseif Find("tunnel") == "yes" then Attribute("brunnel", "tunnel")
817817- elseif Find("ford") == "yes" then Attribute("brunnel", "ford")
818818- end
819819-end
820820-821821--- Set minimum zoom level by area
822822-function SetMinZoomByArea()
823823- SetMinZoomByAreaWithLimit(0)
824824-end
825825-826826--- Set minimum zoom level by area but not below given minzoom
827827-function SetMinZoomByAreaWithLimit(minzoom)
828828- local area=Area()
829829- if minzoom <= 6 and area>ZRES5^2 then MinZoom(6)
830830- elseif minzoom <= 7 and area>ZRES6^2 then MinZoom(7)
831831- elseif minzoom <= 8 and area>ZRES7^2 then MinZoom(8)
832832- elseif minzoom <= 9 and area>ZRES8^2 then MinZoom(9)
833833- elseif minzoom <= 10 and area>ZRES9^2 then MinZoom(10)
834834- elseif minzoom <= 11 and area>ZRES10^2 then MinZoom(11)
835835- elseif minzoom <= 12 and area>ZRES11^2 then MinZoom(12)
836836- elseif minzoom <= 13 and area>ZRES12^2 then MinZoom(13)
837837- else MinZoom(14) end
838838-end
839839-840840--- Calculate POIs (typically rank 1-4 go to 'poi' z12-14, rank 5+ to 'poi_detail' z14)
841841--- returns rank, class, subclass
842842-function GetPOIRank()
843843- local k,list,v,class,rank
844844-845845- -- Can we find the tag?
846846- for k,list in pairs(poiTags) do
847847- if list[Find(k)] then
848848- v = Find(k) -- k/v are the OSM tag pair
849849- class = poiClasses[v] or k
850850- rank = poiClassRanks[class] or 25
851851- subclassKey = poiSubClasses[v]
852852- if subclassKey then
853853- class = v
854854- v = Find(subclassKey)
855855- end
856856- return rank, class, v
857857- end
858858- end
859859-860860- -- Catch-all for shops
861861- local shop = Find("shop")
862862- if shop~="" then return poiClassRanks['shop'], "shop", shop end
863863-864864- -- Nothing found
865865- return nil,nil,nil
866866-end
867867-868868-function SetBuildingHeightAttributes()
869869- local height = tonumber(Find("height"), 10)
870870- local minHeight = tonumber(Find("min_height"), 10)
871871- local levels = tonumber(Find("building:levels"), 10)
872872- local minLevel = tonumber(Find("building:min_level"), 10)
873873-874874- local renderHeight = BUILDING_FLOOR_HEIGHT
875875- if height or levels then
876876- renderHeight = height or (levels * BUILDING_FLOOR_HEIGHT)
877877- end
878878- local renderMinHeight = 0
879879- if minHeight or minLevel then
880880- renderMinHeight = minHeight or (minLevel * BUILDING_FLOOR_HEIGHT)
881881- end
882882-883883- -- Fix upside-down buildings
884884- if renderHeight < renderMinHeight then
885885- renderHeight = renderHeight + renderMinHeight
886886- end
887887-888888- AttributeNumeric("render_height", renderHeight)
889889- AttributeNumeric("render_min_height", renderMinHeight)
890890-end
891891-892892--- Implement z_order as calculated by Imposm
893893--- See https://imposm.org/docs/imposm3/latest/mapping.html#wayzorder for details.
894894-function SetZOrder()
895895- local highway = Find("highway")
896896- local layer = tonumber(Find("layer"))
897897- local bridge = Find("bridge")
898898- local tunnel = Find("tunnel")
899899- local zOrder = 0
900900- if bridge ~= "" and bridge ~= "no" then
901901- zOrder = zOrder + 10
902902- elseif tunnel ~= "" and tunnel ~= "no" then
903903- zOrder = zOrder - 10
904904- end
905905- if not (layer == nil) then
906906- if layer > 7 then
907907- layer = 7
908908- elseif layer < -7 then
909909- layer = -7
910910- end
911911- zOrder = zOrder + layer * 10
912912- end
913913- local hwClass = 0
914914- -- See https://github.com/omniscale/imposm3/blob/53bb80726ca9456e4a0857b38803f9ccfe8e33fd/mapping/columns.go#L251
915915- if highway == "motorway" then
916916- hwClass = 9
917917- elseif highway == "trunk" then
918918- hwClass = 8
919919- elseif highway == "primary" then
920920- hwClass = 6
921921- elseif highway == "secondary" then
922922- hwClass = 5
923923- elseif highway == "tertiary" then
924924- hwClass = 4
925925- else
926926- hwClass = 3
927927- end
928928- zOrder = zOrder + hwClass
929929- ZOrder(zOrder)
930930-end
931931-932932--- ==========================================================
933933--- Lua utility functions
934934-935935-function split(inputstr, sep) -- https://stackoverflow.com/a/7615129/4288232
936936- if sep == nil then
937937- sep = "%s"
938938- end
939939- local t={} ; i=1
940940- for str in string.gmatch(inputstr, "([^"..sep.."]+)") do
941941- t[i] = str
942942- i = i + 1
943943- end
944944- return t
945945-end
946946-947947--- vim: tabstop=2 shiftwidth=2 noexpandtab