source: subversion/applications/routing/pyroutelib2/loadOsm.py @ 34645

Last change on this file since 34645 was 8114, checked in by ojw, 11 years ago

move weighting to a module

File size: 6.4 KB
Line 
1#!/usr/bin/python
2#----------------------------------------------------------------
3# load OSM data file into memory
4#
5#------------------------------------------------------
6# Copyright 2007, Oliver White
7#
8# This program is free software: you can redistribute it and/or modify
9# it under the terms of the GNU General Public License as published by
10# the Free Software Foundation, either version 3 of the License, or
11# (at your option) any later version.
12#
13# This program is distributed in the hope that it will be useful,
14# but WITHOUT ANY WARRANTY; without even the implied warranty of
15# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16# GNU General Public License for more details.
17#
18# You should have received a copy of the GNU General Public License
19# along with this program.  If not, see <http://www.gnu.org/licenses/>.
20#------------------------------------------------------
21# Changelog:
22#  2007-11-04  OJW  Modified from pyroute.py
23#  2007-11-05  OJW  Multiple forms of transport
24#------------------------------------------------------
25import sys
26import os
27import tiledata
28import tilenames
29import re
30import weights
31
32class LoadOsm:
33  """Parse an OSM file looking for routing information, and do routing with it"""
34  def __init__(self, transport):
35    """Initialise an OSM-file parser"""
36    self.routing = {}
37    self.rnodes = {}
38    self.transport = transport
39    self.tiles = {}
40    self.weights = weights.RoutingWeights()
41 
42  def getArea(self, lat, lon):
43    """Download data in the vicinity of a lat/long"""
44   
45    z = tiledata.DownloadLevel()
46    (x,y) = tilenames.tileXY(lat, lon, z)
47
48    tileID = '%d,%d'%(x,y)
49    if(self.tiles.get(tileID,False)):
50      #print "Already got %s" % tileID
51      return
52    self.tiles[tileID] = True
53   
54    filename = tiledata.GetOsmTileData(z,x,y)
55    #print "Loading %d,%d at z%d from %s" % (x,y,z,filename)
56    return(self.loadOsm(filename))
57
58  def loadOsm(self, filename):
59    if(not os.path.exists(filename)):
60      print "No such data file %s" % filename
61      return(False)
62    fp = open(filename, "r")
63    re_way = re.compile("<way id='(\d+)'>\s*$")
64    re_nd = re.compile("\s+<nd id='(\d+)' x='(\d+)' y='(\d+)' />\s*$")
65    re_tag = re.compile("\s+<tag k='(.*)' v='(.*)' />\s*$")
66    re_endway = re.compile("</way>$")
67    in_way = 0
68
69    way_tags = {}
70    way_nodes = []
71
72    for line in fp:
73      result_way = re_way.match(line)
74      result_endway = re_endway.match(line)
75      if(result_way):
76        in_way = True
77        way_tags = {}
78        way_nodes = []
79        way_id = int(result_way.group(1))
80      elif(result_endway):
81        in_way = False
82        self.storeWay(way_id, way_tags, way_nodes)
83      elif(in_way):
84        result_nd = re_nd.match(line)
85        if(result_nd):
86          node_id = int(result_nd.group(1))
87          x = float(result_nd.group(2))
88          y = float(result_nd.group(3))
89
90          (lat,lon) = tilenames.xy2latlon(x,y,31)
91         
92          way_nodes.append([node_id,lat,lon])
93        else:
94          result_tag = re_tag.match(line)
95          if(result_tag):
96            way_tags[result_tag.group(1)] = result_tag.group(2)
97    return(True)
98 
99  def storeWay(self, wayID, tags, nodes):
100    highway = self.equivalent(tags.get('highway', ''))
101    railway = self.equivalent(tags.get('railway', ''))
102    oneway = tags.get('oneway', '')
103    reversible = not oneway in('yes','true','1')
104
105    # Calculate what vehicles can use this route
106    # TODO: just use getWeight != 0
107    access = {}
108    access['cycle'] = highway in ('primary','secondary','tertiary','unclassified','minor','cycleway','residential', 'track','service')
109    access['car'] = highway in ('motorway','trunk','primary','secondary','tertiary','unclassified','minor','residential', 'service')
110    access['train'] = railway in('rail','light_rail','subway')
111    access['foot'] = access['cycle'] or highway in('footway','steps')
112    access['horse'] = highway in ('track','unclassified','bridleway')
113
114    # Store routing information
115    last = [None,None,None]
116
117    if(wayID == 41 and 0):
118      print nodes
119      sys.exit()
120    for node in nodes:
121      (node_id,x,y) = node
122      if last[0]:
123        if(access[self.transport]):
124          weight = self.weights.get(self.transport, highway)
125          self.addLink(last[0], node_id, weight)
126          self.makeNodeRouteable(last)
127          if reversible or self.transport == 'foot':
128            self.addLink(node_id, last[0], weight)
129            self.makeNodeRouteable(node)
130      last = node
131
132  def makeNodeRouteable(self,node):
133    self.rnodes[node[0]] = [node[1],node[2]]
134   
135  def addLink(self,fr,to, weight=1):
136    """Add a routeable edge to the scenario"""
137    try:
138      if to in self.routing[fr].keys():
139        return
140      self.routing[fr][to] = weight
141    except KeyError:
142      self.routing[fr] = {to: weight}
143
144  def equivalent(self,tag):
145    """Simplifies a bunch of tags to nearly-equivalent ones"""
146    equivalent = { \
147      "primary_link":"primary",
148      "trunk":"primary",
149      "trunk_link":"primary",
150      "secondary_link":"secondary",
151      "tertiary":"secondary",
152      "tertiary_link":"secondary",
153      "residential":"unclassified",
154      "minor":"unclassified",
155      "steps":"footway",
156      "driveway":"service",
157      "pedestrian":"footway",
158      "bridleway":"cycleway",
159      "track":"cycleway",
160      "arcade":"footway",
161      "canal":"river",
162      "riverbank":"river",
163      "lake":"river",
164      "light_rail":"railway"
165      }
166    try:
167      return(equivalent[tag])
168    except KeyError:
169      return(tag)
170   
171  def findNode(self,lat,lon):
172    """Find the nearest node that can be the start of a route"""
173    self.getArea(lat,lon)
174    maxDist = 1E+20
175    nodeFound = None
176    posFound = None
177    for (node_id,pos) in self.rnodes.items():
178      dy = pos[0] - lat
179      dx = pos[1] - lon
180      dist = dx * dx + dy * dy
181      if(dist < maxDist):
182        maxDist = dist
183        nodeFound = node_id
184        posFound = pos
185    #print "found at %s"%str(posFound)
186    return(nodeFound)
187     
188  def report(self):
189    """Display some info about the loaded data"""
190    print "Loaded %d nodes" % len(self.rnodes.keys())
191    print "Loaded %d %s routes" % (len(self.routing.keys()), self.transport)
192
193# Parse the supplied OSM file
194if __name__ == "__main__":
195  data = LoadOsm("cycle")
196  if(not data.getArea(52.55291,-1.81824)):
197    print "Failed to get data"
198  data.getArea(52.55291,-1.81824)
199  data.report()
200
201  print "Searching for node: found " + str(data.findNode(52.55291,-1.81824))
Note: See TracBrowser for help on using the repository browser.