| |
|
|
| 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) |
| |
| |
| 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 = 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", |
| ) |
| |
|
|
| 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", |
| ) |
| |
|
|
|
|
| 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 |
| 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 |
| 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 |
| elem2group["area"][area.id_] = group |
|
|
| return elem2group |
|
|