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

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

centre on start node
+ update on drag

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