Added first version of mapping utility
[wifihood] / wifiview
1 #!/usr/bin/env python
2
3 import gtk
4 import gobject
5
6 import urllib2
7 import math
8
9 import os
10
11 class mapWidget(gtk.Image):
12
13   def __init__(self):
14
15     gtk.Image.__init__(self)
16
17     # Maximum width should be 800, but actually gets reduced
18     self.win_x , self.win_y = 800 , 480
19
20     p = gtk.gdk.Pixbuf(gtk.gdk.COLORSPACE_RGB, True, 8, self.win_x, self.win_y)
21     self.set_from_pixbuf(p)
22
23     zoom = 11
24     #lat = 40.4400226381
25     #lon = -3.6880185362
26     lat = 40.40491 
27     lon = -3.6774
28     self.reftile_x , self.reftile_y = self.lon2tilex( lon , zoom ) , self.lat2tiley( lat , zoom )
29     # Half the size of a tile
30     self.refpix_x , self.refpix_y = 128 , 128
31     self.refpix_x , self.refpix_y = 0 , 0
32
33     self.composeMap()
34
35   def lon2tilex ( self , lon , zoom=11 ) :
36     return int( ( lon + 180 ) / 360 * 2 ** zoom )
37
38   def lat2tiley ( self , lat , zoom=11 ) :
39     lat = lat * math.pi / 180
40     return int( ( 1 - math.log( math.tan( lat ) + 1 / math.cos( lat ) ) / math.pi ) / 2 * 2 ** zoom )
41
42   def composeMap( self ) :
43     center_x , center_y = self.win_x / 2 , self.win_y / 2
44
45     # To get the central pixel in the window center, we must shift to the tile origin
46     center_x -= self.refpix_x
47     center_y -= self.refpix_y
48
49     # Ranges should be long enough as to fill the screen
50     # Maybe they should be decided based on self.win_x, self.win_y
51     for i in range(-3,4) :
52       for j in range(-3,4) :
53         file = self.tilename( i , j )
54         if file is None :
55           pixbuf = gtk.gdk.Pixbuf(gtk.gdk.COLORSPACE_RGB, False, 8, 256, 256 )
56           pixbuf.fill( 0x00000000 )
57         else :
58           try :
59             pixbuf = gtk.gdk.pixbuf_new_from_file( file )
60           except gobject.GError , ex :
61             print "Corrupted file %s" % ( file )
62             os.unlink( file )
63             #file = self.tilename( self.reftile_x + i , self.reftile_y + j )
64             file = self.tilename( i , j )
65             try :
66               pixbuf = gtk.gdk.pixbuf_new_from_file( file )
67             except :
68               print "Total failure for tile for %s,%s" % ( self.reftile_x + i , self.reftile_y + j )
69               pixbuf = gtk.gdk.Pixbuf(gtk.gdk.COLORSPACE_RGB, False, 8, 256, 256 )
70
71         dest_x = 256 * i + center_x
72         dest_y = 256 * j + center_y
73
74         init_x = 0
75         size_x = 256
76         if dest_x < 0 :
77            init_x = abs(dest_x)
78            size_x = 256 + dest_x
79            dest_x = 0
80         if dest_x + 256 > self.win_x :
81            size_x = self.win_x - dest_x
82
83         init_y = 0
84         size_y = 256
85         if dest_y < 0 :
86            init_y = abs(dest_y)
87            size_y = 256 + dest_y
88            dest_y = 0
89         if dest_y + 256 > self.win_y :
90            size_y = self.win_y - dest_y
91
92         if ( size_x > 0 and size_y > 0 ) and ( init_x < 256 and init_y < 256 ) :
93             pixbuf.copy_area( init_x, init_y, size_x, size_y, self.get_pixbuf(), dest_x , dest_y )
94         del(pixbuf)
95
96   def tilename ( self , x , y , zoom=11 ) :
97     file = self.tile2file( self.reftile_x + x , self.reftile_y + y , zoom )
98     try :
99       os.stat(file)
100     except :
101     #  if mapDownload :
102       if False :
103         try :
104           # useful members : response.code, response.headers
105           response = urllib2.urlopen( "http://tile.openstreetmap.org/%s/%s/%s.png" % ( zoom , x , y ) )
106           if response.geturl() == "http://tile.openstreetmap.org/11/0/0.png" :
107               return None
108           fd = open( file , 'w' )
109           fd.write( response.read() )
110           fd.close()
111         except :
112           return None
113       else :
114         return None
115     return file
116
117   def tile2file( self , tilex , tiley , zoom=11 ) :
118     basedir = "/home/user/MyDocs/.maps/OpenStreetMap I"
119     rootdir = "%s/%s" % ( basedir , zoom )
120     if not os.path.isdir( rootdir ) :
121       os.mkdir(rootdir)
122     rootsubdir = "%s/%s" % ( rootdir , tilex )
123     if not os.path.isdir( rootsubdir ) :
124       os.mkdir(rootsubdir)
125     return "%s/%s.png" % ( rootsubdir , tiley )
126
127   def Shift( self , dx , dy ) :
128     self.hide()
129
130     tile_x , tile_y = ( self.refpix_x - dx ) / 256 , ( self.refpix_y - dy ) / 256
131     self.reftile_x += tile_x
132     self.reftile_y += tile_y
133
134     self.refpix_x -= dx + 256 * tile_x
135     self.refpix_y -= dy + 256 * tile_y
136
137     self.composeMap()
138     self.show()
139
140   def Up( self ) :
141     self.hide()
142     self.reftile_y -= 1
143     self.composeMap()
144     self.show()
145
146   def Down( self ) :
147     self.hide()
148     self.reftile_y += 1
149     self.composeMap()
150     self.show()
151
152   def Right( self ) :
153     self.hide()
154     self.reftile_x += 1
155     self.composeMap()
156     self.show()
157
158   def Left( self ) :
159     self.hide()
160     self.reftile_x -= 1
161     self.composeMap()
162     self.show()
163
164 class MapWindow:
165
166     def delete_event(self, widget, event, data=None):
167         # If you return FALSE in the "delete_event" signal handler,
168         # GTK will emit the "destroy" signal. Returning TRUE means
169         # you don't want the window to be destroyed.
170         # This is useful for popping up 'are you sure you want to quit?'
171         # type dialogs.
172         print "delete event occurred"
173
174         # Change FALSE to TRUE and the main window will not be destroyed
175         # with a "delete_event".
176         return False
177
178     def destroy(self, widget, data=None):
179         print "destroy signal occurred"
180         gtk.main_quit()
181
182     def press_event ( self, widget, event, *args ) :
183       # FIXME : Set only if far enough from borders
184       border_x = 40
185       border_y = 30
186       print "press  ",event.get_coords(),event.get_root_coords()
187       if event.x > border_x and event.y > border_y and event.x < ( self.size_x - border_x ) and event.y < ( self.size_y - border_y ) :
188         self.click_x = event.x
189         self.click_y = event.y
190
191     def release_event ( self, widget, event, *args ) :
192       min_shift = 50
193       print "unpress",event.get_coords(),event.get_root_coords()
194       if self.click_x is not None and self.click_y is not None :
195         delta_x = int( event.x - self.click_x )
196         delta_y = int( event.y - self.click_y )
197         shift = math.sqrt( delta_x * delta_x + delta_y * delta_y )
198         if shift > min_shift :
199           self.map.Shift(delta_x, delta_y)
200         #  if delta_x > 100 :
201         #    self.map.Left()
202         #  elif delta_x < -100 :
203         #    self.map.Right()
204         #  elif delta_y > 100 :
205         #    self.map.Up()
206         #  elif delta_y < -100 :
207         #    self.map.Down()
208       self.click_x , self.click_y = None , None
209
210     def screen_event ( self, widget, event, *args ) :
211       print "REDIOS",event
212       print "      ",widget
213       print "      ",args
214
215
216     def on_button_press ( self, widget, event, *args ) :
217       print "HOLA",event
218
219     def on_key_press ( self, widget, event, *args ) :
220       if event.keyval == gtk.keysyms.Up :
221           self.map.Up()
222       elif event.keyval == gtk.keysyms.Down :
223           self.map.Down()
224       elif event.keyval == gtk.keysyms.Right :
225           self.map.Right()
226       elif event.keyval == gtk.keysyms.Left :
227           self.map.Left()
228       else :
229           print "UNKNOWN",event.keyval
230
231     def __init__(self):
232
233         self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
234     
235         self.window.connect("delete_event", self.delete_event)
236         self.window.connect("destroy", self.destroy)
237
238         self.window.set_border_width(10)
239     
240         self.window.connect("key-press-event", self.on_key_press)
241         
242         # To get explicit GDK_BUTTON_PRESS instead of paired GDK_LEAVE_NOTIFY & GDK_ENTER_NOTIFY
243 #        self.window.add_events(gtk.gdk.BUTTON_MOTION_MASK | gtk.gdk.BUTTON_PRESS_MASK | gtk.gdk.BUTTON_RELEASE_MASK | gtk.gdk.POINTER_MOTION_MASK)
244         self.window.set_events( gtk.gdk.BUTTON_PRESS_MASK | gtk.gdk.BUTTON_RELEASE_MASK )
245         #
246 #        self.window.connect('motion_notify_event', self.screen_event)
247         self.window.connect('button_press_event', self.press_event)
248         self.window.connect('button_release_event', self.release_event)
249         #
250         self.map = mapWidget()
251         self.window.add( self.map )
252     
253         # and the window
254         self.window.show_all()
255
256         self.size_x , self.size_y = 800 , 480
257         self.click_x , self.click_y = None , None
258
259     def main(self):
260         gtk.main()
261
262 if __name__ == "__main__":
263     map = MapWindow()
264     map.main()
265