From 3e10d9c1eb512b93827112de7190a1277fe9232b Mon Sep 17 00:00:00 2001 From: Sergey Vartanov Date: Tue, 28 Jul 2015 23:06:57 +0300 Subject: [PATCH] Faster parsing; new drawing rules. --- mapper.py | 481 ++++++++++++++++++++++++++------------------------ osm_reader.py | 164 +++++++++++------ 2 files changed, 358 insertions(+), 287 deletions(-) diff --git a/mapper.py b/mapper.py index af2b173..e6b0ba3 100644 --- a/mapper.py +++ b/mapper.py @@ -2,16 +2,20 @@ # -*- coding: utf-8 -*- from __future__ import unicode_literals """ -Simple tool for drawing OSM data. +Simple tool for working with OpenStreetMap data. -Author: Sergey Vartnov +Author: Sergey Vartanov (me@enzet.ru). """ +import datetime import os import re import sys import xml.dom.minidom +import extract_icon +import osm_reader + from flinger import GeoFlinger, Geo sys.path.append('lib') @@ -19,24 +23,19 @@ sys.path.append('lib') import svg from vector import Vector -# class OSMGetter: -# def get(x1, x2, y1, y2): -# input_file = open(cache_folder + '/osm-' + `x1` + '-' + `y1` + '-' + \ -# `x2` + '-' + `y2` -# if not os.path.isfile(input_file): - input_file_name = sys.argv[1] if not os.path.isfile(input_file_name): + print 'Fatal: no such file: ' + input_file_name + '.' sys.exit(1) -print 'Reading input OSM file...' +#start_time = datetime.datetime.now() +#input_file = open(input_file_name) +#content = xml.dom.minidom.parse(input_file) +#input_file.close() +#print 'File readed in ' + str(datetime.datetime.now() - start_time) + '.' -input_file = open(input_file_name) -content = xml.dom.minidom.parse(input_file) -input_file.close() - -print 'Done.' +node_map, way_map, relation_map = osm_reader.parse_osm_file(input_file_name) output_file = svg.SVG(open(sys.argv[2], 'w+')) @@ -53,6 +52,11 @@ water_color = 'AACCFF' water_border_color = '6688BB' wood_color = 'B8CC84' +undraw = ['operator', 'contact:facebook', 'contact:phone', 'opening_hours', + 'cuisine', 'network', 'website', 'contact:website', 'phone', + 'branch', 'route_ref', 'addr:flats', 'brand', 'ref', 'addr:street', + 'wikipedia', 'description', 'layer', 'level'] + output_file.begin(w, h) output_file.rect(0, 0, w, h, color=background_color) @@ -66,13 +70,13 @@ if len(sys.argv) > 3: authors = {} missed_tags = {} -d_street_lamp = 'm 7,1 -3.5,3 1.59375,0 1,5 1.40625,0 0,6 1,0 0,-6 1.40625,0 1,-5 L 12.5,4 9,1 z M 6.125,4 9.875,4 9.09375,8 6.90625,8 z' -d_clinic = 'M 7,5 7,7 5,7 5,9 7,9 7,11 9,11 9,9 11,9 11,7 9,7 9,5 7,5' -d_crossing = 'm 3,5 0,6 1,0 0,-6 z m 4,0 0,6 1,0 0,-6 z m 4,0 0,6 1,0 0,-6 z' -d_traffic_signal = 'M 7,1 C 6.446,1 6,1.446 6,2 L 6,4 C 6,4.1879013 6.0668244,4.3501348 6.15625,4.5 6.0668244,4.6498652 6,4.8120987 6,5 L 6,7 C 6,7.1879013 6.0668244,7.3501348 6.15625,7.5 6.0668244,7.6498652 6,7.8120987 6,8 l 0,2 c 0,0.554 0.446,1 1,1 l 0.5,0 0,3 -1.5,0 0,1 4,0 0,-1 -1.5,0 0,-3 0.5,0 c 0.554,0 1,-0.446 1,-1 L 10,8 C 10,7.8120987 9.9331756,7.6498652 9.84375,7.5 9.9331756,7.3501348 10,7.1879013 10,7 L 10,5 C 10,4.8120987 9.9331756,4.6498652 9.84375,4.5 9.9331756,4.3501348 10,4.1879013 10,4 L 10,2 C 10,1.446 9.554,1 9,1 z M 7,2 9,2 9,4 7,4 z M 7,5 9,5 9,7 7,7 z m 0,3 2,0 0,2 -2,0 z' -d_tree = 'M 7 2 C 6.446 2 6 2.446 6 3 C 5.446 3 5 3.4460001 5 4 L 5 7 C 5 7.5539999 5.446 8 6 8 C 6 8.554 6.446 9 7 9 L 7.5625 9 L 7.5625 12 L 8.4375 12 L 8.4375 9 L 9 9 C 9.554 9 10 8.554 10 8 C 10.554 8 11 7.5539999 11 7 L 11 4 C 11 3.4460001 10.554 3 10 3 C 10 2.446 9.554 2 9 2 L 7 2 z' - def get_d_from_file(file_name): + path, x, y = icons.get_path(file_name) + if path: + return path, x, y + else: + print 'No such icon: ' + file_name + return 'M 4,4 L 4,10 10,10 10,4 4,4', 0, 0 if os.path.isfile('icons/' + file_name + '.svg'): file_name = 'icons/' + file_name + '.svg' size = 16 @@ -97,39 +101,21 @@ def get_d_from_file(file_name): if m: return m.group('path'), size -# Construct node map -def construct_node_map(): - """ - Construct map of nodes. - """ - print 'Construct node map...' - node_map = {} - for element in content.childNodes[0].childNodes: - if element.nodeType != element.TEXT_NODE: - if 'user' in element.attributes.keys(): - author = element.attributes['user'].value - if author in authors: - authors[author] += 1 - else: - authors[author] = 1 - if not ('lat' in element.attributes.keys()): - continue - node_map[element.attributes['id'].value] = \ - Geo(float(element.attributes['lat'].value), - float(element.attributes['lon'].value)) - if float(element.attributes['lat'].value) > maximum.lat: - maximum.lat = float(element.attributes['lat'].value) - if float(element.attributes['lat'].value) < minimum.lat: - minimum.lat = float(element.attributes['lat'].value) - if float(element.attributes['lon'].value) > maximum.lon: - maximum.lon = float(element.attributes['lon'].value) - if float(element.attributes['lon'].value) < minimum.lon: - minimum.lon = float(element.attributes['lon'].value) - print 'Done.' - return node_map +def get_min_max(node_map): + key = node_map.keys()[0] + maximum = Geo(node_map[key]['lon'], node_map[key]['lat']) + minimum = Geo(node_map[key]['lon'], node_map[key]['lat']) + for node_id in node_map: + node = node_map[node_id] + if node['lat'] > maximum.lat: maximum.lat = node['lat'] + if node['lat'] < minimum.lat: minimum.lat = node['lat'] + if node['lon'] > maximum.lon: maximum.lon = node['lon'] + if node['lon'] < minimum.lon: minimum.lon = node['lon'] + return minimum, maximum -node_map = construct_node_map() + +minimum, maximum = get_min_max(node_map) if len(sys.argv) > 3: flinger = GeoFlinger(min1, max1, Vector(0, 0), Vector(w, h)) @@ -140,9 +126,10 @@ else: def draw_path(nodes, style, shift=Vector()): prev_node = None for node_id in nodes: - flinged1 = flinger.fling(node_map[node_id]) + shift + node = node_map[node_id] + flinged1 = flinger.fling(Geo(node['lat'], node['lon'])) + shift if prev_node: - flinged2 = flinger.fling(prev_node) + shift + flinged2 = flinger.fling(Geo(prev_node['lat'], prev_node['lon'])) + shift output_file.write('L ' + `flinged1.x` + ',' + `flinged1.y` + ' ') else: output_file.write('\n') def draw_point_shape(name, x, y, fill): - shape, size = get_d_from_file(name) - draw_point(shape, x, y, fill, size=size) + shape, xx, yy = get_d_from_file(name) + draw_point(shape, x, y, fill, size=16, xx=xx, yy=yy) -def draw_point(shape, x, y, fill, size=16): +def draw_point(shape, x, y, fill, size=16, xx=0, yy=0): x = int(float(x)) y = int(float(y)) output_file.write('\n') + str(x - size / 2.0 - xx * 16) + ',' + str(y - size / 2.0 - yy * 16) + ')" />\n') output_file.write('\n') + str(x - size / 2.0 - xx * 16) + ',' + str(y - size / 2.0 - yy * 16) + ')" />\n') def draw_text(text, x, y, fill): if type(text) == unicode: @@ -177,128 +164,103 @@ def draw_text(text, x, y, fill): output_file.write('' + text + '') -def point(k, v, x, y, fill): +def point(k, v, x, y, fill, text_y): if ('node ' + k + ': ' + v) in missed_tags: missed_tags['node ' + k + ': ' + v] += 1 else: missed_tags['node ' + k + ': ' + v] = 1 - output_file.circle(x, y, 2, fill=fill, color=fill) + #output_file.circle(float(x), float(y), 2, fill=fill, color=fill) text = k + ': ' + v if type(text) == unicode: text = text.encode('utf-8') - output_file.write('' + text + '') # Ways drawing -def construct_way_map(): +def construct_layers(): """ - Construct map of ways and distribute them by layers. One layer may contain - elements of different types. + Construct layers. One layer may contain elements of different types. """ - print 'Construct way map...' - layers = {} - way_map = {} - for element in content.childNodes[0].childNodes: - if element.nodeName == 'way': - way = {'nodes': [], 'tags': {}} - way_map[element.attributes['id'].value] = way - - for element in content.childNodes[0].childNodes: - if element.nodeName == 'relation': - relation = {'members': [], 'tags': {}} - for child_node in element.childNodes: - if isinstance(child_node, xml.dom.minidom.Element): - if child_node.tagName == 'tag': - k = child_node.attributes['k'].value - v = child_node.attributes['v'].value - relation['tags'][k] = v - for child_node in element.childNodes: - if isinstance(child_node, xml.dom.minidom.Element): - if child_node.tagName == 'member': - index = child_node.attributes['ref'].value - if index in way_map: - way = way_map[index] - for tag in relation['tags']: - way['tags'][tag] = relation['tags'][tag] - - for element in content.childNodes[0].childNodes: - if element.nodeName == 'way': - way = way_map[element.attributes['id'].value] - for child_node in element.childNodes: - if isinstance(child_node, xml.dom.minidom.Element): - if child_node.tagName == 'tag': - k = child_node.attributes['k'].value - v = child_node.attributes['v'].value - way['tags'][k] = v - if child_node.tagName == 'nd': - way['nodes'].append(child_node.attributes['ref'].value) - if not ('layer' in way['tags']): - way['tags']['layer'] = 0 - if not (int(way['tags']['layer']) in layers): - layers[int(way['tags']['layer'])] = \ - {'b': [], 'h1': [], 'h2': [], 'r': [], 'n': [], 'l': [], - 'a': [], 'le': [], 'ba': [], 'bo': []} - layer = layers[int(way['tags']['layer'])] - if 'building' in way['tags']: - layer['b'].append(way) - if 'natural' in way['tags']: - layer['n'].append(way) - if 'landuse' in way['tags']: - layer['l'].append(way) - if 'railway' in way['tags']: - layer['r'].append(way) - if 'amenity' in way['tags']: - layer['a'].append(way) - if 'leisure' in way['tags']: - layer['le'].append(way) - if 'barrier' in way['tags']: - layer['ba'].append(way) - if 'highway' in way['tags']: - layer['h1'].append(way) - layer['h2'].append(way) - if 'boundary' in way['tags']: - layer['bo'].append(way) - #else: - # empty = True - # for key in way['tags'].keys(): - # if not (key in ['layer', 'note']): - # empty = False - # if not empty: - # print 'Unknown kind of way:', way['tags'] - return layers, way_map + for way_id in way_map: + way = way_map[way_id] + if not ('layer' in way['tags']): + way['tags']['layer'] = 0 + if not (int(way['tags']['layer']) in layers): + layers[int(way['tags']['layer'])] = \ + {'b': [], 'h1': [], 'h2': [], 'r': [], 'n': [], 'l': [], + 'a': [], 'le': [], 'ba': [], 'bo': []} + layer = layers[int(way['tags']['layer'])] + if 'building' in way['tags']: + layer['b'].append(way) + if 'natural' in way['tags']: + layer['n'].append(way) + if 'landuse' in way['tags']: + layer['l'].append(way) + if 'railway' in way['tags']: + layer['r'].append(way) + if 'amenity' in way['tags']: + layer['a'].append(way) + if 'leisure' in way['tags']: + layer['le'].append(way) + if 'barrier' in way['tags']: + layer['ba'].append(way) + if 'highway' in way['tags']: + layer['h1'].append(way) + layer['h2'].append(way) + if 'boundary' in way['tags']: + layer['bo'].append(way) + #else: + # empty = True + # for key in way['tags'].keys(): + # if not (key in ['layer', 'note']): + # empty = False + # if not empty: + # print 'Unknown kind of way:', way['tags'] + return layers -layers, way_map = construct_way_map() +layers = construct_layers() def draw_raw_ways(): for way_id in way_map: way = way_map[way_id] draw_path(way['nodes'], 'stroke:#FFFFFF;fill:none;stroke-width:0.2;') +def line_center(node_ids): + ma = Vector() + mi = Vector(10000, 10000) + for node_id in node_ids: + node = node_map[node_id] + flinged = flinger.fling(Geo(node['lat'], node['lon'])) + if flinged.x > ma.x: ma.x = flinged.x + if flinged.y > ma.y: ma.y = flinged.y + if flinged.x < mi.x: mi.x = flinged.x + if flinged.y < mi.y: mi.y = flinged.y + return Vector((ma.x + mi.x) / 2.0, (ma.y + mi.y) / 2.0) + + def draw_ways(): for level in sorted(layers.keys()): layer = layers[level] #for entity in ['b', 'h1', 'h2', 'r', 'n', 'l', 'a', 'le', 'ba']: + + # Pre part. + for way in layer['le']: if way['tags']['leisure'] == 'park': style = 'fill:#' + grass_color + ';' else: continue draw_path(way['nodes'], style) + + # Post part. + for way in layer['l']: - ma = Vector() - mi = Vector(10000, 10000) - for node_id in way['nodes']: - node = node_map[node_id] - flinged = flinger.fling(node) - if flinged.x > ma.x: ma.x = flinged.x - if flinged.y > ma.y: ma.y = flinged.y - if flinged.x < mi.x: mi.x = flinged.x - if flinged.y < mi.y: mi.y = flinged.y - c = Vector((ma.x + mi.x) / 2.0, (ma.y + mi.y) / 2.0) + text_y = 0 + c = line_center(way['nodes']) if way['tags']['landuse'] == 'grass': style = 'fill:#' + grass_color + ';stroke:none;' draw_path(way['nodes'], style) @@ -317,17 +279,12 @@ def draw_ways(): else: style = 'fill:#0000FF;stroke:none;' draw_path(way['nodes'], style) + for tag in way['tags']: + if not (tag in ['landuse', 'layer']): + point(tag, str(way['tags'][tag]), c.x, c.y, '000000', text_y) + text_y += 10 for way in layer['a']: - ma = Vector() - mi = Vector(10000, 10000) - for node_id in way['nodes']: - node = node_map[node_id] - flinged = flinger.fling(node) - if flinged.x > ma.x: ma.x = flinged.x - if flinged.y > ma.y: ma.y = flinged.y - if flinged.x < mi.x: mi.x = flinged.x - if flinged.y < mi.y: mi.y = flinged.y - c = Vector((ma.x + mi.x) / 2.0, (ma.y + mi.y) / 2.0) + c = line_center(way['nodes']) if way['tags']['amenity'] == 'parking': style = 'fill:#' + parking_color + ';stroke:none;' draw_path(way['nodes'], style) @@ -357,7 +314,7 @@ def draw_ways(): draw_path(way['nodes'], style) for way in layer['r']: v = way['tags']['railway'] - style = 'fill:none;stroke-dasharray:none;stroke-linejoin:round;stroke-linecap:butt;stroke-width:' + style = 'fill:none;stroke-dasharray:none;stroke-linejoin:round;stroke-linecap:round;stroke-width:' if v == 'subway': style += '10;stroke:#DDDDDD;' if v in ['narrow_gauge', 'tram']: style += '2;stroke:#000000;' @@ -366,11 +323,11 @@ def draw_ways(): draw_path(way['nodes'], style) for way in layer['h1']: if 'tunnel' in way['tags'] and way['tags']['tunnel'] == 'yes': - style = 'fill:none;stroke:#FFFFFF;stroke-dasharray:none;stroke-linejoin:round;stroke-linecap:butt;stroke-width:10;' + style = 'fill:none;stroke:#FFFFFF;stroke-dasharray:none;stroke-linejoin:round;stroke-linecap:round;stroke-width:10;' draw_path(way['nodes'], style) for way in layer['h1']: v = way['tags']['highway'] - style = 'fill:none;stroke:#AAAAAA;stroke-dasharray:none;stroke-linejoin:round;stroke-linecap:butt;stroke-width:' + style = 'fill:none;stroke:#AAAAAA;stroke-dasharray:none;stroke-linejoin:round;stroke-linecap:round;stroke-width:' if v == 'motorway': style += '30' elif v == 'trunk': style += '25' elif v == 'primary': style += '20;stroke:#AA8800' @@ -381,15 +338,15 @@ def draw_ways(): elif v == 'service': style += '7' elif v == 'track': style += '3' elif v in ['footway', 'pedestrian']: style += '2' - elif v == 'steps': style += '5;stroke-dasharray:1,2;stroke-linecap:butt' - elif v == 'path': style += '1;stroke-dasharray:5,5;stroke-linecap:butt' + elif v == 'steps': style += '5;stroke-dasharray:1,2;stroke-linecap:round' + elif v == 'path': style += '1;stroke-dasharray:5,5;stroke-linecap:round' else: continue style += ';' draw_path(way['nodes'], style) for way in layer['h2']: v = way['tags']['highway'] - style = 'fill:none;stroke:#FFFFFF;stroke-linecap:butt;stroke-linejoin:round;stroke-width:' + style = 'fill:none;stroke:#FFFFFF;stroke-linecap:round;stroke-linejoin:round;stroke-width:' if v == 'motorway': style += '28' elif v == 'trunk': style += '23' elif v == 'primary': style += '19;stroke:#FFDD66' @@ -408,16 +365,7 @@ def draw_ways(): #if 'building:levels' in way['tags']: #floors = float(way['tags']['building:levels']) draw_path(way['nodes'], 'fill:#D0D0C0;stroke:#AAAAAA;opacity:1.0;') - ma = Vector() - mi = Vector(10000, 10000) - for node_id in way['nodes']: - node = node_map[node_id] - flinged = flinger.fling(node) - if flinged.x > ma.x: ma.x = flinged.x - if flinged.y > ma.y: ma.y = flinged.y - if flinged.x < mi.x: mi.x = flinged.x - if flinged.y < mi.y: mi.y = flinged.y - c = Vector((ma.x + mi.x) / 2.0, (ma.y + mi.y) / 2.0) + c = line_center(way['nodes']) for tag in way['tags']: v = way['tags'][tag] if tag == 'building': @@ -461,22 +409,13 @@ def draw_ways(): pass else: kk = tag - vv = unicode(v) + vv = v if ('way ' + kk + ': ' + vv) in missed_tags: missed_tags['way ' + kk + ': ' + vv] += 1 else: missed_tags['way ' + kk + ': ' + vv] = 1 for way in layer['le']: - ma = Vector() - mi = Vector(10000, 10000) - for node_id in way['nodes']: - node = node_map[node_id] - flinged = flinger.fling(node) - if flinged.x > ma.x: ma.x = flinged.x - if flinged.y > ma.y: ma.y = flinged.y - if flinged.x < mi.x: mi.x = flinged.x - if flinged.y < mi.y: mi.y = flinged.y - c = Vector((ma.x + mi.x) / 2.0, (ma.y + mi.y) / 2.0) + c = line_center(way['nodes']) if way['tags']['leisure'] == 'playground': style = 'fill:#' + playground_color + ';opacity:0.2;' draw_point_shape('playground', c.x, c.y, '444444') @@ -509,89 +448,157 @@ def draw_raw_nodes(): flinged = flinger.fling(node) output_file.circle(flinged.x, flinged.y, 0.2, color='FFFFFF') +def no_draw(key): + return key in undraw or 'name' in key or 'addr' in key + def draw_nodes(): print 'Draw nodes...' - for element in content.childNodes[0].childNodes: - if element.nodeName != 'node': - continue - flinged = flinger.fling(Geo(float(element.attributes['lat'].value), - float(element.attributes['lon'].value))) + for node_id in node_map: + node = node_map[node_id] + flinged = flinger.fling(Geo(node['lat'], node['lon'])) + x = `flinged.x` + y = `flinged.y` text_y = 0 - pairs = {} - for child_node in element.childNodes: - if isinstance(child_node, xml.dom.minidom.Element): - if child_node.tagName == 'tag': - pairs[child_node.attributes['k'].value] = child_node.attributes['v'].value + if 'tags' in node: + pairs = node['tags'] + else: + pairs = {} fill = '444444' if 'colour' in pairs: if pairs['colour'] == 'blue': fill='2233AA' radius=3 - for k in pairs: + + if pairs == {}: + pass + elif 'amenity' in pairs: + k = 'amenity' + v = pairs['amenity'] + if v in ['bench', 'bicycle_parking', 'cafe', 'waste_basket', 'clinic', + 'restaurant', 'pharmacy', 'drinking_water', 'toilets', + 'theatre', 'bar', 'bank', 'pub', 'post_office']: + draw_point_shape(v, x, y, fill) + elif v == 'fast_food': + main = 'fast_food' + if 'operator' in pairs: + if pairs['operator'] == "McDonald's": + main = 'mcdonalds' + if 'operator:en' in pairs: + if pairs['operator:en'] == "McDonald's": + main = 'mcdonalds' + draw_point_shape(main, x, y, fill) + elif v == 'shop': + if 'shop' in pairs: + if pairs['shop'] in ['fishing']: + draw_point_shape('shop_' + pairs['shop'], x, y, fill) + elif v == 'fountain': + draw_point_shape('fountain', x, y, water_border_color) + elif v == 'recycling': + if not 'recycling_type' in pairs: + draw_point_shape('recycling', x, y, fill) + else: + point(k, v, flinged.x, flinged.y, fill, text_y) + text_y += 10 + for k in pairs: + if 'karaoke' in pairs: + if pairs['karaoke'] == 'yes': + draw_point_shape('microphone', flinged.x + 16, y, fill) + else: + point(k, pairs[k], x, y, fill, text_y) + elif not no_draw(k) and k != 'amenity': + point(k, pairs[k], x, y, fill, text_y) + text_y += 10 + elif 'natural' in pairs: + k = 'natural' + v = pairs['natural'] + if v == 'tree': + main = 'tree' + if 'leaf_type' in pairs and pairs['leaf_type'] in ['broadleaved', 'needleleaved']: + main = pairs['leaf_type'] + if 'denotation' in pairs and pairs['denotation'] == 'urban': + draw_point_shape([main, 'urban_tree_pot'], x, y, wood_color) + else: + draw_point_shape(main, x, y, wood_color) + elif v == 'cave_entrance': + draw_point_shape('cave', x, y, fill) + elif v == 'bush': + draw_point_shape('bush', x, y, wood_color) + else: + point(k, v, flinged.x, flinged.y, fill, text_y) + text_y += 10 + elif 'entrance' in pairs: + k = 'entrance' + v = pairs['entrance'] + if v == 'yes': + draw_point_shape('entrance', x, y, fill) + elif v == 'staircase': + draw_point_shape('staircase', x, y, fill) + elif 'highway' in pairs: + k = 'highway' + v = pairs['highway'] + if v == 'crossing': + shape = 'crossing' + if 'crossing' in pairs: + if pairs['crossing'] == 'zebra': + shape = 'zebra' + elif 'crossing_ref' in pairs: + if pairs['crossing_ref'] == 'zebra': + shape = 'zebra' + draw_point_shape(shape, x, y, fill) + elif v == 'street_lamp': + draw_point_shape('street_lamp', x, y, fill) + else: + for k in pairs: + if not no_draw(k): + point(k, pairs[k], x, y, fill, text_y) + text_y += 10 + + + for k in []: v = pairs[k] - x = `flinged.x` - y = `flinged.y` if k == 'amenity': - if v in ['bench', 'bicycle_parking', 'waste_basket', + if v in ['bench', 'bicycle_parking', 'cafe', 'waste_basket', 'restaurant', 'pharmacy', 'drinking_water', 'toilets', 'fast_food', 'theatre']: draw_point_shape(v, x, y, fill) - elif v == 'clinic': - draw_point(d_clinic, x, y, fill) - elif v == 'fountain': - draw_point_shape('fountain', x, y, water_border_color) - elif v == 'recycling': - if not 'recycling_type' in pairs: - draw_point_shape('recycling', x, y, fill) - else: - point(k, v, flinged.x, flinged.y, fill) elif k == 'artwork_type': if v == 'statue': draw_point_shape('monument', x, y, fill) if v == 'sculpture': draw_point_shape('monument', x, y, fill) else: - point(k, v, flinged.x, flinged.y, fill) + point(k, v, flinged.x, flinged.y, fill, text_y) + text_y += 10 elif k == 'barrier': if v == 'lift_gate': draw_point_shape('liftgate', x, y, fill) elif k in ['crossing', 'crossing_ref']: if v == 'zebra': - draw_point(d_crossing, x, y, fill) + draw_point_shape('crossing', x, y, fill) elif k == 'entrance': draw_point_shape('entrance', x, y, fill) elif k == 'highway': if v == 'street_lamp': - draw_point(d_street_lamp, x, y, fill) + draw_point_shape('street_lamp', x, y, fill) elif v == 'bus_stop': draw_point_shape('bus_stop', x, y, fill) elif v == 'traffic_signals': - draw_point(d_traffic_signal, x, y, fill) + draw_point_shape('traffic_signal', x, y, fill) elif v == 'crossing': if not ('crossing' in pairs): - draw_point(d_crossing, x, y, fill) + draw_point_shape('crossing', x, y, fill) else: - point(k, v, flinged.x, flinged.y, fill) + point(k, v, flinged.x, flinged.y, fill, text_y) + text_y += 10 elif k == 'man_made': if v == 'pole': draw_point_shape('pole', x, y, fill) elif v == 'flagpole': draw_point_shape('flagpole', x, y, fill) else: - point(k, v, flinged.x, flinged.y, fill) - elif k == 'natural': - if v == 'tree': - if 'denotation' in pairs and pairs['denotation'] == 'urban': - draw_point_shape('urban_tree', x, y, wood_color) - else: - draw_point(d_tree, x, y, wood_color) - elif v == 'cave_entrance': - draw_point_shape('cave', x, y, fill) - elif v == 'bush': - draw_point_shape('bush', x, y, wood_color) - else: - point(k, v, flinged.x, flinged.y, fill) + point(k, v, flinged.x, flinged.y, fill, text_y) + text_y += 10 elif k == 'recycling_type': if v == 'centre': draw_point_shape('recycling', x, y, fill) @@ -603,12 +610,14 @@ def draw_nodes(): elif v in ['clothes', 'shoes', 'gift']: draw_point_shape('shop_' + v, x, y, fill) else: - point(k, v, flinged.x, flinged.y, fill) + point(k, v, flinged.x, flinged.y, fill, text_y) + text_y += 10 elif k == 'traffic_calming': if v == 'bump': draw_point_shape('bump', x, y, fill) else: - point(k, v, flinged.x, flinged.y, fill) + point(k, v, flinged.x, flinged.y, fill, text_y) + text_y += 10 elif k == 'emergency': if v == 'fire_hydrant': draw_point_shape(v, x, y, fill) @@ -621,12 +630,7 @@ def draw_nodes(): draw_point_shape('monument', x, y, fill) if v == 'attraction': draw_point_shape('monument', x, y, fill) - elif k in [ - 'operator', 'contact:facebook', 'contact:phone', - 'opening_hours', 'cuisine', 'network', 'website', - 'contact:website', 'phone', 'branch', 'route_ref', - 'addr:flats', 'brand', 'ref', 'addr:street', 'wikipedia', - ] or ('name' in k): + elif k in undraw or ('name' in k): if k == 'route_ref': v = v.replace(';', ' ') draw_text(v, x, str(flinged.y + 18 + text_y), fill) @@ -634,13 +638,20 @@ def draw_nodes(): elif k in ['layer', 'level', 'source', 'note', 'description']: pass # NOTHING TO DRAW else: - point(k, v, flinged.x, flinged.y, fill) + point(k, v, flinged.x, flinged.y, fill, text_y) + text_y += 10 print 'Done.' #draw_raw_nodes() #draw_raw_ways() +text_y = 0 + +icons = extract_icon.IconExtractor('icons.svg') + +#sys.exit(0) + draw_ways() draw_nodes() @@ -655,7 +666,7 @@ top_missed_tags = reversed(sorted(missed_tags.keys(), key=lambda x: -missed_tags for tag in top_missed_tags: print tag + ' (' + str(missed_tags[tag]) + ')' -sys.exit(1) +sys.exit(0) top_authors = sorted(authors.keys(), key=lambda x: -authors[x]) for author in top_authors: diff --git a/osm_reader.py b/osm_reader.py index 597ecfa..e550e51 100644 --- a/osm_reader.py +++ b/osm_reader.py @@ -4,39 +4,81 @@ Reading OpenStreetMap data from XML file. Author: Sergey Vartanov """ +import datetime -def parse_node_full(text): + +def parse_node_full(node_data, silent=False): """ Parse full node parameters using regular expressions: id, visible, version, etc. For faster parsing use parse_node(). """ - m = re.match('id="(.*)" visible="(.*)" version="(.*)" changeset="(.*)" timestamp="(.*)" user="(.*)" uid="(.*)" lat="(.*)" lon="(.*)"', text) + m = re.match('id="(.*)" visible="(.*)" version="(.*)" changeset="(.*)" ' + \ + 'timestamp="(.*)" user="(.*)" uid="(.*)" ' + \ + 'lat="(.*)" lon="(.*)"', node_data) if m: - return {'id': m.group(1), 'visible': m.group(2), 'changeset': m.group(3), 'timestamp': m.group(4), 'user': m.group(5), 'uid': m.group(6), 'lat': m.group(7), 'lon': m.group(8)} + return {'id': int(m.group(1)), 'visible': m.group(2), + 'changeset': m.group(3), 'timestamp': m.group(4), + 'user': m.group(5), 'uid': m.group(6), + 'lat': float(m.group(7)), 'lon': float(m.group(8))} else: - print 'Error node' + if not silent: + print 'Error: cannot parse node data: ' + node_data + '.' + return None def parse_node(text): """ - Parse just identifier, latitude, and longitude from node parameters. + Just parse node identifier, latitude, and longitude. """ id = text[4:text.find('"', 6)] lat_index = text.find('lat') lon_index = text.find('lon') lat = text[lat_index + 5:text.find('"', lat_index + 7)] lon = text[lon_index + 5:text.find('"', lon_index + 7)] - return {'id': id, 'lat': lat, 'lon': lon} + return {'id': int(id), 'lat': float(lat), 'lon': float(lon)} -def parse_way_full(text): - m = re.match('id="(.*)" visible="(.*)" version="(.*)" changeset="(.*)" timestamp="(.*)" user="(.*)" uid="(.*)"', text) +def parse_way_full(way_data, silent=False): + """ + Parse full way parameters using regular expressions: id, visible, version, + etc. For faster parsing use parse_way(). + """ + m = re.match('id="(.*)" visible="(.*)" version="(.*)" changeset="(.*)" ' + \ + 'timestamp="(.*)" user="(.*)" uid="(.*)"', way_data) if m: - return {'id': m.group(1), 'visible': m.group(2), 'changeset': m.group(3), 'timestamp': m.group(4), 'user': m.group(5), 'uid': m.group(6)} + return {'id': m.group(1), 'visible': m.group(2), + 'changeset': m.group(3), 'timestamp': m.group(4), + 'user': m.group(5), 'uid': m.group(6)} else: - print 'Error way' + if not silent: + print 'Error: cannot parse way data: ' + way_data + '.' + return None def parse_way(text): + """ + Just parse way identifier. + """ id = text[4:text.find('"', 6)] - return {'id': id} + return {'id': int(id)} + +def parse_relation(text): + """ + Just parse relation identifier. + """ + id = text[4:text.find('"', 6)] + return {'id': int(id)} + +def parse_member(text): + """ + Parse member type, reference, and role. + """ + if text[6] == 'w': + type = 'way' + else: + type = 'node' + ref_index = text.find('ref') + role_index = text.find('role') + ref = text[ref_index + 5:text.find('"', ref_index + 7)] + role = text[role_index + 6:text.find('"', role_index + 8)] + return {'type': type, 'ref': int(ref), 'role': role} def parse_tag(text): v_index = text.find('v="') @@ -44,52 +86,70 @@ def parse_tag(text): v = text[v_index + 3:text.find('"', v_index + 4)] return k, v -def get_max_min(nodes): - maximum = Geo(nodes[0]['lon'], nodes[0]['lat']) - minimum = Geo(nodes[0]['lon'], nodes[0]['lat']) - for node in nodes: - if node['lat'] > maximum.lat: maximum.lat = node['lat'] - if node['lat'] < minimum.lat: minimum.lat = node['lat'] - if node['lon'] > maximum.lon: maximum.lon = node['lon'] - if node['lon'] < minimum.lon: minimum.lon = node['lon'] - return minimum, maximum - def parse_osm_file(file_name, silent=False): if not silent: print 'Reading OSM file ' + file_name - start_time = datetime.datetime.now() - node_map = {} - way_map = {} - input_file = open(file_name) - line = input_file.readline() - while line != '': - if line[:6] == ' ': - node_map[node['id']] = element - elif line[:5] == ' \n': + node_map[element['id']] = element + + # Way parsing. + + elif line[:5] == ' \n': + way_map[element['id']] = element + + # Relation parsing. + + elif line[:10] == ' \n': + relation_map[element['id']] = element + + # Elements parsing. + + elif line[:6] == '