diff -r 659ab465152e -r f9788970fa46 regions.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/regions.py Wed Jul 29 23:45:53 2020 +0300 @@ -0,0 +1,88 @@ +#!/usr/bin/env python3 +from xml.etree import ElementTree +from geometry import * + +REGION_TYPES = ['major', 'minor'] +REGION_KEY_VALUES = [x + '_region' for x in REGION_TYPES] +SHAPE_KEYS = [x + '_shapes' for x in REGION_TYPES] + +def parse_nodes(root): + nodes = {} + for child in root: + if child.tag == 'node': + lat, lon = float(child.attrib['lat']), float(child.attrib['lon']) + nodes[child.attrib['id']] = Location(lat, lon) + return nodes + +def parse_way(way, nodes): + def choose_shapes(way, boundary): + return (way['major_shapes'] + if boundary == 'major_region' + else way['minor_shapes']) + result = {'minor_shapes': [], 'major_shapes': []} + shape = [] + for child in way: + if child.tag == 'nd': + shape.append(child.attrib['ref']) + elif child.tag == 'tag': + key = child.attrib['k'] + if key in SHAPE_KEYS: + raise ValueError(str.format('tag "{}" is not allowed', key)) + result[key] = child.attrib['v'] + if key == 'boundary' and result['boundary'] not in REGION_KEY_VALUES: + return None # we're not interested in it! + if shape[-1] != shape[0]: + raise ValueError('polygon is not closed: %r' % result) + if 'boundary' not in result: + raise ValueError('polygon not tagged as a boundary: %r' % result) + shape = [nodes[ref] for ref in shape[:-1]] + choose_shapes(result, result['boundary']).append(Polygon(*shape)) + return result + +def parse_boundaries(root, *, nodes): + for child in root: + if child.tag == 'way': + way = parse_way(child, nodes = nodes) + if way: + yield way + +def parse_regions(filename): + from katakana import transliterate as transliterate_katakana + tree = ElementTree.parse(filename) + root = tree.getroot() + nodes = parse_nodes(root) + regions = dict() + extra_shapes = list() + for way in parse_boundaries(root, nodes = nodes): + if 'boundary' in way and way['boundary'] != 'subregion' and 'name' in way: + # defines a region + way['via_factor'] = int(way.get('via_factor', 1)) + if way['name'] in regions: + raise ValueError(str.format( + 'Region {name} defined twice', + name = repr(way['name']), + )) + regions[way['name']] = way + del way['boundary'] + if 'external' in way: + way['boundary'] = 'minor_region' + for prefix in ['', 'short_', 'internal_']: + name_key = prefix + 'name' + if name_key in way and way[name_key] and name_key + ':ja' not in way: + way[name_key + ':ja'] = transliterate_katakana(way[name_key]) + elif 'boundary' in way and 'is_in' in way: + # adds an extra shape to an existing region + extra_shapes.append(way) + for extra_shape in extra_shapes: + name = extra_shape['is_in'] + try: + region = regions[name] + except KeyError: + raise ValueError(str.format( + 'Extra shape refers to {name} which was not found: {extra_shape}', + name = repr(name), + extra_shape = repr(extra_shape), + )) + for key in SHAPE_KEYS: + region[key].extend(extra_shape[key]) + return regions