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

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

Changes from the weekend

File size: 7.1 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
33
34execfile("weights.py")
35
36class LoadOsm(handler.ContentHandler):
37  """Parse an OSM file looking for routing information, and do routing with it"""
38  def __init__(self, filename, storeMap = 0, usecache=0):
39    """Initialise an OSM-file parser"""
40    self.routing = {}
41    self.routeTypes = ('cycle','car','train','foot','horse')
42    self.routeableNodes = {}
43    for routeType in self.routeTypes:
44      self.routing[routeType] = {}
45      self.routeableNodes[routeType] = {}
46    self.nodes = {}
47    self.ways = []
48    self.storeMap = storeMap
49   
50    cachefile = filename + "_cached"
51    if(usecache and os.path.exists(cachefile)):
52      print "Loading cached copy from %s" % cachefile
53      self.load(cachefile)
54    else:
55      parser = make_parser()
56      parser.setContentHandler(self)
57      parser.parse(filename)
58      if(usecache):
59        self.save(cachefile)
60     
61  def report(self):
62    """Display some info about the loaded data"""
63    report = "Loaded %d nodes,\n" % len(self.nodes.keys())
64    report = report + "%d ways, and...\n" % len(self.ways)
65    for routeType in self.routeTypes:
66      report = report + " %d %s routes\n" % ( \
67        len(self.routing[routeType].keys()),
68        routeType)
69    return(report)
70
71  def startElement(self, name, attrs):
72    """Handle XML elements"""
73    if name in('node','way','relation'):
74      self.tags = {}
75      self.waynodes = []
76      if name == 'node':
77        """Nodes need to be stored"""
78        id = int(attrs.get('id'))
79        lat = float(attrs.get('lat'))
80        lon = float(attrs.get('lon'))
81        self.nodes[id] = (lat,lon)
82    elif name == 'nd':
83      """Nodes within a way -- add them to a list"""
84      self.waynodes.append(int(attrs.get('ref')))
85    elif name == 'tag':
86      """Tags - store them in a hash"""
87      k,v = (attrs.get('k'), attrs.get('v'))
88      if not k in ('created_by'):
89        self.tags[k] = v
90 
91  def endElement(self, name):
92    """Handle ways in the OSM data"""
93    if name == 'way':
94      highway = self.equivalent(self.tags.get('highway', ''))
95      railway = self.equivalent(self.tags.get('railway', ''))
96      oneway = self.tags.get('oneway', '')
97      reversible = not oneway in('yes','true','1')
98   
99      # Calculate what vehicles can use this route
100      access = {}
101      access['cycle'] = highway in ('primary','secondary','tertiary','unclassified','minor','cycleway','residential', 'track','service')
102      access['car'] = highway in ('motorway','trunk','primary','secondary','tertiary','unclassified','minor','residential', 'service')
103      access['train'] = railway in('rail','light_rail','subway')
104      access['foot'] = access['cycle'] or highway in('footway','steps')
105      access['horse'] = highway in ('track','unclassified','bridleway')
106     
107      # Store routing information
108      last = -1
109      for i in self.waynodes:
110        if last != -1:
111          #print "%d -> %d & v.v." % (last, i)
112          for routeType in self.routeTypes:
113            if(access[routeType]):
114              weight = getWeight(routeType, highway)
115              self.addLink(last, i, routeType, weight)
116              if reversible or routeType == 'foot':
117                self.addLink(i, last, routeType, weight)
118        last = i
119     
120      # Store map information
121      if(self.storeMap):
122        wayType = self.WayType(self.tags)
123        if(wayType):
124          self.ways.append({ \
125            't':wayType,
126            'n':self.waynodes})
127 
128  def addLink(self,fr,to, routeType, weight=1):
129    """Add a routeable edge to the scenario"""
130    self.routeablefrom(fr,routeType)
131    try:
132      if to in self.routing[routeType][fr].keys():
133        return
134      self.routing[routeType][fr][to] = weight
135    except KeyError:
136      self.routing[routeType][fr] = {to: weight}
137
138  def WayType(self, tags):
139    # Look for a variety of tags (priority order - first one found is used)
140    for key in ('highway','railway','waterway','natural'):
141      value = tags.get('highway', '')
142      if value:
143        return(self.equivalent(value))
144    return('')
145  def equivalent(self,tag):
146    """Simplifies a bunch of tags to nearly-equivalent ones"""
147    equivalent = { \
148      "primary_link":"primary",
149      "trunk":"primary",
150      "trunk_link":"primary",
151      "secondary_link":"secondary",
152      "tertiary":"secondary",
153      "tertiary_link":"secondary",
154      "residential":"unclassified",
155      "minor":"unclassified",
156      "steps":"footway",
157      "driveway":"service",
158      "pedestrian":"footway",
159      "bridleway":"cycleway",
160      "track":"cycleway",
161      "arcade":"footway",
162      "canal":"river",
163      "riverbank":"river",
164      "lake":"river",
165      "light_rail":"railway"
166      }
167    try:
168      return(equivalent[tag])
169    except KeyError:
170      return(tag)
171   
172  def routeablefrom(self,fr,routeType):
173    self.routeableNodes[routeType][fr] = 1
174 
175  def save(self, filename):
176    """Saves routing data (to cache)"""
177    toStore = { \
178      'routing': self.routing,
179      'routeNodes':self.routeableNodes,
180      'nodes':self.nodes,
181      'ways':self.ways}
182    file = open(filename, "w")
183    file.write(str(toStore))
184    file.close()
185
186  def load(self, filename):
187    """Loads routing data (from cache)"""
188    file = open(filename, "r")
189    struct = eval(file.read())
190    self.routing = struct['routing']
191    self.routeableNodes = struct['routeNodes']
192    self.nodes = struct['nodes']
193    self.ways = struct['ways']
194    file.close()
195    print "done"
196   
197  def findNode(self,lat,lon,routeType):
198    """Find the nearest node to a point.
199    Filters for nodes which have a route leading from them"""
200    maxDist = 1000
201    nodeFound = None
202    for id in self.routeableNodes[routeType].keys():
203      n = self.nodes[id]
204      dlat = n[0] - lat
205      dlon = n[1] - lon
206      dist = dlat * dlat + dlon * dlon
207      if(dist < maxDist):
208        maxDist = dist
209        nodeFound = id
210    return(nodeFound)
211   
212# Parse the supplied OSM file
213if __name__ == "__main__":
214  print "Loading data..."
215  data = LoadOsm(sys.argv[1], True)
216  data.report()
Note: See TracBrowser for help on using the repository browser.