Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions ModestMaps/Providers.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ def sourceCoordinate(self, coordinate):

return Coordinate(coordinate.row, wrappedColumn, coordinate.zoom)

def tiles_are_images(self):
return True

class TemplatedMercatorProvider(IMapProvider):
""" Convert URI templates into tile URLs, using a tileUrlTemplate identical to:
http://code.google.com/apis/maps/documentation/overlays.html#Custom_Map_Types
Expand Down
77 changes: 56 additions & 21 deletions ModestMaps/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,7 @@ def load(self, lock, verbose, cache, fatbits_ok, attempt=1, scale=1):
# this is the time-consuming part
try:
imgs = []

for (scheme, netloc, path, params, query, fragment) in map(urlparse.urlparse, urls):
if (netloc, path, query) in cache:
if lock.acquire():
Expand All @@ -265,7 +265,12 @@ def load(self, lock, verbose, cache, fatbits_ok, attempt=1, scale=1):
printlocked(lock, 'Found', urlparse.urlunparse(('http', netloc, path, '', query, '')), 'in cache')

elif scheme in ('file', ''):
img = Image.open(path).convert('RGBA')
if self.provider.tiles_are_images():
img = Image.open(path).convert('RGBA')
else:
fp = builtins.open(fp, "rb")
img = fp.read()
fp.close()
imgs.append(img)

if lock.acquire():
Expand All @@ -277,16 +282,19 @@ def load(self, lock, verbose, cache, fatbits_ok, attempt=1, scale=1):
conn.request('GET', path + ('?' + query).rstrip('?'), headers={'User-Agent': 'Modest Maps python branch (http://modestmaps.com)'})
response = conn.getresponse()
status = str(response.status)

if status.startswith('2'):
img = Image.open(StringIO.StringIO(response.read())).convert('RGBA')
if self.provider.tiles_are_images():
img = Image.open(StringIO.StringIO(response.read())).convert('RGBA')
else:
img = response.read()
imgs.append(img)

if lock.acquire():
cache[(netloc, path, query)] = img
lock.release()

elif status == '404' and fatbits_ok:
elif status == '404' and fatbits_ok and self.provider.tiles_are_images():
#
# We're probably never going to see this tile.
# Try the next lower zoom level for a pixellated output?
Expand All @@ -308,8 +316,10 @@ def load(self, lock, verbose, cache, fatbits_ok, attempt=1, scale=1):
self.coord = parent

return self.load(lock, verbose, cache, fatbits_ok, attempt+1, scale*2)
else:
raise ValueError('unknown fetching scheme.')

if scale > 1:
if scale > 1 and self.provider.tiles_are_images():
imgs = [img.resize((img.size[0] * scale, img.size[1] * scale)) for img in imgs]

except:
Expand Down Expand Up @@ -491,25 +501,23 @@ def draw_bbox(self, bbox, zoom=16, verbose=False) :
self.dimensions = Core.Point(width, height)

return self.draw()

#

def draw(self, verbose=False, fatbits_ok=False):
""" Draw map out to a PIL.Image and return it.
"""

def build_tile_queue(self):
coord = self.coordinate.copy()
corner = Core.Point(int(self.offset.x + self.dimensions.x/2), int(self.offset.y + self.dimensions.y/2))

while corner.x > 0:
corner.x -= self.provider.tileWidth()
coord = coord.left()

while corner.y > 0:
corner.y -= self.provider.tileHeight()
coord = coord.up()

tiles = TileQueue()

rowCoord = coord.copy()
for y in range(corner.y, self.dimensions.y, self.provider.tileHeight()):
tileCoord = rowCoord.copy()
Expand All @@ -518,30 +526,43 @@ def draw(self, verbose=False, fatbits_ok=False):
tileCoord = tileCoord.right()
rowCoord = rowCoord.down()

return self.render_tiles(tiles, self.dimensions.x, self.dimensions.y, verbose, fatbits_ok)
return tiles

#

def render_tiles(self, tiles, img_width, img_height, verbose=False, fatbits_ok=False):

def draw(self, verbose=False, fatbits_ok=False):
""" Draw map out to a PIL.Image and return it.
"""
tiles = self.build_tile_queue()
return self.render_tiles(tiles, self.dimensions.x, self.dimensions.y, verbose, fatbits_ok)

#

def fetch_tiles(self, tiles, verbose=False, fatbits_ok=False):
lock = thread.allocate_lock()
threads = 32
cache = {}

for off in range(0, len(tiles), threads):
pool = tiles[off:(off + threads)]

for tile in pool:
# request all needed images
thread.start_new_thread(tile.load, (lock, verbose, cache, fatbits_ok))

# if it takes any longer than 20 sec overhead + 10 sec per tile, give up
due = time.time() + 20 + len(pool) * 10

while time.time() < due and pool.pending():
# hang around until they are loaded or we run out of time...
time.sleep(1)

#

def render_tiles(self, tiles, img_width, img_height, verbose=False, fatbits_ok=False):

self.fetch_tiles(tiles, verbose, fatbits_ok)

mapImg = Image.new('RGB', (img_width, img_height))

for tile in tiles:
Expand All @@ -554,6 +575,20 @@ def render_tiles(self, tiles, img_width, img_height, verbose=False, fatbits_ok=F

return mapImg

#

def get_tile_data(self, verbose=False, fatbits_ok=False):
""" Fetch all of the tiles necessary and return them as a TileQueue.
:param verbose:
:param fatbits_ok:
:return: TileQueue of loaded TileRequests.
"""
tiles = self.build_tile_queue()

self.fetch_tiles(tiles, verbose, fatbits_ok)

return tiles

if __name__ == '__main__':
import doctest
doctest.testmod()