import wx import atexit import traceback from time import time from cStringIO import StringIO from threading import Thread, Event from Queue import Queue, Empty, Full from httplib import HTTPConnection, ResponseNotReady from tilefetcher import TileFetcher class DownloadRequest: def __init__(self, path, coords): self.path = path self.coords = coords self.status = None self.data = None self.doneevent = Event() def __eq__(self, other): return self.path == other.path def __repr__(self): return "DownloadRequest(%s)" % (repr(self.path)) class DownloadThread(Thread): interval = 1.0 keep_connection = 5.0 def __init__(self, server, queue): Thread.__init__(self) self.queue = queue self.stopevent = Event() self.connection = HTTPConnection(server) self.connecttime = None atexit.register(self.stop) def handle(self, request): self.connection.request("GET", request.path) response = self.connection.getresponse() request.status = response.status request.data = response.read() request.doneevent.set() def run(self): while not self.stopevent.isSet(): try: request = self.queue.get(timeout = self.interval) self.handle(request) except Empty: pass except: traceback.print_exc() if self.connecttime and self.connecttime + self.keep_connection < time.time(): self.connection.close() self.connecttime = None self.connection.close() def stop(self): self.stopevent.set() def join(self): self.stop() Thread.join(self) class TileFetcher_Download(TileFetcher): def _init(self, servers, queuesize = 32): if isinstance(servers, str): servers = [servers] self.serverthreads = [] self.queue = Queue(queuesize) self.ongoing_requests = [] for server in servers: thread = DownloadThread(server, self.queue) self.serverthreads.append(thread) thread.start() def get_image(self, request): '''Get an image from a request and remove the request from ongoing_requests. If a download error occurred, return an empty image.''' assert request.doneevent.isSet() self.ongoing_requests.remove(request) if request.status == 200: buf = StringIO(request.data) return wx.ImageFromStream(buf) else: return self.tilemanager.emptytile def upkeep(self): for request in self.ongoing_requests: if request.doneevent.isSet(): image = self.get_image(request) self.tilemanager.push_back(request.coords, image) def has_tile(self, coords): '''Do the coordinates fit in the range for the server?''' return False def get_path(self, coords): '''Return tile image path on the server''' return "" def put_request(self, request, block): '''Put a request in the queue. If queue is full, discard some of the oldest requests.''' while True: try: self.queue.put(request, block) self.ongoing_requests.append(request) break except Full: # Remove the oldest request dropout = self.queue.get(False) if dropout: self.ongoing_requests.remove(dropout) def _get_tile(self, coords, block): path = self.get_path(coords) if not path: # Invalid coords return self.emptyimage request = DownloadRequest(path, coords) if request in self.ongoing_requests: # Get the previously processed request instance idx = self.ongoing_requests.index(request) request = self.ongoing_requests[idx] else: self.put_request(request, block) if request.doneevent.isSet(): return self.get_image(request) else: return None