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

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

Update bounding box (and hence, displayed images) when the map is moved

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