#! /usr/bin/env python

# Copyright (C) 2000, 2001, 2002 Intevation GmbH <intevation@intevation.de>
# Author: Bernhard Herzog <bh@intevation.de>
#
# This program is free software under the LGPL (>=v2)
# Read the file COPYING coming with MapIt! for details.


"""Usage: rastercut [options] rasterfile [basedir]

Create a tile hierarchy for MapIt! from a raster image file. The tile
hierarchy is created under basedir if given. basedir defaults to the
basename of the rasterfile argument

Options:

 -a          Resize with antialiasing. Only works with PIL >= 1.1.3.

 -f FACTORS  Comma separated list of scale factors. With e.g. 1,2 one
             layer has the full size of the input file and one has half
             the size. Factors are adjusted so that the sizes of the
             scale tile sets are multiples of the tile size.
             Default value is 1

 -o          Create an overview tile. For this, a factor will be added
             to the factors list so that an additional tile set is
             created that has the appropriate size.

 -t W,H      Width/height of the tiles (default 400,400). This tile size
             is only a hint. The real tile size used will be chosen to
             have the same aspect ratio as the input image.
"""


import sys, os
import string
import getopt
import PIL.Image
from math import floor, ceil

__version__ = "$Revision: 1.1 $"


#
#       Directory helper functions
#

def create_directory(dir):
    """Create the directory dir and all its parent directories if necessary"""
    if os.path.isdir(dir):
 	return
    parent, base = os.path.split(dir)
    if parent:
        create_directory(parent)
    try:
        os.mkdir(dir, 0777)
    except os.error, exc:
        print "can't create directory %s:%s" % (dir, exc)
        raise

def make_directories(basedir, num_factors):
    """Create the tile directories for the individual scales under basedir.

    For each number in range(1, num_factors + 1) a directory is created
    under basedir. The numbers used for the names are 'backwards', i.e.
    num_factors for the lowest resolution and 1 for the highest, because
    that's what MapIt! expects (a map of 1:5000 gives more detail than a
    map of 1:100000).
    """
    
    dirs = []
    for i in range(num_factors, 0, -1):
        dir = os.path.join(basedir, `i`)
        create_directory(dir)
        dirs.append(dir)
    return dirs


#
#
#

def make_tileset(image, directory, tile_width, tile_height,
                 reference_size, extension = '.png'):
    """Create the tiles from image in directory."""
    # write the header of the info file
    infofile = open(os.path.join(directory, 'info'), 'w')
    infofile.write("%d\n%d\n" % (tile_width, tile_height))

    reference_width, reference_height = map(float, reference_size)

    width, height = image.size
    num_tiles_x = width / tile_width
    num_tiles_y = height / tile_height

    print "writing tiles in", directory
    for y in range(num_tiles_y):
        for x in range(num_tiles_x):
            left = x * tile_width
            right = left + tile_width
            bottom = y * tile_height
            top = bottom + tile_height
            tile = image.crop((left, height - top, right, height - bottom))

            outputfile = os.path.join(directory, "%dx%d%s" % (x + 1, y + 1,
                                                              extension))
            sys.stdout.write(".")
            sys.stdout.flush()
            tile.save(outputfile)

            ref_llx = x * reference_width / num_tiles_x
            ref_lly = y * reference_height / num_tiles_y
            ref_urx = (x + 1) * reference_width / num_tiles_x
            ref_ury = (y + 1) * reference_height / num_tiles_y

            infofile.write("# [%d,%d]\n" % (x + 1, y + 1))
            infofile.write("%f\n%f\n%f\n%f\n" % (ref_llx, ref_lly,
                                                 ref_urx, ref_ury))

    print "done"

def make_tile_hierarchy(rasterfilename, basedir, factors, tile_size,
                        create_overview, antialias):
    """Render a complete tile hierarchy from a raster file."""
    image = PIL.Image.open(rasterfilename)
    width, height = image.size

    # determine the tile size so that it's as large as possible but no
    # larger than the given tile_size and has the same aspect ratio as
    # the input image.
    tile_scale = min(tile_size[0] / float(width), tile_size[1] / float(height))
    tile_width = int(round(tile_scale * width))
    tile_height = int(round(tile_scale * height))

    print "Adapted tile size: %d, %d" % (tile_width, tile_height)

    # if we're told to create an overview, add a factor that will
    # produce exactly one tile.
    if create_overview:
        factors = list(factors) + [1 / tile_scale]

    # create the directory hierarchy
    directories = make_directories(basedir, len(factors))
    # reverse the list because the factors list is also "reversed" in
    # comparison to the factors in epscut.
    directories.reverse()

    # create the individual tile sets
    for i in range(len(factors)):
        factor = factors[i]

        # work out an "optimal" size for the scaled image. The tiles of
        # one tile set have to have the same size and they have to cover
        # the scaled image exactly. Therefore the size of the scaled
        # image must be an integer multiple of the tile size.

        # first approximation
        scaled_width = width / factor
        scaled_height = height / factor

        # work out a better one by selecting a suitable number of tiles
        # for each dimension and scale the desired tile size (here the
        # overview size) accordingly
        num_tiles_x = int(round(scaled_width / float(tile_width)))
        num_tiles_y = int(round(scaled_height / float(tile_height)))
        scaled_width = tile_width * num_tiles_x
        scaled_height = tile_height * num_tiles_y

        print "requested factor %f: Real factors %f, %f" \
              % (factor, float(width) / scaled_width,
                 float(height) / scaled_height)

        if antialias:
            scaled = image.resize((scaled_width, scaled_height),
                                  PIL.Image.ANTIALIAS)
        else:
            scaled = image.resize((scaled_width, scaled_height))

        make_tileset(scaled, directories[i], tile_width, tile_height,
                     image.size)



def main():
    # default values for the options
    factors = (1,)
    tile_size = (400, 400)
    create_overview = 0
    antialias = 0

    # parse the command line flags
    opts, args = getopt.getopt(sys.argv[1:], "af:hot:")
    for opt, value in opts:
        if opt == '-a':
            antialias = 1
        elif opt == '-f':
            factors = map(float, string.split(value, ","))
        elif opt == '-o':
            create_overview = 1
        elif opt == '-t':
            size = map(int, string.split(value, ","))
            if len(size) != 2:
                print "Argument -o must have exactly two ints as parameter"
                sys.exit(1)
            tile_size = size
        elif opt == '-h':
            print __doc__
            sys.exit(1)
        else:
            print "unknown option", opt
            sys.exit(1)

    # the remaining args must be the input filename and optional
    # directory. args must be either 1 or 2
    if not 1 <= len(args) <= 2:
        print __doc__
        sys.exit(1)
    rasterfile = args[0]
    if len(args) < 2:
        basedir = os.path.splitext(os.path.basename(rasterfile))[0]
    else:
        basedir = args[1]

    # do the work
    make_tile_hierarchy(rasterfile, basedir, factors, tile_size,
                        create_overview, antialias)


if __name__ == '__main__':
    main()
