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

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

Changes from the weekend

  • Property svn:executable set to *
File size: 8.2 KB
Line 
1#!/usr/bin/env python
2import pygtk
3pygtk.require('2.0')
4import gobject
5import gtk
6import sys
7import cairo
8import urllib
9import os
10from math import sqrt
11from time import clock
12from gtk import gdk
13
14# Our modules:
15from loadOsm import *
16from routeOrDirect import *
17from tilenames import *
18from geoPosition import *
19from projection import Projection
20from overlay import *
21from dataStore import *
22from mod_geoRss import geoRss
23
24def update(mapWidget):
25  mapWidget.updatePosition();
26  return(True)
27
28class MapWidget(gtk.Widget):
29  __gsignals__ = { \
30    'realize': 'override',
31    'expose-event' : 'override',
32    'size-allocate': 'override',
33    'size-request': 'override'}
34  def __init__(self, osmDataFile, positionFile):
35    gtk.Widget.__init__(self)
36    self.draw_gc = None
37    self.timer = gobject.timeout_add(30, update, self)
38    self.images = {}
39   
40    self.modules = {'plugins':{}}
41    self.modules['plugins']['rss'] = geoRss('Setup/feeds.txt')
42    self.modules['overlay'] = guiOverlay(self.modules)
43    self.modules['position'] = geoPosition(positionFile)
44    self.modules['data'] = DataStore()
45    self.modules['data'].setState('mode','cycle')
46    self.modules['osmdata'] = LoadOsm(osmDataFile, True)
47    self.modules['projection'] = Projection()
48    self.modules['route'] = RouteOrDirect(self.modules['osmdata'])
49    self.updatePosition()
50    for name,mod in self.modules['plugins'].items():
51      mod.callbacks(self.modules)
52  def updatePosition(self):
53    self.ownpos = self.modules['position'].get()
54    if(not self.modules['projection'].isValid()):
55      print "Projection not yet valid, centering on ownpos"
56      self.centreOnOwnPos()
57      return
58    x,y = self.modules['projection'].ll2xy(self.ownpos[0], self.ownpos[1])
59    x,y = self.modules['projection'].relXY(x,y)
60    border = 0.15
61    followMode = self.modules['data'].getOption("centred")
62    outsideMap = (x < border or y < border or x > (1-border) or y > (1-border))
63    if(followMode):
64      self.centreOnOwnPos()
65    else:
66      self.forceRedraw()
67  def centreOnOwnPos(self):
68    self.modules['projection'].recentre(self.ownpos[0],self.ownpos[1], 0.01)
69    self.forceRedraw()
70   
71   
72  def click(self, x, y):
73    if(self.modules['overlay'].handleClick(x,y)):
74      return
75    transport = self.modules['data'].getState('mode')
76    self.modules['route'].setStartLL(self.ownpos[0],self.ownpos[1], transport)
77
78    lat, lon = self.modules['projection'].xy2ll(x,y)
79    self.modules['route'].setEndLL(lat,lon,transport)
80    self.modules['route'].update()
81    self.forceRedraw()
82     
83  def forceRedraw(self):
84    try:
85      self.window.invalidate_rect((0,0,self.rect.width,self.rect.height),False)
86    except AttributeError:
87      pass
88  def move(self,dx,dy):
89    self.modules['projection'].nudge(-dx,dy,1.0/self.rect.width)
90    self.forceRedraw()
91  def nodeXY(self,node):
92    node = self.modules['osmdata'].nodes[node]
93    return(self.modules['projection'].ll2xy(node[0], node[1]))
94
95  def imageName(self,x,y,z):
96    return("%d_%d_%d" % (z,x,y))
97  def loadImage(self,x,y,z):
98    name = self.imageName(x,y,z)
99    if name in self.images.keys():
100      return
101    filename = "cache/%s.png" % name
102    if not os.path.exists(filename):
103      url = tileURL(x,y,z)
104      urllib.urlretrieve(url, filename)
105    self.images[name]  = cairo.ImageSurface.create_from_png(filename)
106   
107  def drawImage(self,cr, tile, bbox):
108    name = self.imageName(tile[0],tile[1],tile[2])
109    if not name in self.images.keys():
110      return
111    cr.save()
112    cr.translate(bbox[0],bbox[1])
113    cr.scale((bbox[2] - bbox[0]) / 256.0, (bbox[3] - bbox[1]) / 256.0)
114    cr.set_source_surface(self.images[name],0,0)
115    cr.paint()
116    cr.restore()
117   
118  def draw(self, cr):
119    start = clock()
120    # Map as image
121    if(not self.modules['overlay'].fullscreen()):
122      z = 14
123      view_x1,view_y1 = latlon2xy(self.modules['projection'].N,self.modules['projection'].W,z)
124      view_x2,view_y2 = latlon2xy(self.modules['projection'].S,self.modules['projection'].E,z)
125      for x in range(int(floor(view_x1)), int(ceil(view_x2))):
126        for y in range(int(floor(view_y1)), int(ceil(view_y2))):
127          S,W,N,E = tileEdges(x,y,z) 
128          x1,y1 = self.modules['projection'].ll2xy(N,W)
129          x2,y2 = self.modules['projection'].ll2xy(S,E)
130          self.loadImage(x,y,z)
131          self.drawImage(cr,(x,y,z),(x1,y1,x2,y2))
132
133     
134      # The route
135      if(self.modules['route'].valid()):
136        cr.set_source_rgba(0.5, 0.0, 0.0, 0.5)
137        cr.set_line_width(12)
138        count = 0
139        for i in self.modules['route'].route['route']:
140          x,y = self.modules['projection'].ll2xy(i[0],i[1])
141          if(count == 0):
142            cr.move_to(x,y)
143          else:
144            cr.line_to(x,y)
145          count = count + 1
146        cr.stroke()
147
148      for name,source in self.modules['plugins'].items():
149        for group in source.groups:
150          for item in group.items:
151            x,y = self.modules['projection'].ll2xy(item.lat, item.lon)
152            if(self.modules['projection'].onscreen(x,y)):
153              #print " - %s at %s" % (item.title, item.point)
154             
155              cr.set_source_rgb(0.0, 0.4, 0.0)
156              cr.arc(x,y,5, 0,2*3.1415)
157              cr.fill()
158             
159              #cr.select_font_face('Verdana', cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_BOLD)
160              cr.set_font_size(12)
161              cr.move_to(x,y)
162              cr.show_text(item.title)
163
164   
165      # Us
166      x,y = self.modules['projection'].ll2xy(self.ownpos[0],self.ownpos[1])
167      cr.set_source_rgb(0.0, 0.0, 0.0)
168      cr.arc(x,y,14, 0,2*3.1415)
169      cr.fill()
170      cr.set_source_rgb(1.0, 0.0, 0.0)
171      cr.arc(x,y,10, 0,2*3.1415)
172      cr.fill()
173     
174    # Overlay (menus etc)
175    self.modules['overlay'].draw(cr, self.rect)
176   
177    end = clock()
178    delay = end - start
179    #print "%1.1f ms" % (delay * 100)
180   
181  def do_realize(self):
182    self.set_flags(self.flags() | gtk.REALIZED)
183    self.window = gdk.Window( \
184      self.get_parent_window(),
185      width = self.allocation.width,
186      height = self.allocation.height,
187      window_type = gdk.WINDOW_CHILD,
188      wclass = gdk.INPUT_OUTPUT,
189      event_mask = self.get_events() | gdk.EXPOSURE_MASK)
190    self.window.set_user_data(self)
191    self.style.attach(self.window)
192    self.style.set_background(self.window, gtk.STATE_NORMAL)
193    self.window.move_resize(*self.allocation)
194  def do_size_request(self, allocation):
195    pass
196  def do_size_allocate(self, allocation):
197    self.allocation = allocation
198    if self.flags() & gtk.REALIZED:
199      self.window.move_resize(*allocation)
200  def _expose_cairo(self, event, cr):
201    self.rect = self.allocation
202    self.modules['projection'].setView( \
203      self.rect.x, 
204      self.rect.y, 
205      self.rect.width, 
206      self.rect.height)
207    self.draw(cr)
208  def do_expose_event(self, event):
209    self.chain(event)
210    cr = self.window.cairo_create()
211    return self._expose_cairo(event, cr)
212
213class GuiBase:
214  """Wrapper class for a GUI interface"""
215  def __init__(self, osmDataFile, positionFile):
216    # Create the window
217    win = gtk.Window()
218    win.set_title('pyroute')
219    win.connect('delete-event', gtk.main_quit)
220    win.resize(430,600)
221    win.move(50, gtk.gdk.screen_height() - 650)
222   
223    # Events
224    event_box = gtk.EventBox()
225    event_box.connect("button_press_event", lambda w,e: self.pressed(e))
226    event_box.connect("button_release_event", lambda w,e: self.released(e))
227    event_box.connect("motion_notify_event", lambda w,e: self.moved(e))
228    win.add(event_box)
229   
230    # Create the map
231    self.mapWidget = MapWidget(osmDataFile, positionFile)
232    event_box.add(self.mapWidget)
233   
234    # Finalise the window
235    win.show_all()
236    gtk.main()
237   
238  def pressed(self, event):
239    self.dragstartx = event.x
240    self.dragstarty = event.y
241    #print dir(event)
242    #print "Pressed button %d" % event.button
243    self.dragx = event.x
244    self.dragy = event.y
245  def moved(self, event):
246    """Drag-handler"""
247    self.mapWidget.move(event.x - self.dragx, event.y - self.dragy)
248    self.dragx = event.x
249    self.dragy = event.y
250  def released(self, event):
251    dx = event.x - self.dragstartx
252    dy = event.y - self.dragstarty
253    distSq = dx * dx + dy * dy
254    if distSq < 4:
255      self.mapWidget.click(event.x, event.y)
256
257
258if __name__ == "__main__":
259  program = GuiBase(sys.argv[1], sys.argv[2])
Note: See TracBrowser for help on using the repository browser.