ModestMaps-1.4.6/0000775000076500000240000000000012333467175014570 5ustar migurskistaff00000000000000ModestMaps-1.4.6/ModestMaps/0000775000076500000240000000000012333467175016644 5ustar migurskistaff00000000000000ModestMaps-1.4.6/ModestMaps/__init__.py0000664000076500000240000004652412333464754020770 0ustar migurskistaff00000000000000""" >>> m = Map(Microsoft.RoadProvider(), Core.Point(600, 600), Core.Coordinate(3165, 1313, 13), Core.Point(-144, -94)) >>> p = m.locationPoint(Geo.Location(37.804274, -122.262940)) >>> p (370.724, 342.549) >>> m.pointLocation(p) (37.804, -122.263) >>> c = Geo.Location(37.804274, -122.262940) >>> z = 12 >>> d = Core.Point(800, 600) >>> m = mapByCenterZoom(Microsoft.RoadProvider(), c, z, d) >>> m.dimensions (800.000, 600.000) >>> m.coordinate (1582.000, 656.000 @12.000) >>> m.offset (-235.000, -196.000) >>> sw = Geo.Location(36.893326, -123.533554) >>> ne = Geo.Location(38.864246, -121.208153) >>> d = Core.Point(800, 600) >>> m = mapByExtent(Microsoft.RoadProvider(), sw, ne, d) >>> m.dimensions (800.000, 600.000) >>> m.coordinate (98.000, 40.000 @8.000) >>> m.offset (-251.000, -218.000) >>> se = Geo.Location(36.893326, -121.208153) >>> nw = Geo.Location(38.864246, -123.533554) >>> d = Core.Point(1600, 1200) >>> m = mapByExtent(Microsoft.RoadProvider(), se, nw, d) >>> m.dimensions (1600.000, 1200.000) >>> m.coordinate (197.000, 81.000 @9.000) >>> m.offset (-246.000, -179.000) >>> sw = Geo.Location(36.893326, -123.533554) >>> ne = Geo.Location(38.864246, -121.208153) >>> z = 10 >>> m = mapByExtentZoom(Microsoft.RoadProvider(), sw, ne, z) >>> m.dimensions (1693.000, 1818.000) >>> m.coordinate (395.000, 163.000 @10.000) >>> m.offset (-236.000, -102.000) >>> se = Geo.Location(36.893326, -121.208153) >>> nw = Geo.Location(38.864246, -123.533554) >>> z = 9 >>> m = mapByExtentZoom(Microsoft.RoadProvider(), se, nw, z) >>> m.dimensions (846.000, 909.000) >>> m.coordinate (197.000, 81.000 @9.000) >>> m.offset (-246.000, -179.000) """ import os.path __version__ = open(os.path.join(os.path.dirname(__file__), 'VERSION')).read().strip() import sys import urllib import httplib import urlparse import StringIO import math import thread import time try: import Image except ImportError: try: import PIL.Image as Image except ImportError: # you need PIL to do any actual drawing, but # maybe that's not what you're using MMaps for? Image = None import Tiles import Providers import Core import Geo import Yahoo, Microsoft, BlueMarble, OpenStreetMap, CloudMade, MapQuest, Stamen import time # a handy list of possible providers, which isn't # to say that you can't go writing your own. builtinProviders = { 'OPENSTREETMAP': OpenStreetMap.Provider, 'OPEN_STREET_MAP': OpenStreetMap.Provider, 'BLUE_MARBLE': BlueMarble.Provider, 'MAPQUEST_ROAD': MapQuest.RoadProvider, 'MAPQUEST_AERIAL': MapQuest.AerialProvider, 'MICROSOFT_ROAD': Microsoft.RoadProvider, 'MICROSOFT_AERIAL': Microsoft.AerialProvider, 'MICROSOFT_HYBRID': Microsoft.HybridProvider, 'YAHOO_ROAD': Yahoo.RoadProvider, 'YAHOO_AERIAL': Yahoo.AerialProvider, 'YAHOO_HYBRID': Yahoo.HybridProvider, 'CLOUDMADE_ORIGINAL': CloudMade.OriginalProvider, 'CLOUDMADE_FINELINE': CloudMade.FineLineProvider, 'CLOUDMADE_TOURIST': CloudMade.TouristProvider, 'CLOUDMADE_FRESH': CloudMade.FreshProvider, 'CLOUDMADE_PALEDAWN': CloudMade.PaleDawnProvider, 'CLOUDMADE_MIDNIGHTCOMMANDER': CloudMade.MidnightCommanderProvider, 'STAMEN_TONER': Stamen.TonerProvider, 'STAMEN_TERRAIN': Stamen.TerrainProvider, 'STAMEN_WATERCOLOR': Stamen.WatercolorProvider, } def mapByCenterZoom(provider, center, zoom, dimensions): """ Return map instance given a provider, center location, zoom value, and dimensions point. """ centerCoord = provider.locationCoordinate(center).zoomTo(zoom) mapCoord, mapOffset = calculateMapCenter(provider, centerCoord) return Map(provider, dimensions, mapCoord, mapOffset) def mapByExtent(provider, locationA, locationB, dimensions): """ Return map instance given a provider, two corner locations, and dimensions point. """ mapCoord, mapOffset = calculateMapExtent(provider, dimensions.x, dimensions.y, locationA, locationB) return Map(provider, dimensions, mapCoord, mapOffset) def mapByExtentZoom(provider, locationA, locationB, zoom): """ Return map instance given a provider, two corner locations, and zoom value. """ # a coordinate per corner coordA = provider.locationCoordinate(locationA).zoomTo(zoom) coordB = provider.locationCoordinate(locationB).zoomTo(zoom) # precise width and height in pixels width = abs(coordA.column - coordB.column) * provider.tileWidth() height = abs(coordA.row - coordB.row) * provider.tileHeight() # nearest pixel actually dimensions = Core.Point(int(width), int(height)) # projected center of the map centerCoord = Core.Coordinate((coordA.row + coordB.row) / 2, (coordA.column + coordB.column) / 2, zoom) mapCoord, mapOffset = calculateMapCenter(provider, centerCoord) return Map(provider, dimensions, mapCoord, mapOffset) def calculateMapCenter(provider, centerCoord): """ Based on a provider and center coordinate, returns the coordinate of an initial tile and its point placement, relative to the map center. """ # initial tile coordinate initTileCoord = centerCoord.container() # initial tile position, assuming centered tile well in grid initX = (initTileCoord.column - centerCoord.column) * provider.tileWidth() initY = (initTileCoord.row - centerCoord.row) * provider.tileHeight() initPoint = Core.Point(round(initX), round(initY)) return initTileCoord, initPoint def calculateMapExtent(provider, width, height, *args): """ Based on a provider, width & height values, and a list of locations, returns the coordinate of an initial tile and its point placement, relative to the map center. """ coordinates = map(provider.locationCoordinate, args) TL = Core.Coordinate(min([c.row for c in coordinates]), min([c.column for c in coordinates]), min([c.zoom for c in coordinates])) BR = Core.Coordinate(max([c.row for c in coordinates]), max([c.column for c in coordinates]), max([c.zoom for c in coordinates])) # multiplication factor between horizontal span and map width hFactor = (BR.column - TL.column) / (float(width) / provider.tileWidth()) # multiplication factor expressed as base-2 logarithm, for zoom difference hZoomDiff = math.log(hFactor) / math.log(2) # possible horizontal zoom to fit geographical extent in map width hPossibleZoom = TL.zoom - math.ceil(hZoomDiff) # multiplication factor between vertical span and map height vFactor = (BR.row - TL.row) / (float(height) / provider.tileHeight()) # multiplication factor expressed as base-2 logarithm, for zoom difference vZoomDiff = math.log(vFactor) / math.log(2) # possible vertical zoom to fit geographical extent in map height vPossibleZoom = TL.zoom - math.ceil(vZoomDiff) # initial zoom to fit extent vertically and horizontally initZoom = min(hPossibleZoom, vPossibleZoom) ## additionally, make sure it's not outside the boundaries set by provider limits #initZoom = min(initZoom, provider.outerLimits()[1].zoom) #initZoom = max(initZoom, provider.outerLimits()[0].zoom) # coordinate of extent center centerRow = (TL.row + BR.row) / 2 centerColumn = (TL.column + BR.column) / 2 centerZoom = (TL.zoom + BR.zoom) / 2 centerCoord = Core.Coordinate(centerRow, centerColumn, centerZoom).zoomTo(initZoom) return calculateMapCenter(provider, centerCoord) def printlocked(lock, *stuff): """ """ if lock.acquire(): print ' '.join([str(thing) for thing in stuff]) lock.release() class TileRequest: # how many times to retry a failing tile MAX_ATTEMPTS = 5 def __init__(self, provider, coord, offset): self.done = False self.provider = provider self.coord = coord self.offset = offset def loaded(self): return self.done def images(self): return self.imgs def load(self, lock, verbose, cache, fatbits_ok, attempt=1, scale=1): if self.done: # don't bother? return urls = self.provider.getTileUrls(self.coord) if verbose: printlocked(lock, 'Requesting', urls, '- attempt no.', attempt, 'in thread', hex(thread.get_ident())) # 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(): img = cache[(netloc, path, query)].copy() lock.release() if verbose: printlocked(lock, 'Found', urlparse.urlunparse(('http', netloc, path, '', query, '')), 'in cache') elif scheme in ('file', ''): img = Image.open(path).convert('RGBA') imgs.append(img) if lock.acquire(): cache[(netloc, path, query)] = img lock.release() elif scheme == 'http': conn = httplib.HTTPConnection(netloc) 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') imgs.append(img) if lock.acquire(): cache[(netloc, path, query)] = img lock.release() elif status == '404' and fatbits_ok: # # We're probably never going to see this tile. # Try the next lower zoom level for a pixellated output? # neighbor = self.coord.zoomBy(-1) parent = neighbor.container() col_shift = 2 * (neighbor.column - parent.column) row_shift = 2 * (neighbor.row - parent.row) # sleep for a second or two, helps prime the image cache time.sleep(col_shift + row_shift/2) x_shift = scale * self.provider.tileWidth() * col_shift y_shift = scale * self.provider.tileHeight() * row_shift self.offset.x -= int(x_shift) self.offset.y -= int(y_shift) self.coord = parent return self.load(lock, verbose, cache, fatbits_ok, attempt+1, scale*2) if scale > 1: imgs = [img.resize((img.size[0] * scale, img.size[1] * scale)) for img in imgs] except: if verbose: printlocked(lock, 'Failed', urls, '- attempt no.', attempt, 'in thread', hex(thread.get_ident())) if attempt < TileRequest.MAX_ATTEMPTS: time.sleep(1 * attempt) return self.load(lock, verbose, cache, fatbits_ok, attempt+1, scale) else: imgs = [None for url in urls] else: if verbose: printlocked(lock, 'Received', urls, '- attempt no.', attempt, 'in thread', hex(thread.get_ident())) if lock.acquire(): self.imgs = imgs self.done = True lock.release() class TileQueue(list): """ List of TileRequest objects, that's sensitive to when they're loaded. """ def __getslice__(self, i, j): """ Return a TileQueue when a list slice is called-for. Python docs say that __getslice__ is deprecated, but its replacement __getitem__ doesn't seem to be doing anything. """ other = TileQueue() for t in range(i, j): if t < len(self): other.append(self[t]) return other def pending(self): """ True if any contained tile is still loading. """ remaining = [tile for tile in self if not tile.loaded()] return len(remaining) > 0 class Map: def __init__(self, provider, dimensions, coordinate, offset): """ Instance of a map intended for drawing to an image. provider Instance of IMapProvider dimensions Size of output image, instance of Point coordinate Base tile, instance of Coordinate offset Position of base tile relative to map center, instance of Point """ self.provider = provider self.dimensions = dimensions self.coordinate = coordinate self.offset = offset def __str__(self): return 'Map(%(provider)s, %(dimensions)s, %(coordinate)s, %(offset)s)' % self.__dict__ def locationPoint(self, location): """ Return an x, y point on the map image for a given geographical location. """ point = Core.Point(self.offset.x, self.offset.y) coord = self.provider.locationCoordinate(location).zoomTo(self.coordinate.zoom) # distance from the known coordinate offset point.x += self.provider.tileWidth() * (coord.column - self.coordinate.column) point.y += self.provider.tileHeight() * (coord.row - self.coordinate.row) # because of the center/corner business point.x += self.dimensions.x/2 point.y += self.dimensions.y/2 return point def pointLocation(self, point): """ Return a geographical location on the map image for a given x, y point. """ hizoomCoord = self.coordinate.zoomTo(Core.Coordinate.MAX_ZOOM) # because of the center/corner business point = Core.Point(point.x - self.dimensions.x/2, point.y - self.dimensions.y/2) # distance in tile widths from reference tile to point xTiles = (point.x - self.offset.x) / self.provider.tileWidth(); yTiles = (point.y - self.offset.y) / self.provider.tileHeight(); # distance in rows & columns at maximum zoom xDistance = xTiles * math.pow(2, (Core.Coordinate.MAX_ZOOM - self.coordinate.zoom)); yDistance = yTiles * math.pow(2, (Core.Coordinate.MAX_ZOOM - self.coordinate.zoom)); # new point coordinate reflecting that distance coord = Core.Coordinate(round(hizoomCoord.row + yDistance), round(hizoomCoord.column + xDistance), hizoomCoord.zoom) coord = coord.zoomTo(self.coordinate.zoom) location = self.provider.coordinateLocation(coord) return location # def draw_bbox(self, bbox, zoom=16, verbose=False) : sw = Geo.Location(bbox[0], bbox[1]) ne = Geo.Location(bbox[2], bbox[3]) nw = Geo.Location(ne.lat, sw.lon) se = Geo.Location(sw.lat, ne.lon) TL = self.provider.locationCoordinate(nw).zoomTo(zoom) # tiles = TileQueue() cur_lon = sw.lon cur_lat = ne.lat max_lon = ne.lon max_lat = sw.lat x_off = 0 y_off = 0 tile_x = 0 tile_y = 0 tileCoord = TL.copy() while cur_lon < max_lon : y_off = 0 tile_y = 0 while cur_lat > max_lat : tiles.append(TileRequest(self.provider, tileCoord, Core.Point(x_off, y_off))) y_off += self.provider.tileHeight() tileCoord = tileCoord.down() loc = self.provider.coordinateLocation(tileCoord) cur_lat = loc.lat tile_y += 1 x_off += self.provider.tileWidth() cur_lat = ne.lat tile_x += 1 tileCoord = TL.copy().right(tile_x) loc = self.provider.coordinateLocation(tileCoord) cur_lon = loc.lon width = int(self.provider.tileWidth() * tile_x) height = int(self.provider.tileHeight() * tile_y) # Quick, look over there! coord, offset = calculateMapExtent(self.provider, width, height, Geo.Location(bbox[0], bbox[1]), Geo.Location(bbox[2], bbox[3])) self.offset = offset self.coordinates = coord 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. """ 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() for x in range(corner.x, self.dimensions.x, self.provider.tileWidth()): tiles.append(TileRequest(self.provider, tileCoord, Core.Point(x, y))) tileCoord = tileCoord.right() rowCoord = rowCoord.down() return self.render_tiles(tiles, self.dimensions.x, self.dimensions.y, verbose, fatbits_ok) # def render_tiles(self, tiles, img_width, img_height, 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) mapImg = Image.new('RGB', (img_width, img_height)) for tile in tiles: try: for img in tile.images(): mapImg.paste(img, (tile.offset.x, tile.offset.y), img) except: # something failed to paste, so we ignore it pass return mapImg if __name__ == '__main__': import doctest doctest.testmod() ModestMaps-1.4.6/ModestMaps/BlueMarble.py0000664000076500000240000000175411407276763021241 0ustar migurskistaff00000000000000""" >>> p = Provider() >>> p.getTileUrls(Coordinate(10, 13, 7)) ('http://s3.amazonaws.com/com.modestmaps.bluemarble/7-r10-c13.jpg',) >>> p.getTileUrls(Coordinate(13, 10, 7)) ('http://s3.amazonaws.com/com.modestmaps.bluemarble/7-r13-c10.jpg',) """ from math import pi from Core import Coordinate from Geo import MercatorProjection, deriveTransformation from Providers import IMapProvider import Tiles class Provider(IMapProvider): def __init__(self): # the spherical mercator world tile covers (-π, -π) to (π, π) t = deriveTransformation(-pi, pi, 0, 0, pi, pi, 1, 0, -pi, -pi, 0, 1) self.projection = MercatorProjection(0, t) def tileWidth(self): return 256 def tileHeight(self): return 256 def getTileUrls(self, coordinate): return ('http://s3.amazonaws.com/com.modestmaps.bluemarble/%d-r%d-c%d.jpg' % (coordinate.zoom, coordinate.row, coordinate.column),) if __name__ == '__main__': import doctest doctest.testmod() ModestMaps-1.4.6/ModestMaps/CloudMade.py0000664000076500000240000000464311407276763021064 0ustar migurskistaff00000000000000""" >>> p = OriginalProvider('example') >>> p.getTileUrls(Coordinate(25322, 10507, 16)) #doctest: +ELLIPSIS ('http://tile.cloudmade.com/example/1/256/16/10507/25322.png',) >>> p = FineLineProvider('example') >>> p.getTileUrls(Coordinate(25322, 10507, 16)) #doctest: +ELLIPSIS ('http://tile.cloudmade.com/example/2/256/16/10507/25322.png',) >>> p = TouristProvider('example') >>> p.getTileUrls(Coordinate(25322, 10507, 16)) #doctest: +ELLIPSIS ('http://tile.cloudmade.com/example/7/256/16/10507/25322.png',) >>> p = FreshProvider('example') >>> p.getTileUrls(Coordinate(25322, 10507, 16)) #doctest: +ELLIPSIS ('http://tile.cloudmade.com/example/997/256/16/10507/25322.png',) >>> p = PaleDawnProvider('example') >>> p.getTileUrls(Coordinate(25322, 10507, 16)) #doctest: +ELLIPSIS ('http://tile.cloudmade.com/example/998/256/16/10507/25322.png',) >>> p = MidnightCommanderProvider('example') >>> p.getTileUrls(Coordinate(25322, 10507, 16)) #doctest: +ELLIPSIS ('http://tile.cloudmade.com/example/999/256/16/10507/25322.png',) >>> p = BaseProvider('example', 510) >>> p.getTileUrls(Coordinate(25322, 10507, 16)) #doctest: +ELLIPSIS ('http://tile.cloudmade.com/example/510/256/16/10507/25322.png',) """ from math import pi from Core import Coordinate from Geo import MercatorProjection, deriveTransformation from Providers import IMapProvider import random, Tiles class BaseProvider(IMapProvider): def __init__(self, apikey, style=None): # the spherical mercator world tile covers (-π, -π) to (π, π) t = deriveTransformation(-pi, pi, 0, 0, pi, pi, 1, 0, -pi, -pi, 0, 1) self.projection = MercatorProjection(0, t) self.key = apikey if style: self.style = style def tileWidth(self): return 256 def tileHeight(self): return 256 def getTileUrls(self, coordinate): zoom, column, row = coordinate.zoom, coordinate.column, coordinate.row return ('http://tile.cloudmade.com/%s/%d/256/%d/%d/%d.png' % (self.key, self.style, zoom, column, row),) class OriginalProvider(BaseProvider): style = 1 class FineLineProvider(BaseProvider): style = 2 class TouristProvider(BaseProvider): style = 7 class FreshProvider(BaseProvider): style = 997 class PaleDawnProvider(BaseProvider): style = 998 class MidnightCommanderProvider(BaseProvider): style = 999 if __name__ == '__main__': import doctest doctest.testmod() ModestMaps-1.4.6/ModestMaps/Core.py0000664000076500000240000000446611341335144020104 0ustar migurskistaff00000000000000""" >>> p = Point(0, 1) >>> p (0.000, 1.000) >>> p.x 0 >>> p.y 1 >>> c = Coordinate(0, 1, 2) >>> c (0.000, 1.000 @2.000) >>> c.row 0 >>> c.column 1 >>> c.zoom 2 >>> c.zoomTo(3) (0.000, 2.000 @3.000) >>> c.zoomTo(1) (0.000, 0.500 @1.000) >>> c.up() (-1.000, 1.000 @2.000) >>> c.right() (0.000, 2.000 @2.000) >>> c.down() (1.000, 1.000 @2.000) >>> c.left() (0.000, 0.000 @2.000) """ import math class Point: def __init__(self, x, y): self.x = x self.y = y def __repr__(self): return '(%(x).3f, %(y).3f)' % self.__dict__ class Coordinate: MAX_ZOOM = 25 def __init__(self, row, column, zoom): self.row = row self.column = column self.zoom = zoom def __repr__(self): return '(%(row).3f, %(column).3f @%(zoom).3f)' % self.__dict__ def __eq__(self, other): return self.zoom == other.zoom and self.row == other.row and self.column == other.column def __cmp__(self, other): return cmp((self.zoom, self.row, self.column), (other.zoom, other.row, other.column)) def __hash__(self): return hash(('Coordinate', self.row, self.column, self.zoom)) def copy(self): return self.__class__(self.row, self.column, self.zoom) def container(self): return self.__class__(math.floor(self.row), math.floor(self.column), self.zoom) def zoomTo(self, destination): return self.__class__(self.row * math.pow(2, destination - self.zoom), self.column * math.pow(2, destination - self.zoom), destination) def zoomBy(self, distance): return self.__class__(self.row * math.pow(2, distance), self.column * math.pow(2, distance), self.zoom + distance) def up(self, distance=1): return self.__class__(self.row - distance, self.column, self.zoom) def right(self, distance=1): return self.__class__(self.row, self.column + distance, self.zoom) def down(self, distance=1): return self.__class__(self.row + distance, self.column, self.zoom) def left(self, distance=1): return self.__class__(self.row, self.column - distance, self.zoom) if __name__ == '__main__': import doctest doctest.testmod() ModestMaps-1.4.6/ModestMaps/Geo.py0000664000076500000240000001132211341335144017713 0ustar migurskistaff00000000000000""" >>> t = Transformation(1, 0, 0, 0, 1, 0) >>> p = Point(1, 1) >>> p (1.000, 1.000) >>> p_ = t.transform(p) >>> p_ (1.000, 1.000) >>> p__ = t.untransform(p_) >>> p__ (1.000, 1.000) >>> t = Transformation(0, 1, 0, 1, 0, 0) >>> p = Point(0, 1) >>> p (0.000, 1.000) >>> p_ = t.transform(p) >>> p_ (1.000, 0.000) >>> p__ = t.untransform(p_) >>> p__ (0.000, 1.000) >>> t = Transformation(1, 0, 1, 0, 1, 1) >>> p = Point(0, 0) >>> p (0.000, 0.000) >>> p_ = t.transform(p) >>> p_ (1.000, 1.000) >>> p__ = t.untransform(p_) >>> p__ (0.000, 0.000) >>> m = MercatorProjection(10) >>> m.locationCoordinate(Location(0, 0)) (-0.000, 0.000 @10.000) >>> m.coordinateLocation(Coordinate(0, 0, 10)) (0.000, 0.000) >>> m.locationCoordinate(Location(37, -122)) (0.696, -2.129 @10.000) >>> m.coordinateLocation(Coordinate(0.696, -2.129, 10.000)) (37.001, -121.983) """ import math from Core import Point, Coordinate class Location: def __init__(self, lat, lon): self.lat = lat self.lon = lon def __repr__(self): return '(%(lat).3f, %(lon).3f)' % self.__dict__ class Transformation: def __init__(self, ax, bx, cx, ay, by, cy): self.ax = ax self.bx = bx self.cx = cx self.ay = ay self.by = by self.cy = cy def transform(self, point): return Point(self.ax*point.x + self.bx*point.y + self.cx, self.ay*point.x + self.by*point.y + self.cy) def untransform(self, point): return Point((point.x*self.by - point.y*self.bx - self.cx*self.by + self.cy*self.bx) / (self.ax*self.by - self.ay*self.bx), (point.x*self.ay - point.y*self.ax - self.cx*self.ay + self.cy*self.ax) / (self.bx*self.ay - self.by*self.ax)) def deriveTransformation(a1x, a1y, a2x, a2y, b1x, b1y, b2x, b2y, c1x, c1y, c2x, c2y): """ Generates a transform based on three pairs of points, a1 -> a2, b1 -> b2, c1 -> c2. """ ax, bx, cx = linearSolution(a1x, a1y, a2x, b1x, b1y, b2x, c1x, c1y, c2x) ay, by, cy = linearSolution(a1x, a1y, a2y, b1x, b1y, b2y, c1x, c1y, c2y) return Transformation(ax, bx, cx, ay, by, cy) def linearSolution(r1, s1, t1, r2, s2, t2, r3, s3, t3): """ Solves a system of linear equations. t1 = (a * r1) + (b + s1) + c t2 = (a * r2) + (b + s2) + c t3 = (a * r3) + (b + s3) + c r1 - t3 are the known values. a, b, c are the unknowns to be solved. returns the a, b, c coefficients. """ # make them all floats r1, s1, t1, r2, s2, t2, r3, s3, t3 = map(float, (r1, s1, t1, r2, s2, t2, r3, s3, t3)) a = (((t2 - t3) * (s1 - s2)) - ((t1 - t2) * (s2 - s3))) \ / (((r2 - r3) * (s1 - s2)) - ((r1 - r2) * (s2 - s3))) b = (((t2 - t3) * (r1 - r2)) - ((t1 - t2) * (r2 - r3))) \ / (((s2 - s3) * (r1 - r2)) - ((s1 - s2) * (r2 - r3))) c = t1 - (r1 * a) - (s1 * b) return a, b, c class IProjection: def __init__(self, zoom, transformation=Transformation(1, 0, 0, 0, 1, 0)): self.zoom = zoom self.transformation = transformation def rawProject(self, point): raise NotImplementedError("Abstract method not implemented by subclass.") def rawUnproject(self, point): raise NotImplementedError("Abstract method not implemented by subclass.") def project(self, point): point = self.rawProject(point) if(self.transformation): point = self.transformation.transform(point) return point def unproject(self, point): if(self.transformation): point = self.transformation.untransform(point) point = self.rawUnproject(point) return point def locationCoordinate(self, location): point = Point(math.pi * location.lon / 180.0, math.pi * location.lat / 180.0) point = self.project(point) return Coordinate(point.y, point.x, self.zoom) def coordinateLocation(self, coordinate): coordinate = coordinate.zoomTo(self.zoom) point = Point(coordinate.column, coordinate.row) point = self.unproject(point) return Location(180.0 * point.y / math.pi, 180.0 * point.x / math.pi) class LinearProjection(IProjection): def rawProject(self, point): return Point(point.x, point.y) def rawUnproject(self, point): return Point(point.x, point.y) class MercatorProjection(IProjection): def rawProject(self, point): return Point(point.x, math.log(math.tan(0.25 * math.pi + 0.5 * point.y))) def rawUnproject(self, point): return Point(point.x, 2 * math.atan(math.pow(math.e, point.y)) - 0.5 * math.pi) if __name__ == '__main__': import doctest doctest.testmod() ModestMaps-1.4.6/ModestMaps/MapQuest.py0000664000076500000240000000301411567004774020753 0ustar migurskistaff00000000000000""" >>> p = RoadProvider() >>> p.getTileUrls(Coordinate(10, 13, 7)) #doctest: +ELLIPSIS ('http://otile....mqcdn.com/tiles/1.0.0/7/13/10.png',) >>> p.getTileUrls(Coordinate(13, 10, 7)) #doctest: +ELLIPSIS ('http://otile....mqcdn.com/tiles/1.0.0/7/10/13.png',) >>> p = AerialProvider() >>> p.getTileUrls(Coordinate(10, 13, 7)) #doctest: +ELLIPSIS ('http://oatile....mqcdn.com/naip/7/13/10.png',) >>> p.getTileUrls(Coordinate(13, 10, 7)) #doctest: +ELLIPSIS ('http://oatile....mqcdn.com/naip/7/10/13.png',) """ from math import pi from Core import Coordinate from Geo import MercatorProjection, deriveTransformation from Providers import IMapProvider import random, Tiles class AbstractProvider(IMapProvider): def __init__(self): # the spherical mercator world tile covers (-π, -π) to (π, π) t = deriveTransformation(-pi, pi, 0, 0, pi, pi, 1, 0, -pi, -pi, 0, 1) self.projection = MercatorProjection(0, t) def tileWidth(self): return 256 def tileHeight(self): return 256 class RoadProvider(AbstractProvider): def getTileUrls(self, coordinate): return ('http://otile%d.mqcdn.com/tiles/1.0.0/%d/%d/%d.png' % (random.randint(1, 4), coordinate.zoom, coordinate.column, coordinate.row),) class AerialProvider(AbstractProvider): def getTileUrls(self, coordinate): return ('http://oatile%d.mqcdn.com/naip/%d/%d/%d.png' % (random.randint(1, 4), coordinate.zoom, coordinate.column, coordinate.row),) if __name__ == '__main__': import doctest doctest.testmod() ModestMaps-1.4.6/ModestMaps/Microsoft.py0000664000076500000240000000463411407276763021174 0ustar migurskistaff00000000000000""" >>> p = RoadProvider() >>> p.getTileUrls(Coordinate(25322, 10507, 16)) #doctest: +ELLIPSIS ('http://r....ortho.tiles.virtualearth.net/tiles/r0230102122203031.png?g=90&shading=hill',) >>> p.getTileUrls(Coordinate(25333, 10482, 16)) #doctest: +ELLIPSIS ('http://r....ortho.tiles.virtualearth.net/tiles/r0230102033330212.png?g=90&shading=hill',) >>> p = AerialProvider() >>> p.getTileUrls(Coordinate(25322, 10507, 16)) #doctest: +ELLIPSIS ('http://a....ortho.tiles.virtualearth.net/tiles/a0230102122203031.jpeg?g=90',) >>> p.getTileUrls(Coordinate(25333, 10482, 16)) #doctest: +ELLIPSIS ('http://a....ortho.tiles.virtualearth.net/tiles/a0230102033330212.jpeg?g=90',) >>> p = HybridProvider() >>> p.getTileUrls(Coordinate(25322, 10507, 16)) #doctest: +ELLIPSIS ('http://h....ortho.tiles.virtualearth.net/tiles/h0230102122203031.jpeg?g=90',) >>> p.getTileUrls(Coordinate(25333, 10482, 16)) #doctest: +ELLIPSIS ('http://h....ortho.tiles.virtualearth.net/tiles/h0230102033330212.jpeg?g=90',) """ from math import pi from Core import Coordinate from Geo import MercatorProjection, deriveTransformation from Providers import IMapProvider import random, Tiles class AbstractProvider(IMapProvider): def __init__(self): # the spherical mercator world tile covers (-π, -π) to (π, π) t = deriveTransformation(-pi, pi, 0, 0, pi, pi, 1, 0, -pi, -pi, 0, 1) self.projection = MercatorProjection(0, t) def getZoomString(self, coordinate): return Tiles.toMicrosoft(int(coordinate.column), int(coordinate.row), int(coordinate.zoom)) def tileWidth(self): return 256 def tileHeight(self): return 256 class RoadProvider(AbstractProvider): def getTileUrls(self, coordinate): return ('http://r%d.ortho.tiles.virtualearth.net/tiles/r%s.png?g=90&shading=hill' % (random.randint(0, 3), self.getZoomString(self.sourceCoordinate(coordinate))),) class AerialProvider(AbstractProvider): def getTileUrls(self, coordinate): return ('http://a%d.ortho.tiles.virtualearth.net/tiles/a%s.jpeg?g=90' % (random.randint(0, 3), self.getZoomString(self.sourceCoordinate(coordinate))),) class HybridProvider(AbstractProvider): def getTileUrls(self, coordinate): return ('http://h%d.ortho.tiles.virtualearth.net/tiles/h%s.jpeg?g=90' % (random.randint(0, 3), self.getZoomString(self.sourceCoordinate(coordinate))),) if __name__ == '__main__': import doctest doctest.testmod() ModestMaps-1.4.6/ModestMaps/OpenStreetMap.py0000664000076500000240000000165211407276763021752 0ustar migurskistaff00000000000000""" >>> p = Provider() >>> p.getTileUrls(Coordinate(10, 13, 7)) ('http://tile.openstreetmap.org/7/13/10.png',) >>> p.getTileUrls(Coordinate(13, 10, 7)) ('http://tile.openstreetmap.org/7/10/13.png',) """ from math import pi from Core import Coordinate from Geo import MercatorProjection, deriveTransformation from Providers import IMapProvider import Tiles class Provider(IMapProvider): def __init__(self): # the spherical mercator world tile covers (-π, -π) to (π, π) t = deriveTransformation(-pi, pi, 0, 0, pi, pi, 1, 0, -pi, -pi, 0, 1) self.projection = MercatorProjection(0, t) def tileWidth(self): return 256 def tileHeight(self): return 256 def getTileUrls(self, coordinate): return ('http://tile.openstreetmap.org/%d/%d/%d.png' % (coordinate.zoom, coordinate.column, coordinate.row),) if __name__ == '__main__': import doctest doctest.testmod() ModestMaps-1.4.6/ModestMaps/Providers.py0000664000076500000240000000525011720256624021167 0ustar migurskistaff00000000000000import re from math import pi, pow from Core import Coordinate from Geo import LinearProjection, MercatorProjection, deriveTransformation ids = ('MICROSOFT_ROAD', 'MICROSOFT_AERIAL', 'MICROSOFT_HYBRID', 'YAHOO_ROAD', 'YAHOO_AERIAL', 'YAHOO_HYBRID', 'BLUE_MARBLE', 'OPEN_STREET_MAP') class IMapProvider: def __init__(self): raise NotImplementedError("Abstract method not implemented by subclass.") def getTileUrls(self, coordinate): raise NotImplementedError("Abstract method not implemented by subclass.") def getTileUrls(self, coordinate): raise NotImplementedError("Abstract method not implemented by subclass.") def tileWidth(self): raise NotImplementedError("Abstract method not implemented by subclass.") def tileHeight(self): raise NotImplementedError("Abstract method not implemented by subclass.") def locationCoordinate(self, location): return self.projection.locationCoordinate(location) def coordinateLocation(self, location): return self.projection.coordinateLocation(location) def sourceCoordinate(self, coordinate): raise NotImplementedError("Abstract method not implemented by subclass.") def sourceCoordinate(self, coordinate): wrappedColumn = coordinate.column % pow(2, coordinate.zoom) while wrappedColumn < 0: wrappedColumn += pow(2, coordinate.zoom) return Coordinate(coordinate.row, wrappedColumn, coordinate.zoom) 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 """ def __init__(self, template): # the spherical mercator world tile covers (-π, -π) to (π, π) t = deriveTransformation(-pi, pi, 0, 0, pi, pi, 1, 0, -pi, -pi, 0, 1) self.projection = MercatorProjection(0, t) self.templates = [] while template: match = re.match(r'^((http|https|file)://\S+?)(,(http|https|file)://\S+)?$', template) first = match.group(1) if match: self.templates.append(first) template = template[len(first):].lstrip(',') else: break def tileWidth(self): return 256 def tileHeight(self): return 256 def getTileUrls(self, coordinate): x, y, z = str(int(coordinate.column)), str(int(coordinate.row)), str(int(coordinate.zoom)) return [t.replace('{X}', x).replace('{Y}', y).replace('{Z}', z) for t in self.templates] ModestMaps-1.4.6/ModestMaps/Stamen.py0000664000076500000240000000337012221213300020417 0ustar migurskistaff00000000000000""" >>> p = BaseProvider('toner') >>> p.getTileUrls(Coordinate(25322, 10507, 16)) #doctest: +ELLIPSIS ('http://tile.stamen.com/toner/16/10507/25322.png',) >>> p = TonerProvider() >>> p.getTileUrls(Coordinate(25322, 10507, 16)) #doctest: +ELLIPSIS ('http://tile.stamen.com/toner/16/10507/25322.png',) >>> p = TerrainProvider() >>> p.getTileUrls(Coordinate(25322, 10507, 16)) #doctest: +ELLIPSIS ('http://tile.stamen.com/terrain/16/10507/25322.png',) >>> p = WatercolorProvider() >>> p.getTileUrls(Coordinate(25322, 10507, 16)) #doctest: +ELLIPSIS ('http://tile.stamen.com/watercolor/16/10507/25322.png',) """ from math import pi from Core import Coordinate from Geo import MercatorProjection, deriveTransformation from Providers import IMapProvider import random, Tiles class BaseProvider(IMapProvider): def __init__(self, style): # the spherical mercator world tile covers (-π, -π) to (π, π) t = deriveTransformation(-pi, pi, 0, 0, pi, pi, 1, 0, -pi, -pi, 0, 1) self.projection = MercatorProjection(0, t) self.style = style def tileWidth(self): return 256 def tileHeight(self): return 256 def getTileUrls(self, coordinate): zoom, column, row = coordinate.zoom, coordinate.column, coordinate.row return ('http://tile.stamen.com/%s/%d/%d/%d.png' % (self.style, zoom, column, row),) class TonerProvider(BaseProvider): def __init__(self): BaseProvider.__init__(self, 'toner') class TerrainProvider(BaseProvider): def __init__(self): BaseProvider.__init__(self, 'terrain') class WatercolorProvider(BaseProvider): def __init__(self): BaseProvider.__init__(self, 'watercolor') if __name__ == '__main__': import doctest doctest.testmod() ModestMaps-1.4.6/ModestMaps/Tiles.py0000664000076500000240000001011611341335144020261 0ustar migurskistaff00000000000000""" >>> toBinaryString(1) '1' >>> toBinaryString(2) '10' >>> toBinaryString(3) '11' >>> toBinaryString(4) '100' >>> fromBinaryString('1') 1 >>> fromBinaryString('11') 3 >>> fromBinaryString('101') 5 >>> fromBinaryString('1001') 9 >>> fromYahooRoad(0, 0, 17) (0, 0, 1) >>> fromYahooRoad(10507, 7445, 2) (10507, 25322, 16) >>> fromYahooRoad(10482, 7434, 2) (10482, 25333, 16) >>> toYahooRoad(0, 0, 1) (0, 0, 17) >>> toYahooRoad(10507, 25322, 16) (10507, 7445, 2) >>> toYahooRoad(10482, 25333, 16) (10482, 7434, 2) >>> fromYahooAerial(0, 0, 17) (0, 0, 1) >>> fromYahooAerial(10507, 7445, 2) (10507, 25322, 16) >>> fromYahooAerial(10482, 7434, 2) (10482, 25333, 16) >>> toYahooAerial(0, 0, 1) (0, 0, 17) >>> toYahooAerial(10507, 25322, 16) (10507, 7445, 2) >>> toYahooAerial(10482, 25333, 16) (10482, 7434, 2) >>> fromMicrosoftRoad('0') (0, 0, 1) >>> fromMicrosoftRoad('0230102122203031') (10507, 25322, 16) >>> fromMicrosoftRoad('0230102033330212') (10482, 25333, 16) >>> toMicrosoftRoad(0, 0, 1) '0' >>> toMicrosoftRoad(10507, 25322, 16) '0230102122203031' >>> toMicrosoftRoad(10482, 25333, 16) '0230102033330212' >>> fromMicrosoftAerial('0') (0, 0, 1) >>> fromMicrosoftAerial('0230102122203031') (10507, 25322, 16) >>> fromMicrosoftAerial('0230102033330212') (10482, 25333, 16) >>> toMicrosoftAerial(0, 0, 1) '0' >>> toMicrosoftAerial(10507, 25322, 16) '0230102122203031' >>> toMicrosoftAerial(10482, 25333, 16) '0230102033330212' """ import math octalStrings = ('000', '001', '010', '011', '100', '101', '110', '111') def toBinaryString(i): """ Return a binary string for an integer. """ return ''.join([octalStrings[int(c)] for c in oct(i)]).lstrip('0') def fromBinaryString(s): """ Return an integer for a binary string. """ s = list(s) e = 0 i = 0 while(len(s)): if(s[-1]) == '1': i += int(math.pow(2, e)) e += 1 s.pop() return i def fromYahoo(x, y, z): """ Return column, row, zoom for Yahoo x, y, z. """ zoom = 18 - z row = int(math.pow(2, zoom - 1) - y - 1) col = x return col, row, zoom def toYahoo(col, row, zoom): """ Return x, y, z for Yahoo tile column, row, zoom. """ x = col y = int(math.pow(2, zoom - 1) - row - 1) z = 18 - zoom return x, y, z def fromYahooRoad(x, y, z): """ Return column, row, zoom for Yahoo Road tile x, y, z. """ return fromYahoo(x, y, z) def toYahooRoad(col, row, zoom): """ Return x, y, z for Yahoo Road tile column, row, zoom. """ return toYahoo(col, row, zoom) def fromYahooAerial(x, y, z): """ Return column, row, zoom for Yahoo Aerial tile x, y, z. """ return fromYahoo(x, y, z) def toYahooAerial(col, row, zoom): """ Return x, y, z for Yahoo Aerial tile column, row, zoom. """ return toYahoo(col, row, zoom) microsoftFromCorners = {'0': '00', '1': '01', '2': '10', '3': '11'} microsoftToCorners = {'00': '0', '01': '1', '10': '2', '11': '3'} def fromMicrosoft(s): """ Return column, row, zoom for Microsoft tile string. """ row, col = map(fromBinaryString, zip(*[list(microsoftFromCorners[c]) for c in s])) zoom = len(s) return col, row, zoom def toMicrosoft(col, row, zoom): """ Return string for Microsoft tile column, row, zoom. """ x = col y = row y, x = toBinaryString(y).rjust(zoom, '0'), toBinaryString(x).rjust(zoom, '0') string = ''.join([microsoftToCorners[y[c]+x[c]] for c in range(zoom)]) return string def fromMicrosoftRoad(s): """ Return column, row, zoom for Microsoft Road tile string. """ return fromMicrosoft(s) def toMicrosoftRoad(col, row, zoom): """ Return x, y, z for Microsoft Road tile column, row, zoom. """ return toMicrosoft(col, row, zoom) def fromMicrosoftAerial(s): """ Return column, row, zoom for Microsoft Aerial tile string. """ return fromMicrosoft(s) def toMicrosoftAerial(col, row, zoom): """ Return x, y, z for Microsoft Aerial tile column, row, zoom. """ return toMicrosoft(col, row, zoom) if __name__ == '__main__': import doctest doctest.testmod() ModestMaps-1.4.6/ModestMaps/VERSION0000664000076500000240000000000612333464776017714 0ustar migurskistaff000000000000001.4.6 ModestMaps-1.4.6/ModestMaps/Yahoo.py0000664000076500000240000000531311407276763020301 0ustar migurskistaff00000000000000""" >>> p = RoadProvider() >>> p.getTileUrls(Coordinate(25322, 10507, 16)) #doctest: +ELLIPSIS ('http://us.maps2.yimg.com/us.png.maps.yimg.com/png?v=...&t=m&x=10507&y=7445&z=2',) >>> p.getTileUrls(Coordinate(25333, 10482, 16)) #doctest: +ELLIPSIS ('http://us.maps2.yimg.com/us.png.maps.yimg.com/png?v=...&t=m&x=10482&y=7434&z=2',) >>> p = AerialProvider() >>> p.getTileUrls(Coordinate(25322, 10507, 16)) #doctest: +ELLIPSIS ('http://us.maps3.yimg.com/aerial.maps.yimg.com/tile?v=...&t=a&x=10507&y=7445&z=2',) >>> p.getTileUrls(Coordinate(25333, 10482, 16)) #doctest: +ELLIPSIS ('http://us.maps3.yimg.com/aerial.maps.yimg.com/tile?v=...&t=a&x=10482&y=7434&z=2',) >>> p = HybridProvider() >>> p.getTileUrls(Coordinate(25322, 10507, 16)) #doctest: +ELLIPSIS ('http://us.maps3.yimg.com/aerial.maps.yimg.com/tile?v=...&t=a&x=10507&y=7445&z=2', 'http://us.maps3.yimg.com/aerial.maps.yimg.com/png?v=...&t=h&x=10507&y=7445&z=2') >>> p.getTileUrls(Coordinate(25333, 10482, 16)) #doctest: +ELLIPSIS ('http://us.maps3.yimg.com/aerial.maps.yimg.com/tile?v=...&t=a&x=10482&y=7434&z=2', 'http://us.maps3.yimg.com/aerial.maps.yimg.com/png?v=...&t=h&x=10482&y=7434&z=2') """ from math import pi from Core import Coordinate from Geo import MercatorProjection, deriveTransformation from Providers import IMapProvider import Tiles ROAD_VERSION = '3.52' AERIAL_VERSION = '1.7' HYBRID_VERSION = '2.2' class AbstractProvider(IMapProvider): def __init__(self): # the spherical mercator world tile covers (-π, -π) to (π, π) t = deriveTransformation(-pi, pi, 0, 0, pi, pi, 1, 0, -pi, -pi, 0, 1) self.projection = MercatorProjection(0, t) def getZoomString(self, coordinate): return 'x=%d&y=%d&z=%d' % Tiles.toYahoo(int(coordinate.column), int(coordinate.row), int(coordinate.zoom)) def tileWidth(self): return 256 def tileHeight(self): return 256 class RoadProvider(AbstractProvider): def getTileUrls(self, coordinate): return ('http://us.maps2.yimg.com/us.png.maps.yimg.com/png?v=%s&t=m&%s' % (ROAD_VERSION, self.getZoomString(self.sourceCoordinate(coordinate))),) class AerialProvider(AbstractProvider): def getTileUrls(self, coordinate): return ('http://us.maps3.yimg.com/aerial.maps.yimg.com/tile?v=%s&t=a&%s' % (AERIAL_VERSION, self.getZoomString(self.sourceCoordinate(coordinate))),) class HybridProvider(AbstractProvider): def getTileUrls(self, coordinate): under = AerialProvider().getTileUrls(coordinate)[0] over = 'http://us.maps3.yimg.com/aerial.maps.yimg.com/png?v=%s&t=h&%s' % (HYBRID_VERSION, self.getZoomString(self.sourceCoordinate(coordinate))) return (under, over) if __name__ == '__main__': import doctest doctest.testmod() ModestMaps-1.4.6/PKG-INFO0000664000076500000240000000035012333467175015663 0ustar migurskistaff00000000000000Metadata-Version: 1.1 Name: ModestMaps Version: 1.4.6 Summary: Modest Maps python port Home-page: http://modestmaps.com Author: Michal Migurski Author-email: UNKNOWN License: BSD Description: UNKNOWN Platform: UNKNOWN Requires: PIL ModestMaps-1.4.6/setup.py0000664000076500000240000000061712333466531016301 0ustar migurskistaff00000000000000#!/usr/bin/env python from distutils.core import setup version = open('ModestMaps/VERSION', 'r').read().strip() setup(name='ModestMaps', version=version, description='Modest Maps python port', author='Michal Migurski', url='http://modestmaps.com', requires=['PIL'], packages=['ModestMaps'], package_data={'ModestMaps': ['VERSION']}, license='BSD')