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

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

Some work on loading images from the network (still not usable as a
routing GUI for general use)

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