| 1 |
import sys, math, random, tile_params, PIL.Image, urllib, StringIO, time, optparse |
|---|
| 2 |
|
|---|
| 3 |
def deg2rad(deg): |
|---|
| 4 |
return deg * math.pi / 180 |
|---|
| 5 |
|
|---|
| 6 |
def project(lat, lon): |
|---|
| 7 |
x = lon |
|---|
| 8 |
y = math.log((1 + math.sin(lat)) / (1 - math.sin(lat))) / 2 |
|---|
| 9 |
return x / math.pi, y / math.pi |
|---|
| 10 |
|
|---|
| 11 |
def locationCoord(lat, lon, zoom=0): |
|---|
| 12 |
x, y = project(deg2rad(lat), deg2rad(lon)) |
|---|
| 13 |
|
|---|
| 14 |
row = math.pow(2, zoom) * ((1 - y) / 2) |
|---|
| 15 |
col = math.pow(2, zoom) * ((x + 1) / 2) |
|---|
| 16 |
|
|---|
| 17 |
return row, col, zoom |
|---|
| 18 |
|
|---|
| 19 |
parser = optparse.OptionParser() |
|---|
| 20 |
|
|---|
| 21 |
parser.add_option('-o', '--out', dest='outfile', |
|---|
| 22 |
help='Write to output file') |
|---|
| 23 |
|
|---|
| 24 |
parser.add_option('-z', '--zoom', dest='zoomlevel', |
|---|
| 25 |
help='Zoom level', type='int') |
|---|
| 26 |
|
|---|
| 27 |
parser.add_option('-b', '--bounds', dest='bounds', |
|---|
| 28 |
help='Lat/long bounding box', type='float', nargs=4) |
|---|
| 29 |
|
|---|
| 30 |
parser.add_option('-p', '--provider', dest='provider', |
|---|
| 31 |
help='Map Provider', choices=('microsoft-road', 'microsoft-hybrid', 'microsoft-aerial')) |
|---|
| 32 |
|
|---|
| 33 |
(options, args) = parser.parse_args() |
|---|
| 34 |
|
|---|
| 35 |
if __name__ == '__main__': |
|---|
| 36 |
|
|---|
| 37 |
#print locationCoord(85, -180, 0) |
|---|
| 38 |
#print locationCoord(0, 0, 0) |
|---|
| 39 |
#print locationCoord(-85, 180, 0) |
|---|
| 40 |
#sys.exit() |
|---|
| 41 |
|
|---|
| 42 |
zoom = options.zoomlevel |
|---|
| 43 |
|
|---|
| 44 |
#print math.pow(2, zoom) |
|---|
| 45 |
|
|---|
| 46 |
minRow, minCol, minZoom = map(int, map(math.floor, locationCoord(options.bounds[0], options.bounds[1], zoom))) |
|---|
| 47 |
maxRow, maxCol, maxZoom = map(int, map(math.ceil, locationCoord(options.bounds[2], options.bounds[3], zoom))) |
|---|
| 48 |
|
|---|
| 49 |
rows = (maxRow - minRow) |
|---|
| 50 |
cols = (maxCol - minCol) |
|---|
| 51 |
|
|---|
| 52 |
print 'Allocating...', rows, 'x', cols |
|---|
| 53 |
destImg = PIL.Image.new('RGB', (256 * cols, 256 * rows)) |
|---|
| 54 |
|
|---|
| 55 |
for row in range(minRow, maxRow): |
|---|
| 56 |
for col in range(minCol, maxCol): |
|---|
| 57 |
destY = (row - minRow) * 256 |
|---|
| 58 |
destX = (col - minCol) * 256 |
|---|
| 59 |
|
|---|
| 60 |
try: |
|---|
| 61 |
if options.provider == 'microsoft-road': |
|---|
| 62 |
url = 'http://r%d.ortho.tiles.virtualearth.net/tiles/r%s.png?g=90&shading=hill' % (random.randint(0,3), tile_params.toMicrosoft(col, row, zoom)) |
|---|
| 63 |
elif options.provider == 'microsoft-hybrid': |
|---|
| 64 |
url = 'http://h%d.ortho.tiles.virtualearth.net/tiles/h%s.jpeg?g=90' % (random.randint(0,3), tile_params.toMicrosoft(col, row, zoom)) |
|---|
| 65 |
elif options.provider == 'microsoft-aerial': |
|---|
| 66 |
url = 'http://a%d.ortho.tiles.virtualearth.net/tiles/a%s.jpeg?g=90' % (random.randint(0,3), tile_params.toMicrosoft(col, row, zoom)) |
|---|
| 67 |
|
|---|
| 68 |
print row, col, zoom, '->', url, '->', destX, destY |
|---|
| 69 |
|
|---|
| 70 |
img = PIL.Image.open(StringIO.StringIO(urllib.urlopen(url).read())) |
|---|
| 71 |
destImg.paste(img, (destX, destY)) |
|---|
| 72 |
|
|---|
| 73 |
except Exception, e: |
|---|
| 74 |
print e |
|---|
| 75 |
|
|---|
| 76 |
#time.sleep(10) |
|---|
| 77 |
|
|---|
| 78 |
print 'Saving...' |
|---|
| 79 |
destImg.save(options.outfile) |
|---|
| 80 |
print options.outfile |
|---|