-
-
Save perrygeo/cb248e39add8390f09abc030d7dd938b to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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