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

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

some tests with continually updating position (unstable version)

  • Property svn:executable set to *
File size: 8.8 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(700, 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.01)
85    self.forceRedraw()
86    newnode = self.data.findNode(self.ownpos[0],self.ownpos[1])
87    if(newnode != self.routeStart):
88      self.routeStart = newnode
89      self.updateRoute()
90   
91  def updateRoute(self):
92    if(self.routeStart== 0 or self.routeEnd == 0):
93      self.route = []
94      return
95    print "Routing from %d to %d using %s" % (self.routeStart, self.routeEnd, self.transport)
96    result, self.route = self.router.doRoute(self.routeStart, self.routeEnd, self.transport)
97    if result == 'success':
98      print "Routed OK"
99      self.forceRedraw()
100    else:
101      print "No route"
102   
103  def click(self, x, y):
104    lat, lon = self.projection.xy2ll(x,y)
105    self.routeEnd = self.data.findNode(lat,lon)
106    self.updateRoute()
107     
108  def forceRedraw(self):
109    try:
110      self.window.invalidate_rect((0,0,self.rect.width,self.rect.height),False)
111    except AttributeError:
112      pass
113  def move(self,dx,dy):
114    self.projection.nudge(-dx,dy,1.0/self.rect.width)
115    self.forceRedraw()
116  def nodeXY(self,node):
117    node = self.data.nodes[node]
118    return(self.projection.ll2xy(node[0], node[1]))
119  def setupDrawStyle(self, cr, wayType):
120    styles = { \
121      "primary": {'rgb':(0.5, 0.0, 0.0),'w':3},
122      "secondary": {'rgb':(0.5, 0.25, 0.0),'w':2},
123      "unclassified": {'rgb':(0.8, 0.5, 0.5)},
124      "service": {'rgb':(0.5, 0.5, 0.5),'w':0.5},
125      "footway": {'rgb':(0.0, 0.5, 0.5)},
126      "cycleway": {'rgb':(0.0, 0.5, 0.8)},
127      "river": {'rgb':(0.5, 0.5, 1.0),'w':2},
128      "rail": {'rgb':(0.3, 0.3, 0.3)}
129      }
130    try:
131      style = styles[wayType]
132    except KeyError:
133      if not wayType in self.unknownTags:
134        print "Unknown %s" % wayType
135        self.unknownTags.append(wayType)
136      return(False)
137    rgb = style['rgb']
138    cr.set_source_rgb(rgb[0], rgb[1], rgb[2])
139    try:
140      width = style['w']
141    except KeyError:
142      width = 1
143    cr.set_line_width(width)
144    return(True)
145 
146  def imageName(self,x,y,z):
147    return("%d_%d_%d" % (z,x,y))
148  def loadImage(self,x,y,z):
149    name = self.imageName(x,y,z)
150    if name in self.images.keys():
151      return
152    filename = "cache/%s.png" % name
153    if not os.path.exists(filename):
154      url = tileURL(x,y,z)
155      urllib.urlretrieve(url, filename)
156    self.images[name]  = cairo.ImageSurface.create_from_png(filename)
157   
158  def drawImage(self,cr, tile, bbox):
159    name = self.imageName(tile[0],tile[1],tile[2])
160    if not name in self.images.keys():
161      return
162    cr.save()
163    cr.translate(bbox[0],bbox[1])
164    cr.scale((bbox[2] - bbox[0]) / 256.0, (bbox[3] - bbox[1]) / 256.0)
165    cr.set_source_surface(self.images[name],0,0)
166    cr.paint()
167    cr.restore()
168   
169  def draw(self, cr):
170    # Map as image
171    z = 14
172    view_x1,view_y1 = latlon2xy(self.projection.N,self.projection.W,z)
173    view_x2,view_y2 = latlon2xy(self.projection.S,self.projection.E,z)
174    #print "X: %1.3f - %1.3f. y: %1.3f - %1.3f" % (view_x1,view_x2,view_y1,view_y2)
175    for x in range(int(floor(view_x1)), int(ceil(view_x2))):
176      for y in range(int(floor(view_y1)), int(ceil(view_y2))):
177        S,W,N,E = tileEdges(x,y,z) 
178        x1,y1 = self.projection.ll2xy(N,W)
179        x2,y2 = self.projection.ll2xy(S,E)
180        self.loadImage(x,y,z)
181        self.drawImage(cr,(x,y,z),(x1,y1,x2,y2))
182   
183    # The map
184    if 0:
185      for way in self.data.ways:
186        if(self.setupDrawStyle(cr, way['t'])):
187          firstTime = True
188          for node in way['n']:
189            x,y = self.nodeXY(node)
190            if firstTime:
191              cr.move_to(x,y)
192              firstTime = False
193            else:
194              cr.line_to(x,y)
195          cr.stroke()
196   
197    # The route
198    if(len(self.route) > 1):
199      cr.set_source_rgba(0.5, 0.0, 0.0, 0.5)
200      cr.set_line_width(12)
201      x,y = self.nodeXY(self.route[0])
202      cr.move_to(x,y)
203      for i in self.route:
204        x,y = self.nodeXY(i)
205        cr.line_to(x,y)
206      cr.stroke()
207 
208    # Us
209    x,y = self.projection.ll2xy(self.ownpos[0],self.ownpos[1])
210    cr.set_source_rgb(0.0, 0.0, 0.0)
211    cr.arc(x,y,14, 0,2*3.1415)
212    cr.fill()
213    cr.set_source_rgb(1.0, 0.0, 0.0)
214    cr.arc(x,y,10, 0,2*3.1415)
215    cr.fill()
216   
217  def do_realize(self):
218    self.set_flags(self.flags() | gtk.REALIZED)
219    self.window = gdk.Window( \
220      self.get_parent_window(),
221      width = self.allocation.width,
222      height = self.allocation.height,
223      window_type = gdk.WINDOW_CHILD,
224      wclass = gdk.INPUT_OUTPUT,
225      event_mask = self.get_events() | gdk.EXPOSURE_MASK)
226    self.window.set_user_data(self)
227    self.style.attach(self.window)
228    self.style.set_background(self.window, gtk.STATE_NORMAL)
229    self.window.move_resize(*self.allocation)
230  def do_size_request(self, allocation):
231    pass
232  def do_size_allocate(self, allocation):
233    self.allocation = allocation
234    if self.flags() & gtk.REALIZED:
235      self.window.move_resize(*allocation)
236  def _expose_cairo(self, event, cr):
237    self.rect = self.allocation
238    self.projection.setView( \
239      self.rect.x, 
240      self.rect.y, 
241      self.rect.width, 
242      self.rect.height)
243    self.draw(cr)
244  def do_expose_event(self, event):
245    self.chain(event)
246    cr = self.window.cairo_create()
247    return self._expose_cairo(event, cr)
248
249class GuiBase:
250  """Wrapper class for a GUI interface"""
251  def __init__(self, osmDataFile, positionFile):
252    # Create the window
253    win = gtk.Window()
254    win.set_title('map')
255    win.connect('delete-event', gtk.main_quit)
256    win.resize(600,800)
257    win.move(50, gtk.gdk.screen_height() - 850)
258   
259    # Events
260    event_box = gtk.EventBox()
261    event_box.connect("button_press_event", lambda w,e: self.pressed(e))
262    event_box.connect("button_release_event", lambda w,e: self.released(e))
263    event_box.connect("motion_notify_event", lambda w,e: self.moved(e))
264    win.add(event_box)
265   
266    # Create the map
267    self.mapWidget = MapWidget(osmDataFile, positionFile)
268    event_box.add(self.mapWidget)
269   
270    # Finalise the window
271    win.show_all()
272    gtk.main()
273   
274  def pressed(self, event):
275    self.dragstartx = event.x
276    self.dragstarty = event.y
277    self.dragx = event.x
278    self.dragy = event.y
279  def moved(self, event):
280    """Drag-handler"""
281    self.mapWidget.move(event.x - self.dragx, event.y - self.dragy)
282    self.dragx = event.x
283    self.dragy = event.y
284  def released(self, event):
285    dx = event.x - self.dragstartx
286    dy = event.y - self.dragstarty
287    distSq = dx * dx + dy * dy
288    if distSq < 4:
289      self.mapWidget.click(event.x, event.y)
290
291
292if __name__ == "__main__":
293  program = GuiBase(sys.argv[1], sys.argv[2])
Note: See TracBrowser for help on using the repository browser.