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

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

reading geonames (gets XML from a file at the moment, instead of
downloading it)

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