source: subversion/applications/routing/pyroute/loadOsm.py @ 6828

Last change on this file since 6828 was 6828, checked in by ojw, 12 years ago

tab -> space

File size: 8.7 KB
Line 
1#!/usr/bin/python
2#----------------------------------------------------------------
3# load OSM data file into memory
4#
5#------------------------------------------------------
6# Usage:
7#   data = LoadOsm(filename)
8# or:
9#   loadOsm.py filename.osm
10#------------------------------------------------------
11# Copyright 2007, Oliver White
12#
13# This program is free software: you can redistribute it and/or modify
14# it under the terms of the GNU General Public License as published by
15# the Free Software Foundation, either version 3 of the License, or
16# (at your option) any later version.
17#
18# This program is distributed in the hope that it will be useful,
19# but WITHOUT ANY WARRANTY; without even the implied warranty of
20# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21# GNU General Public License for more details.
22#
23# You should have received a copy of the GNU General Public License
24# along with this program.  If not, see <http://www.gnu.org/licenses/>.
25#------------------------------------------------------
26# Changelog:
27#  2007-11-04  OJW  Modified from pyroute.py
28#  2007-11-05  OJW  Multiple forms of transport
29#------------------------------------------------------
30import sys
31import os
32from xml.sax import make_parser, handler
33import xml
34from util_binary import *
35from struct import *
36
37execfile("weights.py")
38
39class LoadOsm(handler.ContentHandler):
40  """Parse an OSM file looking for routing information, and do routing with it"""
41  def __init__(self, filename, storeMap = 0):
42    """Initialise an OSM-file parser"""
43    self.routing = {}
44    self.routeTypes = ('cycle','car','train','foot','horse')
45    self.routeableNodes = {}
46    for routeType in self.routeTypes:
47      self.routing[routeType] = {}
48      self.routeableNodes[routeType] = {}
49    self.nodes = {}
50    self.ways = []
51    self.storeMap = storeMap
52   
53    if(filename == None):
54      return
55    self.loadOsm(filename)
56   
57  def loadOsm(self, filename):
58    if(not os.path.exists(filename)):
59      print "No such data file %s" % filename
60      return
61    try:
62      parser = make_parser()
63      parser.setContentHandler(self)
64      parser.parse(filename)
65    except xml.sax._exceptions.SAXParseException:
66      print "Error loading %s" % filename
67   
68  def report(self):
69    """Display some info about the loaded data"""
70    report = "Loaded %d nodes,\n" % len(self.nodes.keys())
71    report = report + "%d ways, and...\n" % len(self.ways)
72    for routeType in self.routeTypes:
73      report = report + " %d %s routes\n" % ( \
74        len(self.routing[routeType].keys()),
75        routeType)
76    return(report)
77   
78  def savebin(self,filename):
79    self.newIDs = {}
80   
81    f = open(filename,"wb")
82    f.write(pack('L',len(self.nodes.keys())))
83    count = 0
84    for id, n in self.nodes.items():
85      self.newIDs[id] = count
86      f.write(encodeLL(n[0],n[1]))
87      count = count + 1
88     
89    f.write(pack('B', len(self.routing.keys())))
90    errors = 0
91    for routeType, data in self.routing.items():
92      f.write(pack('B', len(routeType)))
93      f.write(routeType)
94     
95      f.write(pack('L', len(data.keys())))
96      for fr, destinations in data.items():
97        try:
98          f.write(pack('L', self.newIDs[fr]))
99        except KeyError:
100          f.write(pack('L', 0))
101          errors = errors + 1
102          continue
103
104        f.write(pack('B', len(destinations.keys())))
105        for to, weight in destinations.items():
106          try:
107            f.write(pack('Lf', self.newIDs[to], weight))
108          except KeyError:
109            f.write(pack('Lf', 0, 0))
110            errors = errors + 1
111   
112    print "%d key errors" % errors
113    f.close()
114   
115  def loadbin(self,filename):
116    f = open(filename,"rb")
117    n = unpack('L', f.read(4))[0]
118    print "%u nodes" % n
119    id = 0
120    for i in range(n):
121      lat,lon = decodeLL(f.read(8))
122      #print "%u: %f, %f" % (id,lat,lon)
123      id = id + 1
124   
125    numTypes = unpack('B', f.read(1))[0]
126    print "%u types:" % numTypes
127    for typ in range(numTypes):
128      lenName = unpack('B', f.read(1))[0]
129      name = f.read(lenName)
130      numLinks = 0
131     
132      numHubs = unpack('L', f.read(4))[0]
133      for hub in range(numHubs):
134        fr = unpack('L', f.read(4))[0]
135        numDest = unpack('B', f.read(1))[0]
136        for dest in range(numDest):
137          to,weight = unpack('Lf', f.read(8))
138          numLinks = numLinks + 1
139      print \"%s\" (%u segments)" % (name, numLinks)
140
141    f.close()
142
143  def startElement(self, name, attrs):
144    """Handle XML elements"""
145    if name in('node','way','relation'):
146      self.tags = {}
147      self.waynodes = []
148      if name == 'node':
149        """Nodes need to be stored"""
150        id = int(attrs.get('id'))
151        lat = float(attrs.get('lat'))
152        lon = float(attrs.get('lon'))
153        self.nodes[id] = (lat,lon)
154    elif name == 'nd':
155      """Nodes within a way -- add them to a list"""
156      self.waynodes.append(int(attrs.get('ref')))
157    elif name == 'tag':
158      """Tags - store them in a hash"""
159      k,v = (attrs.get('k'), attrs.get('v'))
160      if not k in ('created_by'):
161        self.tags[k] = v
162 
163  def endElement(self, name):
164    """Handle ways in the OSM data"""
165    if name == 'way':
166      highway = self.equivalent(self.tags.get('highway', ''))
167      railway = self.equivalent(self.tags.get('railway', ''))
168      oneway = self.tags.get('oneway', '')
169      reversible = not oneway in('yes','true','1')
170   
171      # Calculate what vehicles can use this route
172      access = {}
173      access['cycle'] = highway in ('primary','secondary','tertiary','unclassified','minor','cycleway','residential', 'track','service')
174      access['car'] = highway in ('motorway','trunk','primary','secondary','tertiary','unclassified','minor','residential', 'service')
175      access['train'] = railway in('rail','light_rail','subway')
176      access['foot'] = access['cycle'] or highway in('footway','steps')
177      access['horse'] = highway in ('track','unclassified','bridleway')
178     
179      # Store routing information
180      last = -1
181      for i in self.waynodes:
182        if last != -1:
183          #print "%d -> %d & v.v." % (last, i)
184          for routeType in self.routeTypes:
185            if(access[routeType]):
186              weight = getWeight(routeType, highway)
187              self.addLink(last, i, routeType, weight)
188              if reversible or routeType == 'foot':
189                self.addLink(i, last, routeType, weight)
190        last = i
191     
192      # Store map information
193      if(self.storeMap):
194        wayType = self.WayType(self.tags)
195        if(wayType):
196          self.ways.append({ \
197            't':wayType,
198            'n':self.waynodes})
199 
200  def addLink(self,fr,to, routeType, weight=1):
201    """Add a routeable edge to the scenario"""
202    self.routeablefrom(fr,routeType)
203    try:
204      if to in self.routing[routeType][fr].keys():
205        return
206      self.routing[routeType][fr][to] = weight
207    except KeyError:
208      self.routing[routeType][fr] = {to: weight}
209
210  def WayType(self, tags):
211    # Look for a variety of tags (priority order - first one found is used)
212    for key in ('highway','railway','waterway','natural'):
213      value = tags.get('highway', '')
214      if value:
215        return(self.equivalent(value))
216    return('')
217  def equivalent(self,tag):
218    """Simplifies a bunch of tags to nearly-equivalent ones"""
219    equivalent = { \
220      "primary_link":"primary",
221      "trunk":"primary",
222      "trunk_link":"primary",
223      "secondary_link":"secondary",
224      "tertiary":"secondary",
225      "tertiary_link":"secondary",
226      "residential":"unclassified",
227      "minor":"unclassified",
228      "steps":"footway",
229      "driveway":"service",
230      "pedestrian":"footway",
231      "bridleway":"cycleway",
232      "track":"cycleway",
233      "arcade":"footway",
234      "canal":"river",
235      "riverbank":"river",
236      "lake":"river",
237      "light_rail":"railway"
238      }
239    try:
240      return(equivalent[tag])
241    except KeyError:
242      return(tag)
243   
244  def routeablefrom(self,fr,routeType):
245    self.routeableNodes[routeType][fr] = 1
246
247  def findNode(self,lat,lon,routeType):
248    """Find the nearest node to a point.
249    Filters for nodes which have a route leading from them"""
250    maxDist = 1000
251    nodeFound = None
252    for id in self.routeableNodes[routeType].keys():
253      if id not in self.nodes:
254        print "Ignoring undefined node %s" % id
255        continue
256      n = self.nodes[id]
257      dlat = n[0] - lat
258      dlon = n[1] - lon
259      dist = dlat * dlat + dlon * dlon
260      if(dist < maxDist):
261        maxDist = dist
262        nodeFound = id
263    return(nodeFound)
264   
265# Parse the supplied OSM file
266if __name__ == "__main__":
267  #print "Loading data..."
268  #data = LoadOsm(sys.argv[1], True)
269  #print data.report()
270  #print "Saving binary..."
271  #data.savebin("data/routing.bin")
272  print "Loading binary..."
273  data2 = LoadOsm(None, False)
274  data2.loadbin("data/routing.bin")
275  print "Done"
Note: See TracBrowser for help on using the repository browser.