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

Last change on this file was 12252, checked in by jeansch, 11 years ago

Fixed 'tah' URL
Fixed starting from another directory
Implemented 'scroll' events to allow to change zoom from mouse-wheel
Implemented initial position

File size: 8.8 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(os.path.join(os.path.dirname(__file__), "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.