#!/usr/bin/env python
from __future__ import print_function, division
from argparse import ArgumentParser
from imagetile2 import tile_image
from PIL import Image
import os, json, sys, re, datetime, urlparse
from math import ceil, log
"""
Maybe a better name for this script is tiling or tiler as it's not particularly leaflet specific.
"""
def tiles_path_for (n):
return n + ".tiles"
def autolink (text):
def sub (m):
return u'LINK'.format(m.group(0))
return re.sub(r"https?://[\S]+", sub, text, re.I)
def parse8601 (t, fmt=None):
""" simple 8601 parser that doesn't care about more than YMDHMS"""
# 2016-11-16T14:13:40.379857
m = re.search(r"(?P\d\d\d\d)-(?P\d\d)-(?P\d\d)T(?P\d\d):(?P\d\d):(?P\d\d)", t)
if m:
d = m.groupdict()
ret = datetime.datetime(int(d['year']), int(d['month']), int(d['day']), int(d['hour']), int(d['minute']), int(d['second']))
if fmt:
return ret.strftime(fmt)
else:
return ret
class tiles_wrapper (object):
""" Image wrapper abstraction... include URL to original + caption
"""
def __init__(self, path, url=None, text=None, tilename="z{0[z]}y{0[y]}x{0[x]}.png"):
self.path = path
# self.item = item
self.url = url
self.text = text
self.tilename = tilename
def get_tile_path (self, z, y, x):
return os.path.join(self.path, self.tilename.format({'z':z,'y':y,'x':x}))
def zoom (self):
""" return serialized version of self """
node = {}
node['zoomable'] = True
if self.text:
node['text'] = self.text
else:
# autotext is a link to the url showing the basename
_, basename = os.path.split(self.url)
base, ext = os.path.splitext(basename)
ext = ext[1:]
node['text'] = u"
{1}".format(self.url, ext.upper())
node['url'] = self.url
node['image'] = self.get_tile_path(0, 0, 0)
return node
def zoom_recursive (self, caption, x=0, y=0, z=0, maxzoom=3):
""" old style zoom in place -- ie render self to child nodes """
node = {}
node['text'] = self.text
node['image'] = self.get_tile_path(z, y, x)
if z < maxzoom:
kids = []
for r in range(2):
for c in range(2):
kids.append(self.zoom_recursive(caption, (x*2)+c, (y*2)+r, z+1, maxzoom))
node['children'] = kids
return node
def cell_layout(items, w=2):
i = 0
for r in range(w):
for c in range(w):
if i=1 and 'date' in items[0].item:
# node['text'] = items[0].item['date']
# else:
# node['text'] = ''
# node['image'] = ''
node['children'] = cc = [None, None, None, None]
ai = 0
for x in items[:3]:
# cap = os.path.splitext(os.path.basename(x.path))[0]
# cc.append(x) # x.zoom()
if (ai == direction):
ai += 1
cc[ai] = x
ai += 1;
rest = items[3:]
if rest:
# recurse
# cc.append(recursiverender(rest, basename, tilewidth, tileheight, z+1))
cc[direction] = recursiverender(rest, basename, tilewidth, tileheight, direction, z+1)
newim = fourup([x.get("image") for x in node['children'] if x != None and x.get("image")], tilewidth, tileheight)
# simplified name works just because there's only one generated tile per level
newimpath = u"{0}.z{1}.png".format(basename, z)
newim.save(newimpath)
node['image'] = newimpath
return node
def layoutxyz (n, x=0, y=0, z=0, outnode={}):
# print ("layout", n, x, y, z, file=sys.stderr)
outnode["{0},{1},{2}".format(x,y,z)] = {
"text": n['text'],
"image": n['image']
}
if 'children' in n:
for child, cx, cy in cell_layout(n['children']):
layout(child, (x*2)+cx, (y*2)+cy, z+1, outnode)
return outnode
def html (node, title):
page = u"""
""" + title + u"""
"""
return page
def make_gallery(args):
"""
to do -- separate the actual tiling process...
make tiling a separate pass ON THE ACTUAL NODE jSON
NB: this command accepts two different kinds of input.
1. One or more images as (argv) arguments -or-
2. A JSON stream (one object per line) on stdin.
"""
bgcolor = None # (0, 0, 0)
items = []
if args.input:
for x in args.input:
i = {'url': x}
items.append(i)
else:
for line in sys.stdin:
line = line.rstrip()
if line and not line.startswith("#"):
item = json.loads(line)
items.append(item)
# Ensure / Generate tiles per image
items.sort(key=lambda x: x['url'])
tiles = []
for item in items:
n = item['url']
# print (n, file=sys.stderr)
path = os.path.join(args.tilespath, n)
# TODO date format...
caption = ''
if 'text' or 'date' in item:
caption += u'
';
if 'text' in item:
caption += u'{0}'.format(autolink(item['text']))
if 'date' in item:
dt = parse8601(item['date'], "%d %b %Y")
caption += u'{0}'.format(dt)
if 'url' in item:
ext = os.path.splitext(urlparse.urlparse(item['url']).path)[1]
if ext:
ext = ext[1:].upper()
caption += u'{1}'.format(item['url'], ext)
if 'text' or 'date' in item:
caption += u'
';
t = tiles_wrapper(path, item['url'], text=caption)
tiles.append(t)
tile0 = t.get_tile_path(0, 0, 0) # os.path.join(path, args.tilename.format({'x': 0, 'y': 0, 'z': 0}))
if not os.path.exists(tile0) or args.force:
print ("Tiling {0}".format(n), file=sys.stderr)
try:
im = Image.open(n)
try:
os.makedirs(path)
except OSError:
pass
tile_image(im, args.zoom, args.tilewidth, args.tileheight, path+"/", args.tilename, bgcolor)
# tiles.append(t)
except IOError as e:
print ("Missing {0}, skipping".format(n), file=sys.stderr)
tiles = tiles[:-1]
# DO THE LAYOUT, generating intermediate tiles (zoom outs)
if args.reverse:
tiles.reverse()
tiles = [t.zoom() for t in tiles]
basename = os.path.join(args.tilespath, args.name)
if args.recursive:
root_node = recursiverender(tiles, basename, args.tilewidth, args.tileheight, args.direction)
else:
root_node = gridrender(tiles, basename, args.tilewidth, args.tileheight)
# OUTPUT ROOT NODE
if args.html:
print (html(root_node, args.name))
else:
print (json.dumps(root_node, indent=args.indent))
if __name__ == "__main__":
ap = ArgumentParser("")
ap.add_argument("--basepath", default=".")
ap.add_argument("--baseuri", default="")
ap.add_argument("--tilespath", default="tiles")
ap.add_argument("--tilewidth", type=int, default=256)
ap.add_argument("--tileheight", type=int, default=256)
ap.add_argument("--zoom", type=int, default=3)
ap.add_argument("--tilename", default="z{0[z]}y{0[y]}x{0[x]}.png")
ap.add_argument("--reverse", default=False, action="store_true")
ap.add_argument("--indent", default=2, type=int)
ap.add_argument("--recursive", default=False, action="store_true")
ap.add_argument("--force", default=False, action="store_true")
subparsers = ap.add_subparsers(help='sub-command help')
ap_gallery = subparsers.add_parser('gallery', help='Create a grid gallery of images')
ap_gallery.add_argument("input", nargs="*")
ap_gallery.add_argument("--html", default=False, action="store_true")
ap_gallery.add_argument("--recursive", default=False, action="store_true")
ap_gallery.add_argument("--reverse", default=False, action="store_true")
ap_gallery.add_argument("--direction", type=int, default=3, help="cell to recursively expand into, 0-3, default: 3 (bottom-right)")
ap_gallery.add_argument("--name", default="gallery")
ap_gallery.set_defaults(func=make_gallery)
args = ap.parse_args()
args.func(args)