|
1 #!/usr/bin/env python3 |
|
2 from xml.etree import ElementTree |
|
3 from geometry import * |
|
4 |
|
5 REGION_TYPES = ['major', 'minor'] |
|
6 REGION_KEY_VALUES = [x + '_region' for x in REGION_TYPES] |
|
7 SHAPE_KEYS = [x + '_shapes' for x in REGION_TYPES] |
|
8 |
|
9 def parse_nodes(root): |
|
10 nodes = {} |
|
11 for child in root: |
|
12 if child.tag == 'node': |
|
13 lat, lon = float(child.attrib['lat']), float(child.attrib['lon']) |
|
14 nodes[child.attrib['id']] = Location(lat, lon) |
|
15 return nodes |
|
16 |
|
17 def parse_way(way, nodes): |
|
18 def choose_shapes(way, boundary): |
|
19 return (way['major_shapes'] |
|
20 if boundary == 'major_region' |
|
21 else way['minor_shapes']) |
|
22 result = {'minor_shapes': [], 'major_shapes': []} |
|
23 shape = [] |
|
24 for child in way: |
|
25 if child.tag == 'nd': |
|
26 shape.append(child.attrib['ref']) |
|
27 elif child.tag == 'tag': |
|
28 key = child.attrib['k'] |
|
29 if key in SHAPE_KEYS: |
|
30 raise ValueError(str.format('tag "{}" is not allowed', key)) |
|
31 result[key] = child.attrib['v'] |
|
32 if key == 'boundary' and result['boundary'] not in REGION_KEY_VALUES: |
|
33 return None # we're not interested in it! |
|
34 if shape[-1] != shape[0]: |
|
35 raise ValueError('polygon is not closed: %r' % result) |
|
36 if 'boundary' not in result: |
|
37 raise ValueError('polygon not tagged as a boundary: %r' % result) |
|
38 shape = [nodes[ref] for ref in shape[:-1]] |
|
39 choose_shapes(result, result['boundary']).append(Polygon(*shape)) |
|
40 return result |
|
41 |
|
42 def parse_boundaries(root, *, nodes): |
|
43 for child in root: |
|
44 if child.tag == 'way': |
|
45 way = parse_way(child, nodes = nodes) |
|
46 if way: |
|
47 yield way |
|
48 |
|
49 def parse_regions(filename): |
|
50 from katakana import transliterate as transliterate_katakana |
|
51 tree = ElementTree.parse(filename) |
|
52 root = tree.getroot() |
|
53 nodes = parse_nodes(root) |
|
54 regions = dict() |
|
55 extra_shapes = list() |
|
56 for way in parse_boundaries(root, nodes = nodes): |
|
57 if 'boundary' in way and way['boundary'] != 'subregion' and 'name' in way: |
|
58 # defines a region |
|
59 way['via_factor'] = int(way.get('via_factor', 1)) |
|
60 if way['name'] in regions: |
|
61 raise ValueError(str.format( |
|
62 'Region {name} defined twice', |
|
63 name = repr(way['name']), |
|
64 )) |
|
65 regions[way['name']] = way |
|
66 del way['boundary'] |
|
67 if 'external' in way: |
|
68 way['boundary'] = 'minor_region' |
|
69 for prefix in ['', 'short_', 'internal_']: |
|
70 name_key = prefix + 'name' |
|
71 if name_key in way and way[name_key] and name_key + ':ja' not in way: |
|
72 way[name_key + ':ja'] = transliterate_katakana(way[name_key]) |
|
73 elif 'boundary' in way and 'is_in' in way: |
|
74 # adds an extra shape to an existing region |
|
75 extra_shapes.append(way) |
|
76 for extra_shape in extra_shapes: |
|
77 name = extra_shape['is_in'] |
|
78 try: |
|
79 region = regions[name] |
|
80 except KeyError: |
|
81 raise ValueError(str.format( |
|
82 'Extra shape refers to {name} which was not found: {extra_shape}', |
|
83 name = repr(name), |
|
84 extra_shape = repr(extra_shape), |
|
85 )) |
|
86 for key in SHAPE_KEYS: |
|
87 region[key].extend(extra_shape[key]) |
|
88 return regions |