regions.py

changeset 1
f9788970fa46
child 2
7378b802ddf8
--- /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

mercurial