Skip to content

Instantly share code, notes, and snippets.

@perrygeo
Created February 10, 2017 18:44
Show Gist options
  • Save perrygeo/cb248e39add8390f09abc030d7dd938b to your computer and use it in GitHub Desktop.
Save perrygeo/cb248e39add8390f09abc030d7dd938b to your computer and use it in GitHub Desktop.
#!/usr/bin/env python
# pylint: disable=W0621
import json
import math
import re
import sys
# From mercantile
def ul(*tile):
"""Returns the upper left (lon, lat) of a tile"""
if len(tile) == 1:
tile = tile[0]
xtile, ytile, zoom = tile
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 (lon_deg, lat_deg)
# From mercantile
def bounds(*tile):
"""Returns the (lon, lat) bounding box of a tile"""
if len(tile) == 1:
tile = tile[0]
xtile, ytile, zoom = tile
a = ul(xtile, ytile, zoom)
b = ul(xtile + 1, ytile + 1, zoom)
return (a[0], b[1], b[0], a[1])
# From mercantile
def tile(lng, lat, zoom):
"""Returns the (x, y, z) tile"""
lat = math.radians(lat)
n = 2.0 ** zoom
try:
xtile = int(math.floor((lng + 180.0) / 360.0 * n))
ytile = int(math.floor((1.0 - math.log(
math.tan(lat) + (1.0 / math.cos(lat))) / math.pi) / 2.0 * n))
except ValueError:
xtile = 0
ytile = 0
return (xtile, ytile, zoom)
def center(*tile):
"""Returns center lon, lat of a tile"""
x1, y1, x2, y2 = bounds(*tile)
return ((x1 + x2) / 2, (y1 + y2) / 2)
def xyz(instream):
for inp in instream:
if re.match(r'.*\d+[\-\/]\d+[\-\/]\d+.*', inp):
z, x, y = [int(t) for t in re.findall(
r'(^|[\/\s])(\d+)[\-\/](\d+)[\-\/](\d+)', inp)[0][1:]]
elif re.match(r'.*\d+\/.*\d+\..*\d+\/.*\d+\..*\d+', inp):
result = inp.split("/")
z = int(result[0])
lat = float(result[1])
lon = float(result[2])
x, y, _ = tile(lon, lat, z)
elif re.match(r'\[\d+\,\s\d+\,\s\d+\]', inp):
x, y, z = json.loads(inp)
else:
raise ValueError("No match for input tile")
yield x, y, z
def tile_format(x, y, z, style="json-xyz"):
if style == 'json-xyz':
return json.dumps([x, y, z])
elif style in ('dash', 'slash'):
delimiter = {'dash': '-', 'slash': '/'}[style]
return "{1}{0}{2}{0}{3}".format(delimiter, z, x, y)
elif style == 'zlatlon':
lon, lat = center(x, y, z)
return "{0}/{1}/{2}".format(z, lat, lon)
elif style == 'coords':
lon, lat = center(x, y, z)
return "[{0},{1}]".format(lon, lat)
else:
raise ValueError("invalid style {}".format(style))
def usage():
print("""xt
-d, --dash Outputs z-x-y
-s, --slash Outputs z/x/y
-l, --zlatlon Outputs z/lat/lon
-j, --json Outputs [x, y, z] (DEFAULT, mercantile-compatible)
-m, --mercantile alias for --json
-c, --coordinates Output a '[lon, lat]' array (Lossy, not invertable)""")
def parse_style(argv):
if len(argv) == 2:
if argv[1] in ('-d', '--dash', 'z-x-y'):
return 'dash'
elif argv[1] in ('-s', '--slash', 'z/x/y'):
return 'slash'
elif argv[1] in ('-j', '--json', '-m', '--mercantile', 'x,y,z'):
return 'json-xyz'
elif argv[1] in ('-l', '--zlatlon'):
return 'zlatlon'
elif argv[1] in ('-c', '--coordinates'):
return 'coords'
elif argv[1] in ('-h', '--help'):
usage()
exit(0)
else:
usage()
exit(1)
elif len(argv) > 2:
usage()
exit(1)
else:
return 'json-xyz'
if __name__ == '__main__':
s = parse_style(sys.argv)
for t in xyz(sys.stdin.readlines()):
print(tile_format(*t, style=s))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment