~ubuntu-branches/debian/wheezy/agtl/wheezy

« back to all changes in this revision

Viewing changes to files/advancedcaching/openstreetmap.py

  • Committer: Bazaar Package Importer
  • Author(s): Heiko Stuebner
  • Date: 2011-01-22 13:55:12 UTC
  • mfrom: (1.1.5 upstream)
  • Revision ID: james.westby@ubuntu.com-20110122135512-1mik0vssgpnx2fgu
Tags: 0.8.0.3-1
New upstream release

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
#!/usr/bin/python
2
2
# -*- coding: utf-8 -*-
3
3
 
4
 
#        Copyright (C) 2009 Daniel Fett
5
 
#         This program is free software: you can redistribute it and/or modify
 
4
#   Copyright (C) 2010 Daniel Fett
 
5
#   This program is free software: you can redistribute it and/or modify
6
6
#   it under the terms of the GNU General Public License as published by
7
7
#   the Free Software Foundation, either version 3 of the License, or
8
8
#   (at your option) any later version.
15
15
#   You should have received a copy of the GNU General Public License
16
16
#   along with this program.  If not, see <http://www.gnu.org/licenses/>.
17
17
#
18
 
#        Author: Daniel Fett advancedcaching@fragcom.de
 
18
#   Author: Daniel Fett agtl@danielfett.de
 
19
#   Jabber: fett.daniel@jaber.ccc.de
 
20
#   Bugtracker and GIT Repository: http://github.com/webhamster/advancedcaching
19
21
#
20
22
 
21
 
 
22
 
import math
23
 
import thread
24
 
import geo
25
 
import gobject
26
 
import gtk
27
 
import os
28
 
import threading
29
 
import urllib
30
 
# import socket
31
 
 
32
 
# socket.setdefaulttimeout(10)
33
 
# makes problem on slow (gprs) connections
34
 
 
35
 
class TileLoader(threading.Thread):
36
 
    downloading = []
37
 
    semaphore = threading.Semaphore(40)
38
 
    lock = thread.allocate_lock() #download-lock
39
 
    running_threads = 0
40
 
    noimage = None
41
 
                
42
 
    def __init__(self, tile, zoom, gui, base_dir, num=0):
43
 
        threading.Thread.__init__(self)
44
 
        self.daemon = False
45
 
        self.tile = tile
46
 
        self.zoom = zoom
47
 
        self.gui = gui
48
 
        self.base_dir = base_dir
49
 
        self.pbuf = None
50
 
        self.num = num
51
 
        self.local_filename = os.path.join(self.base_dir, str(self.zoom), str(self.tile[0]), "%d.png" % self.tile[1])
52
 
        self.remote_filename = "http://128.40.168.104/mapnik/%d/%d/%d.png" % (self.zoom, self.tile[0], self.tile[1])
53
 
        self.my_noimage = None
54
 
        #self.remote_filename = "http://andy.sandbox.cloudmade.com/tiles/cycle/%d/%d/%d.png" % (self.zoom, self.tile[0], self.tile[1])
55
 
               
56
 
    def run(self):
57
 
        self.__log("start")
58
 
        answer = True
59
 
        if not os.path.isfile(self.local_filename):
60
 
            print "Datei existiert nicht: '%s' " % self.local_filename
61
 
            path_1 = "%s%d" % (self.base_dir, self.zoom)
62
 
            path_2 = "%s/%d" % (path_1, self.tile[0])
63
 
            try:
64
 
                if not os.path.exists(path_1):
65
 
                    os.mkdir(path_1)
66
 
                if not os.path.exists(path_2):
67
 
                    os.mkdir(path_2)
 
23
from __future__ import with_statement
 
24
 
 
25
import logging
 
26
logger = logging.getLogger('openstreetmap')
 
27
 
 
28
from os import path, mkdir, extsep, remove
 
29
from threading import Semaphore
 
30
from urllib import urlretrieve
 
31
from socket import setdefaulttimeout
 
32
setdefaulttimeout(30)
 
33
 
 
34
CONCURRENT_THREADS = 10
 
35
 
 
36
def get_tile_loader(prefix, remote_url, max_zoom = 18, reverse_zoom = False, file_type = 'png', size = 256):
 
37
    class TileLoader():
 
38
        downloading = {}
 
39
        semaphore = Semaphore(CONCURRENT_THREADS)
 
40
        noimage_cantload = None
 
41
        noimage_loading = None
 
42
        base_dir = ''
 
43
        
 
44
        PREFIX = prefix
 
45
        MAX_ZOOM = max_zoom
 
46
        FILE_TYPE = file_type
 
47
        REMOTE_URL = remote_url
 
48
        TILE_SIZE = size
 
49
 
 
50
        TPL_LOCAL_PATH = path.join("%s", PREFIX, "%d", "%d")
 
51
        TPL_LOCAL_FILENAME = path.join("%s", "%%d%s%s" % (extsep, FILE_TYPE))
 
52
 
 
53
        def __init__(self, id_string, tile, zoom, undersample = False, x = 0, y = 0, callback_draw = None, callback_load = None):
 
54
            self.id_string = id_string
 
55
            self.undersample = undersample
 
56
            self.tile = tile
 
57
            #self.download_tile = self.gui.ts.check_bounds(*tile)
 
58
            self.download_tile = tile
 
59
            if not undersample:
 
60
                self.download_zoom = zoom
 
61
                self.display_zoom = zoom
 
62
            else:
 
63
                self.download_zoom = zoom - 1
 
64
                self.display_zoom = zoom
 
65
                self.download_tile = (int(self.download_tile[0]/2), int(self.download_tile[1]/2))
 
66
            self.pbuf = None
 
67
            self.callback_draw = callback_draw
 
68
            self.callback_load = callback_load
 
69
 
 
70
            self.my_noimage = None
 
71
            self.stop = False
 
72
            self.x = x
 
73
            self.y = y
 
74
 
 
75
            # setup paths
 
76
            self.local_path = self.TPL_LOCAL_PATH % (self.base_dir, self.download_zoom, self.download_tile[0])
 
77
            self.local_filename = self.TPL_LOCAL_FILENAME % (self.local_path, self.download_tile[1])
 
78
            self.remote_filename = self.REMOTE_URL % {'zoom': self.download_zoom, 'x' : self.download_tile[0], 'y' : self.download_tile[1]}
 
79
            
 
80
 
 
81
        def halt(self):
 
82
            self.stop = True
 
83
            
 
84
 
 
85
        @staticmethod
 
86
        def create_recursive(dpath):
 
87
            if dpath != '/':
 
88
                if not path.exists(dpath):
 
89
                    head, tail = path.split(dpath)
 
90
                    TileLoader.create_recursive(head)
 
91
                    try:
 
92
                        mkdir(dpath)
 
93
                    except Exception:
 
94
                        # let others fail here.
 
95
                        pass
 
96
 
 
97
 
 
98
        def run(self):
 
99
            answer = True
 
100
            if not path.isfile(self.local_filename):
 
101
                self.create_recursive(self.local_path)
 
102
                self.draw(self.get_no_image(self.noimage_loading))
 
103
                answer = self.__download(self.remote_filename, self.local_filename)
 
104
 
 
105
            # now the file hopefully exists
 
106
            if answer == True:
 
107
                self.load()
 
108
                self.draw(self.pbuf)
 
109
                #gobject.idle_add(lambda: self.draw(self.pbuf))
 
110
            elif answer == False:
 
111
                #gobject.idle_add(lambda: self.draw(self.get_no_image(self.noimage_cantload)))
 
112
                self.draw(self.get_no_image(self.noimage_cantload))
 
113
            else:
 
114
                # Do nothing here, as the thread was told to stop
 
115
                pass
 
116
 
 
117
        def run_again(self):
 
118
            self.load()
 
119
            #gobject.idle_add(lambda: self.draw(self.pbuf))
 
120
            self.draw(self.pbuf)
 
121
            return False
 
122
 
 
123
        def get_no_image(self, default):
 
124
            return (default, None)
 
125
            '''
 
126
            if self.my_noimage != None:
 
127
                return self.my_noimage
 
128
            size, tile = self.TILE_SIZE, self.tile
 
129
            # we have no image available. so what do now?
 
130
            # first, check if we've the "supertile" available (zoomed out)
 
131
            supertile_zoom = self.download_zoom - 1
 
132
            supertile_x = int(tile[0]/2)
 
133
            supertile_y = int(tile[1]/2)
 
134
            supertile_path = self.TPL_LOCAL_PATH % (self.base_dir, supertile_zoom, supertile_x)
 
135
            supertile_name = self.TPL_LOCAL_FILENAME % (supertile_path, supertile_y)
 
136
            #supertile_name = path.join(TileLoader.base_dir, self.PREFIX, str(supertile_zoom), str(supertile_x), "%d%s%s" % (supertile_y, extsep, self.FILE_TYPE))
 
137
            if not self.undersample and path.exists(supertile_name):
 
138
                off_x = (tile[0]/2.0 - supertile_x) * size
 
139
                off_y = (tile[1]/2.0 - supertile_y) * size
 
140
                #pbuf = gtk.gdk.pixbuf_new_from_file(supertile_name)
 
141
                #dest = gtk.gdk.Pixbuf(pbuf.get_colorspace(), pbuf.get_has_alpha(), pbuf.get_bits_per_sample(), size, size)
 
142
                #pbuf.scale(dest, 0, 0, 256, 256, -off_x*2, -off_y*2, 2, 2, gtk.gdk.INTERP_BILINEAR)
 
143
                self.pbuf = (surface, (off_x, off_y))
 
144
                self.my_noimage = surface
 
145
                return dest
 
146
            else:
 
147
                self.my_noimage = default
 
148
                return default
 
149
            '''
 
150
 
 
151
        def load(self, tryno=0):
 
152
            # load the pixbuf to memory
 
153
            if self.stop:
 
154
                return True
 
155
            try:
 
156
                size, tile = self.TILE_SIZE, self.tile
 
157
                if self.undersample:
 
158
                    # don't load the tile directly, but load the supertile instead
 
159
                    supertile_x = int(tile[0]/2)
 
160
                    supertile_y = int(tile[1]/2)
 
161
                    off_x = (tile[0]/2.0 - supertile_x) * size
 
162
                    off_y = (tile[1]/2.0 - supertile_y) * size
 
163
                    surface = self.callback_load(self.local_filename)
 
164
                    
 
165
                    self.pbuf = (surface, (off_x, off_y))
 
166
                else:
 
167
                    surface = self.callback_load(self.local_filename)
 
168
                    self.pbuf = (surface, None)
 
169
                return True
 
170
            except Exception, e:
 
171
                if tryno == 0:
 
172
                    return self.recover()
 
173
                else:
 
174
                    logger.exception("Exception while loading map tile: %s" % e)
 
175
                    self.pbuf = (self.noimage_cantload, None)
 
176
                    return True
 
177
 
 
178
        def recover(self):
 
179
            try:
 
180
                remove(self.local_filename)
68
181
            except:
69
182
                pass
70
 
                # this may fail due to threading issues.
71
 
                # too lazy to do proper locking here
72
 
                # so just forget about the error
73
 
                                                        
74
 
 
75
 
            gobject.idle_add(lambda: self.draw(self.get_no_image()))
76
 
            answer = self.download(self.remote_filename, self.local_filename)
77
 
        # now the file hopefully exists
78
 
        if answer == True:
79
 
            self.load()
80
 
            gobject.idle_add(lambda: self.draw(self.pbuf))
81
 
        elif answer == False:
82
 
            print "loading default"
83
 
            gobject.idle_add(lambda: self.draw(self.get_no_image()))
84
 
        else:
85
 
            print "nothing"
86
 
            
87
 
 
88
 
        self.__log("prep draw")
89
 
 
90
 
    def get_no_image(self):
91
 
        if self.my_noimage != None:
92
 
            return self.my_noimage
93
 
        size = self.gui.ts.tile_size()
94
 
        # we have no image available. so what do now?
95
 
        # first, check if we've the "supertile" available (zoomed out)
96
 
        supertile_zoom = self.zoom - 1
97
 
        supertile_x = int(self.tile[0]/2)
98
 
        supertile_y = int(self.tile[1]/2)
99
 
        supertile_name = os.path.join(self.base_dir, str(supertile_zoom), str(supertile_x), "%d.png" % supertile_y)
100
 
        if os.path.exists(supertile_name):
101
 
            print "Loading supertile for %d %d, which is %d %d" % (self.tile[0], self.tile[1], supertile_x, supertile_y)
102
 
            # great! now find the right spot.
103
 
            # the supertile is 'size' px wide and high.
104
 
            off_x = (self.tile[0]/2.0 - supertile_x) * size
105
 
            off_y = (self.tile[1]/2.0 - supertile_y) * size
106
 
            print off_x, off_y
107
 
            pbuf = gtk.gdk.pixbuf_new_from_file(supertile_name)
108
 
            #pbuf.scale(pbuf, 0, 0, size/2, size/2, off_x, off_y, 2, 2, gtk.gdk.INTERP_BILINEAR)
109
 
            dest = gtk.gdk.Pixbuf(pbuf.get_colorspace(), pbuf.get_has_alpha(), pbuf.get_bits_per_sample(), size, size)
110
 
            pbuf.scale(dest, 0, 0, 256, 256, -off_x*2, -off_y*2, 2, 2, gtk.gdk.INTERP_BILINEAR)
111
 
            self.my_noimage = dest
112
 
            return dest
113
 
        else:
114
 
            self.my_noimage = TileLoader.noimage
115
 
            return TileLoader.noimage
116
 
                
117
 
    def load(self, tryno=0):
118
 
        # load the pixbuf to memory
119
 
        try:
120
 
            self.pbuf = gtk.gdk.pixbuf_new_from_file(self.local_filename)
121
 
            if self.pbuf.get_width() < self.gui.ts.tile_size() or self.pbuf.get_height() < self.gui.ts.tile_size():
122
 
                raise Exception("Image too small, probably corrupted file")
123
 
            return True
124
 
        except Exception as e:
125
 
            if tryno == 0:
126
 
                return self.recover()
127
 
            else:
128
 
                print e
129
 
                self.pbuf = TileLoader.noimage
 
183
            self.__download(self.remote_filename, self.local_filename)
 
184
            return self.load(1)
 
185
 
 
186
        def draw(self, pbuf):
 
187
            if not self.stop:
 
188
                return self.callback_draw(self.id_string, pbuf[0], self.x, self.y, pbuf[1])
 
189
            return False
 
190
 
 
191
 
 
192
        def __download(self, remote, local):
 
193
            if path.exists(local):
130
194
                return True
131
 
                                
132
 
    def recover(self):
133
 
        try:
134
 
            os.remove(self.local_filename)
135
 
        except:
136
 
            pass
137
 
        self.download(self.remote_filename, self.local_filename)
138
 
        return self.load(1)
139
 
                
140
 
    def draw(self, pbuf):
141
 
        try:
142
 
            #gc.set_function(gtk.gdk.COPY)
143
 
            #gc.set_rgb_fg_color(self.COLOR_BG)
144
 
            # to draw "night mode": INVERT
145
 
                        
146
 
            size = self.gui.ts.tile_size()
147
 
            x = self.gui.map_center_x
148
 
            y = self.gui.map_center_y
149
 
            xi = int(self.gui.map_center_x)
150
 
            yi = int(self.gui.map_center_y)
151
 
            span_x = int(math.ceil(float(self.gui.map_width) / (size * 2.0)))
152
 
            span_y = int(math.ceil(float(self.gui.map_height) / (size * 2.0)))
153
 
            if self.tile[0] in xrange(xi - span_x, xi + span_x + 1, 1) and self.tile[1] in xrange(yi - span_y, yi + span_y + 1, 1) and self.zoom == self.gui.ts.zoom:
154
 
 
155
 
                offset_x = int(self.gui.map_width / 2 - (x - int(x)) * size)
156
 
                offset_y = int(self.gui.map_height / 2 -(y - int(y)) * size)
157
 
                dx = (self.tile[0] - xi) * size + offset_x
158
 
                dy = (self.tile[1] - yi) * size + offset_y
159
 
 
160
 
                gc = self.gui.xgc
161
 
 
162
 
                if pbuf != None:
163
 
                    self.gui.pixmap.draw_pixbuf(gc, pbuf, 0, 0, dx, dy, size, size)
164
 
                else:
165
 
                    self.gui.pixmap.draw_pixbuf(gc, TileLoader.noimage, 0, 0, dx, dy, size, size)
166
 
 
167
 
                self.gui.drawing_area.queue_draw_area(max(self.gui.draw_root_x + self.gui.draw_at_x  + dx, 0), max(self.gui.draw_root_y + self.gui.draw_at_y  + dy, 0), size, size)
168
 
 
169
 
                                
170
 
        finally:
171
 
            pass
172
 
                
173
 
    def download(self, remote, local):
174
 
        print "downloading", remote
175
 
        acquired = False
176
 
        self.__log("dl-start")
177
 
        
178
 
        TileLoader.lock.acquire()
179
 
        try:
180
 
            if remote in TileLoader.downloading:
181
 
                print 'lädt schon: %s' % remote
182
 
                return None
183
 
            if os.path.exists(local):
184
 
                print 'ex schon: %s' % remote
185
 
                return None
186
 
            TileLoader.downloading.append(remote)
187
 
        finally:
188
 
            TileLoader.lock.release()
189
 
            
190
 
        TileLoader.semaphore.acquire()
191
 
        acquired = True
192
 
        try:
193
 
            if not self.zoom == self.gui.ts.zoom:
194
 
                return None
195
 
            info = urllib.urlretrieve(remote, local)    
196
 
            print "feddisch!"
197
 
            if "text/html" in info[1]['Content-Type']:
198
 
                print "File not found: %s" % remote
199
 
                return False
200
 
            return True
201
 
        except Exception as e:
202
 
            print "Download Error", e
203
 
            return False
204
 
        finally:
205
 
            if acquired:
206
 
                TileLoader.semaphore.release()
207
 
            TileLoader.downloading.remove(remote)
208
 
            self.__log("dl-end")
209
 
            
210
 
            
211
 
    def __log(self, string):
212
 
        pass
213
 
#        a = "%d  " % self.num
214
 
#        for i in xrange(self.num):
215
 
#            a += "   "
216
 
        #print a, string
217
 
                
218
 
class TileServer():
219
 
 
220
 
    def __init__(self):
221
 
        self.zoom = 14
222
 
        self.max_zoom = 18
223
 
                
224
 
    def get_zoom(self):
225
 
        return self.zoom
226
 
                
227
 
    def set_zoom(self, zoom):
228
 
        if zoom < 1 or zoom > self.max_zoom:
229
 
            return
230
 
        self.zoom = zoom
231
 
 
232
 
    @staticmethod
233
 
    def tile_size():
234
 
        return 256
235
 
                
236
 
    def deg2tilenum(self, lat_deg, lon_deg):
237
 
        #lat_rad = lat_deg * math.pi / 180.0
238
 
        lat_rad = math.radians(lat_deg)
239
 
        n = 2 ** self.zoom
240
 
        xtile = int((lon_deg + 180) / 360 * n)
241
 
        ytile = int((1.0 - math.log(math.tan(lat_rad) + (1.0 / math.cos(lat_rad))) / math.pi) / 2.0 * n)
242
 
        return(xtile, ytile)
243
 
                
244
 
    def deg2num(self, coord):
245
 
        lat_rad = math.radians(coord.lat)
246
 
        #lat_rad = (coord.lat * math.pi) / 180.0
247
 
        n = 2 ** self.zoom
248
 
        xtile = (coord.lon + 180) / 360 * n
249
 
        ytile = (1.0 - math.log(math.tan(lat_rad) + (1.0 / math.cos(lat_rad))) / math.pi) / 2.0 * n
250
 
        return(xtile, ytile)
251
 
                
252
 
    def num2deg(self, xtile, ytile):
253
 
        n = 2 ** self.zoom
254
 
        lon_deg = xtile / n * 360.0 - 180.0
255
 
        lat_rad = math.atan(math.sinh(math.pi * (1 - 2 * ytile / n)))
256
 
        lat_deg = lat_rad * 180.0 / math.pi
257
 
        return geo.Coordinate(lat_deg, lon_deg)
258
 
 
 
195
            #import time
 
196
            #time.sleep(10)
 
197
            #return False
 
198
            with TileLoader.semaphore:
 
199
                try:
 
200
                    if self.stop:
 
201
                        return None
 
202
                    info = urlretrieve(remote, local)
 
203
 
 
204
                    if "text/html" in info[1]['Content-Type']:
 
205
                        return False
 
206
                    return True
 
207
                except Exception, e:
 
208
                    print "Download Error", e
 
209
                    return False
 
210
 
 
211
        def download_tile_only(self):
 
212
            if not path.isfile(self.local_filename):
 
213
                self.create_recursive(self.local_path)
 
214
            return self.__download(self.remote_filename, self.local_filename)
 
215
 
 
216
    return TileLoader
259
217