Spaces:
Sleeping
Sleeping
# Copyright (c) Meta Platforms, Inc. and affiliates. | |
import logging | |
import re | |
from typing import List | |
from .reader import OSMData, OSMElement, OSMNode, OSMWay | |
IGNORE_TAGS = {"source", "phone", "entrance", "inscription", "note", "name"} | |
def parse_levels(string: str) -> List[float]: | |
"""Parse string representation of level sequence value.""" | |
try: | |
cleaned = string.replace(",", ";").replace(" ", "") | |
return list(map(float, cleaned.split(";"))) | |
except ValueError: | |
logging.debug("Cannot parse level description from `%s`.", string) | |
return [] | |
def filter_level(elem: OSMElement): | |
level = elem.tags.get("level") | |
if level is not None: | |
levels = parse_levels(level) | |
# In the US, ground floor levels are sometimes marked as level=1 | |
# so let's be conservative and include it. | |
if not (0 in levels or 1 in levels): | |
return False | |
layer = elem.tags.get("layer") | |
if layer is not None: | |
layer = parse_levels(layer) | |
if len(layer) > 0 and max(layer) < 0: | |
return False | |
return ( | |
elem.tags.get("location") != "underground" | |
and elem.tags.get("parking") != "underground" | |
) | |
def filter_node(node: OSMNode): | |
return len(node.tags.keys() - IGNORE_TAGS) > 0 and filter_level(node) | |
def is_area(way: OSMWay): | |
if way.nodes[0] != way.nodes[-1]: | |
return False | |
if way.tags.get("area") == "no": | |
return False | |
filters = [ | |
"area", | |
"building", | |
"amenity", | |
"indoor", | |
"landuse", | |
"landcover", | |
"leisure", | |
"public_transport", | |
"shop", | |
] | |
for f in filters: | |
if f in way.tags and way.tags.get(f) != "no": | |
return True | |
if way.tags.get("natural") in {"wood", "grassland", "water"}: | |
return True | |
return False | |
def filter_area(way: OSMWay): | |
return len(way.tags.keys() - IGNORE_TAGS) > 0 and is_area(way) and filter_level(way) | |
def filter_way(way: OSMWay): | |
return not filter_area(way) and way.tags != {} and filter_level(way) | |
def parse_node(tags): | |
keys = tags.keys() | |
for key in [ | |
"amenity", | |
"natural", | |
"highway", | |
"barrier", | |
"shop", | |
"tourism", | |
"public_transport", | |
"emergency", | |
"man_made", | |
]: | |
if key in keys: | |
if "disused" in tags[key]: | |
continue | |
return f"{key}:{tags[key]}" | |
return None | |
def parse_area(tags): | |
if "building" in tags: | |
group = "building" | |
kind = tags["building"] | |
if kind == "yes": | |
for key in ["amenity", "tourism"]: | |
if key in tags: | |
kind = tags[key] | |
break | |
if kind != "yes": | |
group += f":{kind}" | |
return group | |
if "area:highway" in tags: | |
return f'highway:{tags["area:highway"]}' | |
for key in [ | |
"amenity", | |
"landcover", | |
"leisure", | |
"shop", | |
"highway", | |
"tourism", | |
"natural", | |
"waterway", | |
"landuse", | |
]: | |
if key in tags: | |
return f"{key}:{tags[key]}" | |
return None | |
def parse_way(tags): | |
keys = tags.keys() | |
for key in ["highway", "barrier", "natural"]: | |
if key in keys: | |
return f"{key}:{tags[key]}" | |
return None | |
def match_to_group(label, patterns): | |
for group, pattern in patterns.items(): | |
if re.match(pattern, label): | |
return group | |
return None | |
class Patterns: | |
areas = dict( | |
building="building($|:.*?)*", | |
parking="amenity:parking", | |
playground="leisure:(playground|pitch)", | |
grass="(landuse:grass|landcover:grass|landuse:meadow|landuse:flowerbed|natural:grassland)", | |
park="leisure:(park|garden|dog_park)", | |
forest="(landuse:forest|natural:wood)", | |
water="(natural:water|waterway:*)", | |
) | |
# + ways: road, path | |
# + node: fountain, bicycle_parking | |
ways = dict( | |
fence="barrier:(fence|yes)", | |
wall="barrier:(wall|retaining_wall)", | |
hedge="barrier:hedge", | |
kerb="barrier:kerb", | |
building_outline="building($|:.*?)*", | |
cycleway="highway:cycleway", | |
path="highway:(pedestrian|footway|steps|path|corridor)", | |
road="highway:(motorway|trunk|primary|secondary|tertiary|service|construction|track|unclassified|residential|.*_link)", | |
busway="highway:busway", | |
tree_row="natural:tree_row", # maybe merge with node? | |
) | |
# + nodes: bollard | |
nodes = dict( | |
tree="natural:tree", | |
stone="(natural:stone|barrier:block)", | |
crossing="highway:crossing", | |
lamp="highway:street_lamp", | |
traffic_signal="highway:traffic_signals", | |
bus_stop="highway:bus_stop", | |
stop_sign="highway:stop", | |
junction="highway:motorway_junction", | |
bus_stop_position="public_transport:stop_position", | |
gate="barrier:(gate|lift_gate|swing_gate|cycle_barrier)", | |
bollard="barrier:bollard", | |
shop="(shop.*?|amenity:(bank|post_office))", | |
restaurant="amenity:(restaurant|fast_food)", | |
bar="amenity:(cafe|bar|pub|biergarten)", | |
pharmacy="amenity:pharmacy", | |
fuel="amenity:fuel", | |
bicycle_parking="amenity:(bicycle_parking|bicycle_rental)", | |
charging_station="amenity:charging_station", | |
parking_entrance="amenity:parking_entrance", | |
atm="amenity:atm", | |
toilets="amenity:toilets", | |
vending_machine="amenity:vending_machine", | |
fountain="amenity:fountain", | |
waste_basket="amenity:(waste_basket|waste_disposal)", | |
bench="amenity:bench", | |
post_box="amenity:post_box", | |
artwork="tourism:artwork", | |
recycling="amenity:recycling", | |
give_way="highway:give_way", | |
clock="amenity:clock", | |
fire_hydrant="emergency:fire_hydrant", | |
pole="man_made:(flagpole|utility_pole)", | |
street_cabinet="man_made:street_cabinet", | |
) | |
# + ways: kerb | |
class Groups: | |
areas = list(Patterns.areas) | |
ways = list(Patterns.ways) | |
nodes = list(Patterns.nodes) | |
def group_elements(osm: OSMData): | |
elem2group = { | |
"area": {}, | |
"way": {}, | |
"node": {}, | |
} | |
for node in filter(filter_node, osm.nodes.values()): | |
label = parse_node(node.tags) | |
if label is None: | |
continue | |
group = match_to_group(label, Patterns.nodes) | |
if group is None: | |
group = match_to_group(label, Patterns.ways) | |
if group is None: | |
continue # missing | |
elem2group["node"][node.id_] = group | |
for way in filter(filter_way, osm.ways.values()): | |
label = parse_way(way.tags) | |
if label is None: | |
continue | |
group = match_to_group(label, Patterns.ways) | |
if group is None: | |
group = match_to_group(label, Patterns.nodes) | |
if group is None: | |
continue # missing | |
elem2group["way"][way.id_] = group | |
for area in filter(filter_area, osm.ways.values()): | |
label = parse_area(area.tags) | |
if label is None: | |
continue | |
group = match_to_group(label, Patterns.areas) | |
if group is None: | |
group = match_to_group(label, Patterns.ways) | |
if group is None: | |
group = match_to_group(label, Patterns.nodes) | |
if group is None: | |
continue # missing | |
elem2group["area"][area.id_] = group | |
return elem2group | |