#!/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) node['text'] = u"{1}".format(self.url, basename) 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("--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)