""" Reading OpenStreetMap data from XML file. Author: Sergey Vartanov """ from typing import Any, Dict, List, Optional from roentgen import ui class OSMNode: """ OpenStreetMap node. See https://wiki.openstreetmap.org/wiki/Node """ def __init__(self, id_: int = 0, lat: float = 0, lon: float = 0): self.id_: int = id_ self.lat: float = lat self.lon: float = lon self.tags: Dict[str, str] = {} self.visible: Optional[str] = None self.changeset: Optional[str] = None self.timestamp: Optional[str] = None self.user: Optional[str] = None self.uid: Optional[str] = None def parse_from_xml(self, text: str, is_full: bool = False) -> "OSMNode": """ Parse from XML node representation. :param text: XML node representation :param is_full: if false, parse only ID, latitude and longitude """ self.id_ = int(get_value("id", text)) self.lat = float(get_value("lat", text)) self.lon = float(get_value("lon", text)) if is_full: self.visible = get_value("visible", text) self.changeset = get_value("changeset", text) self.timestamp = get_value("timestamp", text) self.user = get_value("user", text) self.uid = get_value("uid", text) return self class OSMWay: """ OpenStreetMap way. See https://wiki.openstreetmap.org/wiki/Way """ def __init__(self, id_: int = 0, nodes=None): self.id_: int = id_ self.nodes: List[int] = [] if nodes is None else nodes self.tags: Dict[str, str] = {} self.visible: Optional[str] = None self.changeset: Optional[str] = None self.user: Optional[str] = None self.timestamp: Optional[str] = None self.uid: Optional[str] = None def parse_from_xml(self, text: str, is_full: bool = False) -> "OSMWay": """ Parse from XML way representation. :param text: XML way representation :param is_full: if false, parse only ID """ self.id_ = int(get_value("id", text)) if is_full: self.visible = get_value("visible", text) self.changeset = get_value("changeset", text) self.timestamp = get_value("timestamp", text) self.user = get_value("user", text) self.uid = get_value("uid", text) return self def is_cycle(self) -> bool: return self.nodes[0] == self.nodes[-1] def try_to_glue(self, other: "OSMWay"): if self.nodes[0] == other.nodes[0]: return OSMWay(nodes=list(reversed(other.nodes[1:])) + self.nodes) elif self.nodes[0] == other.nodes[-1]: return OSMWay(nodes=other.nodes[:-1] + self.nodes) elif self.nodes[-1] == other.nodes[-1]: return OSMWay(nodes=self.nodes + list(reversed(other.nodes[:-1]))) elif self.nodes[-1] == other.nodes[0]: return OSMWay(nodes=self.nodes + other.nodes[1:]) def __repr__(self): return f"Way <{self.id_}> {self.nodes}" class OSMRelation: """ OpenStreetMap relation. See https://wiki.openstreetmap.org/wiki/Relation """ def __init__(self, id_: int = 0): self.id_: int = id_ self.tags: Dict[str, str] = {} self.members: List["OSMMember"] = [] def parse_from_xml(self, text: str) -> "OSMRelation": """ Parse from XML relation representation. :param text: XML way representation """ self.id_ = int(get_value("id", text)) return self class OSMMember: """ Member of OpenStreetMap relation. """ def __init__(self, text: str): self.type_ = get_value("type", text) self.ref = int(get_value("ref", text)) self.role = get_value("role", text) def get_value(key: str, text: str): """ Parse xml value from the tag in the format of key="value". """ if key + '="' in text: index: int = text.find(key + '="') value = text[index + len(key) + 2:text.find('"', index + len(key) + 4)] return value class Map: def __init__(self, node_map, way_map, relation_map): self.node_map = node_map self.way_map = way_map self.relation_map = relation_map class OSMReader: """ OSM XML representation reader. """ def __init__(self, file_name: str): self.file_name = file_name def parse_osm_file( self, parse_nodes: bool = True, parse_ways: bool = True, parse_relations: bool = True, full: bool = False) -> Map: """ Parse OSM XML representation. """ node_map: Dict[int, OSMNode] = {} way_map: Dict[int, OSMWay] = {} relation_map: Dict[int, OSMRelation] = {} print(f"Line number counting for {self.file_name}...") with open(self.file_name) as f: for lines_number, _ in enumerate(f): pass print("Done.") print(f"Parsing OSM file {self.file_name}...") input_file = open(self.file_name) line = input_file.readline() line_number = 0 while line != "": line_number += 1 ui.write_line(line_number, lines_number) # Node parsing. if line[:6] in [" \n", "\t\n", " \n"]: node_map[element.id_] = element # Way parsing. elif line[:5] in [' \n', '\t\n'] or line == " \n": way_map[element.id_] = element # Relation parsing. elif line[:10] in [" \n", "\t\n"] or \ line == " \n": relation_map[element.id_] = element # Elements parsing. elif line[:6] in ["