| | |
| |
|
| | 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 |
| |
|