source: subversion/applications/routing/pyroute/gui.py @ 5362

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

only update start point to be current position when the end position is
chosen, then leave it while we travel

  • Property svn:executable set to *
File size: 8.7 KB
Line 
1#!/usr/bin/env python
2import pygtk
3pygtk.require('2.0')
4import gobject
5import gtk
6import sys
7from gtk import gdk
8from loadOsm import *
9from route import *
10from tilenames import *
11from geoPosition import *
12from math import sqrt
13import cairo
14import urllib
15import os
16
17def update(mapWidget):
18  mapWidget.updatePosition();
19  return(True)
20
21class Projection:
22  def __init__(self):
23    pass
24  def setView(self,x,y,w,h):
25    self.w = w / 2
26    self.h = h / 2
27    self.xc = x + self.w
28    self.yc = y + self.h
29  def recentre(self,lat,lon,scale):
30    self.lat = lat
31    self.lon = lon
32    self.scale = scale  # TODO: scale and scaleCosLat
33    self.findEdges()
34  def findEdges(self):
35    """(S,W,E,N) are derived from (lat,lon,scale)"""
36    self.S = self.lat - self.scale
37    self.N = self.lat + self.scale
38    self.W = self.lon - self.scale
39    self.E = self.lon + self.scale
40  def nudge(self,dx,dy,scale):
41    self.lat = self.lat + dy * scale * self.scale
42    self.lon = self.lon + dx * scale * self.scale
43    self.findEdges()
44  def ll2xy(self,lat,lon):
45    px = (lon - self.lon) / self.scale
46    py = (lat - self.lat) / self.scale
47    x = self.xc + self.w * px
48    y = self.yc - self.h * py
49    return(x,y)
50  def xy2ll(self,x,y):
51    px = (x - self.xc) / self.w
52    py = (y - self.yc) / self.h
53    lon = self.lon + px * self.scale
54    lat = self.lat - py * self.scale
55    return(lat,lon)
56   
57class MapWidget(gtk.Widget):
58  __gsignals__ = { \
59    'realize': 'override',
60    'expose-event' : 'override',
61    'size-allocate': 'override',
62    'size-request': 'override'}
63  def __init__(self, osmDataFile, positionFile):
64    gtk.Widget.__init__(self)
65    self.draw_gc = None
66    self.timer = gobject.timeout_add(1100, update, self)
67    self.transport = 'cycle'
68    self.data = LoadOsm(osmDataFile, True)
69    self.projection = Projection()
70    self.router = Router(self.data)
71    #routeStart, routeEnd = (107318,107999) # short
72    #routeStart, routeEnd = (21533912, 109833) # across top
73    #routeStart, routeEnd = (21544863, 108898) # crescent to aldwych
74    self.routeStart = 0
75    self.routeEnd = 0
76    self.route = []
77    self.position = geoPosition(positionFile)
78    self.unknownTags = []
79    self.images = {}
80    self.updatePosition()
81  def updatePosition(self):
82    self.ownpos = self.position.get()
83    #print "position is now %1.4f, %1.4f" % (self.ownpos[0],self.ownpos[1])
84    self.projection.recentre(self.ownpos[0],self.ownpos[1], 0.03)
85    self.forceRedraw()
86 
87  def updateRoute(self):
88    if(self.routeStart== 0 or self.routeEnd == 0):
89      self.route = []
90      return
91    print "Routing from %d to %d using %s" % (self.routeStart, self.routeEnd, self.transport)
92    result, self.route = self.router.doRoute(self.routeStart, self.routeEnd, self.transport)
93    if result == 'success':
94      print "Routed OK"
95      self.forceRedraw()
96    else:
97      print "No route"
98   
99  def click(self, x, y):
100    self.routeStart = self.data.findNode(self.ownpos[0],self.ownpos[1], self.transport)
101    lat, lon = self.projection.xy2ll(x,y)
102    self.routeEnd = self.data.findNode(lat,lon,self.transport)
103    self.updateRoute()
104     
105  def forceRedraw(self):
106    try:
107      self.window.invalidate_rect((0,0,self.rect.width,self.rect.height),False)
108    except AttributeError:
109      pass
110  def move(self,dx,dy):
111    self.projection.nudge(-dx,dy,1.0/self.rect.width)
112    self.forceRedraw()
113  def nodeXY(self,node):
114    node = self.data.nodes[node]
115    return(self.projection.ll2xy(node[0], node[1]))
116  def setupDrawStyle(self, cr, wayType):
117    styles = { \
118      "primary": {'rgb':(0.5, 0.0, 0.0),'w':3},
119      "secondary": {'rgb':(0.5, 0.25, 0.0),'w':2},
120      "unclassified": {'rgb':(0.8, 0.5, 0.5)},
121      "service": {'rgb':(0.5, 0.5, 0.5),'w':0.5},
122      "footway": {'rgb':(0.0, 0.5, 0.5)},
123      "cycleway": {'rgb':(0.0, 0.5, 0.8)},
124      "river": {'rgb':(0.5, 0.5, 1.0),'w':2},
125      "rail": {'rgb':(0.3, 0.3, 0.3)}
126      }
127    try:
128      style = styles[wayType]
129    except KeyError:
130      if not wayType in self.unknownTags:
131        print "Unknown %s" % wayType
132        self.unknownTags.append(wayType)
133      return(False)
134    rgb = style['rgb']
135    cr.set_source_rgb(rgb[0], rgb[1], rgb[2])
136    try:
137      width = style['w']
138    except KeyError:
139      width = 1
140    cr.set_line_width(width)
141    return(True)
142 
143  def imageName(self,x,y,z):
144    return("%d_%d_%d" % (z,x,y))
145  def loadImage(self,x,y,z):
146    name = self.imageName(x,y,z)
147    if name in self.images.keys():
148      return
149    filename = "cache/%s.png" % name
150    if not os.path.exists(filename):
151      url = tileURL(x,y,z)
152      urllib.urlretrieve(url, filename)
153    self.images[name]  = cairo.ImageSurface.create_from_png(filename)
154   
155  def drawImage(self,cr, tile, bbox):
156    name = self.imageName(tile[0],tile[1],tile[2])
157    if not name in self.images.keys():
158      return
159    cr.save()
160    cr.translate(bbox[0],bbox[1])
161    cr.scale((bbox[2] - bbox[0]) / 256.0, (bbox[3] - bbox[1]) / 256.0)
162    cr.set_source_surface(self.images[name],0,0)
163    cr.paint()
164    cr.restore()
165   
166  def draw(self, cr):
167    # Map as image
168    z = 14
169    view_x1,view_y1 = latlon2xy(self.projection.N,self.projection.W,z)
170    view_x2,view_y2 = latlon2xy(self.projection.S,self.projection.E,z)
171    #print "X: %1.3f - %1.3f. y: %1.3f - %1.3f" % (view_x1,view_x2,view_y1,view_y2)
172    for x in range(int(floor(view_x1)), int(ceil(view_x2))):
173      for y in range(int(floor(view_y1)), int(ceil(view_y2))):
174        S,W,N,E = tileEdges(x,y,z) 
175        x1,y1 = self.projection.ll2xy(N,W)
176        x2,y2 = self.projection.ll2xy(S,E)
177        self.loadImage(x,y,z)
178        self.drawImage(cr,(x,y,z),(x1,y1,x2,y2))
179   
180    # The map
181    if 0:
182      for way in self.data.ways:
183        if(self.setupDrawStyle(cr, way['t'])):
184          firstTime = True
185          for node in way['n']:
186            x,y = self.nodeXY(node)
187            if firstTime:
188              cr.move_to(x,y)
189              firstTime = False
190            else:
191              cr.line_to(x,y)
192          cr.stroke()
193   
194    # The route
195    if(len(self.route) > 1):
196      cr.set_source_rgba(0.5, 0.0, 0.0, 0.5)
197      cr.set_line_width(12)
198      x,y = self.nodeXY(self.route[0])
199      cr.move_to(x,y)
200      for i in self.route:
201        x,y = self.nodeXY(i)
202        cr.line_to(x,y)
203      cr.stroke()
204 
205    # Us
206    x,y = self.projection.ll2xy(self.ownpos[0],self.ownpos[1])
207    cr.set_source_rgb(0.0, 0.0, 0.0)
208    cr.arc(x,y,14, 0,2*3.1415)
209    cr.fill()
210    cr.set_source_rgb(1.0, 0.0, 0.0)
211    cr.arc(x,y,10, 0,2*3.1415)
212    cr.fill()
213   
214  def do_realize(self):
215    self.set_flags(self.flags() | gtk.REALIZED)
216    self.window = gdk.Window( \
217      self.get_parent_window(),
218      width = self.allocation.width,
219      height = self.allocation.height,
220      window_type = gdk.WINDOW_CHILD,
221      wclass = gdk.INPUT_OUTPUT,
222      event_mask = self.get_events() | gdk.EXPOSURE_MASK)
223    self.window.set_user_data(self)
224    self.style.attach(self.window)
225    self.style.set_background(self.window, gtk.STATE_NORMAL)
226    self.window.move_resize(*self.allocation)
227  def do_size_request(self, allocation):
228    pass
229  def do_size_allocate(self, allocation):
230    self.allocation = allocation
231    if self.flags() & gtk.REALIZED:
232      self.window.move_resize(*allocation)
233  def _expose_cairo(self, event, cr):
234    self.rect = self.allocation
235    self.projection.setView( \
236      self.rect.x, 
237      self.rect.y, 
238      self.rect.width, 
239      self.rect.height)
240    self.draw(cr)
241  def do_expose_event(self, event):
242    self.chain(event)
243    cr = self.window.cairo_create()
244    return self._expose_cairo(event, cr)
245
246class GuiBase:
247  """Wrapper class for a GUI interface"""
248  def __init__(self, osmDataFile, positionFile):
249    # Create the window
250    win = gtk.Window()
251    win.set_title('map')
252    win.connect('delete-event', gtk.main_quit)
253    win.resize(600,800)
254    win.move(50, gtk.gdk.screen_height() - 850)
255   
256    # Events
257    event_box = gtk.EventBox()
258    event_box.connect("button_press_event", lambda w,e: self.pressed(e))
259    event_box.connect("button_release_event", lambda w,e: self.released(e))
260    event_box.connect("motion_notify_event", lambda w,e: self.moved(e))
261    win.add(event_box)
262   
263    # Create the map
264    self.mapWidget = MapWidget(osmDataFile, positionFile)
265    event_box.add(self.mapWidget)
266   
267    # Finalise the window
268    win.show_all()
269    gtk.main()
270   
271  def pressed(self, event):
272    self.dragstartx = event.x
273    self.dragstarty = event.y
274    self.dragx = event.x
275    self.dragy = event.y
276  def moved(self, event):
277    """Drag-handler"""
278    self.mapWidget.move(event.x - self.dragx, event.y - self.dragy)
279    self.dragx = event.x
280    self.dragy = event.y
281  def released(self, event):
282    dx = event.x - self.dragstartx
283    dy = event.y - self.dragstarty
284    distSq = dx * dx + dy * dy
285    if distSq < 4:
286      self.mapWidget.click(event.x, event.y)
287
288
289if __name__ == "__main__":
290  program = GuiBase(sys.argv[1], sys.argv[2])
Note: See TracBrowser for help on using the repository browser.