Skip to content

Instantly share code, notes, and snippets.

@TheMapSmith
Created September 30, 2014 01:26
Show Gist options
  • Save TheMapSmith/622e884832f5fc5d562d to your computer and use it in GitHub Desktop.
Save TheMapSmith/622e884832f5fc5d562d to your computer and use it in GitHub Desktop.
fetch tiles and stitch together
import csv
import math
import os.path
import urllib
### Function to Read in trace from csv ###
def traceImportCSV(fname):
import csv
trace = []
for lat,lon in csv.reader(open(fname,'r')):
trace.append([float(lat),float(lon)])
return trace
### Function to Read in trace from GPX ###
### Function to get trace boundries ###
def traceBoundaries(trace):
lat = zip(*trace)[0]
lon = zip(*trace)[1]
return {"north":max(lat),"south":min(lat),"east":max(lon),"west":min(lon)}
### Function to convert lat,lon degrees to tile x/y number ###
def deg2num(lat_deg, lon_deg, zoom):
lat_rad = math.radians(lat_deg)
n = 2.0 ** zoom
xtile = int((lon_deg + 180.0) / 360.0 * n)
ytile = int((1.0 - math.log(math.tan(lat_rad) + (1 / math.cos(lat_rad))) / math.pi) / 2.0 * n)
return (xtile, ytile)
### Function to convert xy tile to NW corner of tile ###
def num2deg(xtile, ytile, zoom):
n = 2.0 ** zoom
lon_deg = xtile / n * 360.0 - 180.0
lat_rad = math.atan(math.sinh(math.pi * (1 - 2 * ytile / n)))
lat_deg = math.degrees(lat_rad)
return (lat_deg, lon_deg)
### Determine tile range given boundaries and zoom ###
def determineTileRange(boundaries,zoom):
Xmax,Ymin = deg2num(boundaries["north"],boundaries["east"],zoom)
Xmin,Ymax = deg2num(boundaries["south"],boundaries["west"],zoom)
return {"xMin":Xmin,"xMax":Xmax,"yMin":Ymin,"yMax":Ymax}
### Take a tile range and download them (if not locally present) ###
def getTiles(xyRange,zoom):
#set acive directory to that of the script
currentdir = os.curdir
tileDir = os.path.join(currentdir,"/Users/spoon/Documents/tiles/golf/peter/")
tileServerUrl = "http://localhost:3000/style/"
#create a list of all the x and y coordinates to download
xRange = range(xyRange["xMin"],xyRange["xMax"]+1)
yRange = range(xyRange["yMin"],xyRange["yMax"]+1)
for x in xRange:
for y in yRange:
#define the file name
tileFileName = str(y)+".png"
#define the local path as well as the complete path to the local and remote files
localPath = os.path.join(tileDir,str(zoom),str(x))
localFile = os.path.join(localPath,tileFileName)
preFile = tileServerUrl+str(zoom)+"/"+str(x)+"/"+str(y)+"@4x.png"
remoteFile = preFile + "?id=tmstyle:///Users/spoon/Dropbox/GolfMap/_style/peter.tm2&hzrt9lj6"
#check if the file exists locally
if not os.path.isfile(localFile):
print "retrieving "+remoteFile
#if local directory doesn't yet exist, make it
if not os.path.isdir(localPath):
os.makedirs(localPath)
#retrieve the file from the server and save it
urllib.urlretrieve(remoteFile,localFile)
### Merge tiles into one image ###
def mergeTiles(xyRange,zoom,filename):
from PIL import Image
tileSize = 1024
tileDir = os.path.join(os.getcwd(),"/Users/spoon/Documents/tiles/golf/peter/",str(zoom))
out = Image.new( 'RGBA', ((xyRange["xMax"]-xyRange["xMin"]+1) * tileSize, (xyRange["yMax"]-xyRange["yMin"]+1) * tileSize) )
imx = 0;
for x in range(xyRange["xMin"], xyRange["xMax"]+1):
imy = 0
for y in range(xyRange["yMin"], xyRange["yMax"]+1):
tileFile = os.path.join(tileDir,str(x),str(y)+".png")
tile = Image.open(tileFile)
out.paste( tile, (imx, imy) )
imy += tileSize
imx += tileSize
out.save(os.path.join(os.curdir,filename))
# define parameters
zoom = 21
trace = traceImportCSV(r"peter.csv")
# determine the boundaries of the trace
boundaries_trace = traceBoundaries(trace)
# determine xy numbers of boundary tiles
tileRange = determineTileRange(boundaries_trace,zoom)
# count number of tiles in x and y direction
xTiles = tileRange["xMax"]-tileRange["xMin"]
yTiles = tileRange["yMax"]-tileRange["yMin"]
numTiles = xTiles*yTiles
print "fetching " + str(numTiles) + " tiles"
# download tiles if needed
getTiles(tileRange,zoom)
print "Got all the tiles. Merging"
# merge tiles into oneimage
mergeTiles(tileRange,zoom,r"peter-21-4x.png")
print "boundaries: ",boundaries_trace
print "tile X,Y range: ",tileRange
print "x tiles: ",xTiles
print "y tiles: ",yTiles
print "num tiles: ",numTiles
#Current record: AVAST Pete WP sized
#x tiles: 433
#y tiles: 258
#num tiles: 111714
#[Finished in 18099.2s]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment