forked from vvoovv/blosm
-
Notifications
You must be signed in to change notification settings - Fork 0
/
osm_parser.py
131 lines (119 loc) · 3.67 KB
/
osm_parser.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
import xml.etree.cElementTree as etree
import inspect, importlib
def prepareHandlers(kwArgs):
nodeHandlers = []
wayHandlers = []
# getting a dictionary with local variables
_locals = locals()
for handlers in ("nodeHandlers", "wayHandlers"):
if handlers in kwArgs:
for handler in kwArgs[handlers]:
if isinstance(handler, str):
# we've got a module name
handler = importlib.import_module(handler)
if inspect.ismodule(handler):
# iterate through all module functions
for f in inspect.getmembers(handler, inspect.isclass):
_locals[handlers].append(f[1])
elif inspect.isclass(handler):
_locals[handlers].append(handler)
if len(_locals[handlers])==0: _locals[handlers] = None
return (nodeHandlers if len(nodeHandlers) else None, wayHandlers if len(wayHandlers) else None)
class OsmParser:
def __init__(self, filename, **kwargs):
self.nodes = {}
self.ways = {}
self.relations = {}
self.minLat = 90
self.maxLat = -90
self.minLon = 180
self.maxLon = -180
(self.nodeHandlers, self.wayHandlers) = prepareHandlers(kwargs)
self.doc = etree.parse(filename)
self.osm = self.doc.getroot()
self.prepare()
def prepare(self):
for e in self.osm: # e stands for element
attrs = e.attrib
if e.tag != "node" and e.tag != "way": continue
if "action" in attrs and attrs["action"] == "delete": continue
_id = attrs["id"]
if e.tag == "node":
tags = None
for c in e:
if c.tag == "tag":
if not tags: tags = {}
tags[c.get("k")] = c.get("v")
lat = float(attrs["lat"])
lon = float(attrs["lon"])
# calculating minLat, maxLat, minLon, maxLon
# commented out: only imported objects take part in the extent calculation
#if lat<self.minLat: self.minLat = lat
#elif lat>self.maxLat: self.maxLat = lat
#if lon<self.minLon: self.minLon = lon
#elif lon>self.maxLon: self.maxLon = lon
# creating entry
entry = dict(
id=_id,
e=e,
lat=lat,
lon=lon
)
if tags: entry["tags"] = tags
self.nodes[_id] = entry
elif e.tag == "way":
nodes = []
tags = None
for c in e:
if c.tag == "nd":
nodes.append(c.get("ref"))
elif c.tag == "tag":
if not tags: tags = {}
tags[c.get("k")] = c.get("v")
# ignore ways without tags
if tags:
self.ways[_id] = dict(
id=_id,
e=e,
nodes=nodes,
tags=tags
)
self.calculateExtent()
def iterate(self, wayFunction, nodeFunction):
nodeHandlers = self.nodeHandlers
wayHandlers = self.wayHandlers
if wayHandlers:
for _id in self.ways:
way = self.ways[_id]
if "tags" in way:
for handler in wayHandlers:
if handler.condition(way["tags"], way):
wayFunction(way, handler)
continue
if nodeHandlers:
for _id in self.nodes:
node = self.nodes[_id]
if "tags" in node:
for handler in nodeHandlers:
if handler.condition(node["tags"], node):
nodeFunction(node, handler)
continue
def parse(self, **kwargs):
def wayFunction(way, handler):
handler.handler(way, self, kwargs)
def nodeFunction(node, handler):
handler.handler(node, self, kwargs)
self.iterate(wayFunction, nodeFunction)
def calculateExtent(self):
def wayFunction(way, handler):
wayNodes = way["nodes"]
for node in range(len(wayNodes)-1): # skip the last node which is the same as the first ones
nodeFunction(self.nodes[wayNodes[node]])
def nodeFunction(node, handler=None):
lon = node["lon"]
lat = node["lat"]
if lat<self.minLat: self.minLat = lat
elif lat>self.maxLat: self.maxLat = lat
if lon<self.minLon: self.minLon = lon
elif lon>self.maxLon: self.maxLon = lon
self.iterate(wayFunction, nodeFunction)