Example #1
0
def test_custom(grid_definition_proj, grid_definition_epsg):
    for grid_def in [grid_definition_proj, grid_definition_epsg]:
        tp = TilePyramid(grid_def, metatiling=8)
        tp_dict = tp.to_dict()
        assert isinstance(tp_dict, dict)
        tp2 = TilePyramid.from_dict(tp_dict)
        assert tp == tp2
Example #2
0
def count_tiles(geometry, pyramid, minzoom, maxzoom, init_zoom=0):
    """
    Count number of tiles intersecting with geometry.

    Parameters
    ----------
    geometry : shapely geometry
    pyramid : TilePyramid
    minzoom : int
    maxzoom : int
    init_zoom : int

    Returns
    -------
    number of tiles
    """
    if not 0 <= init_zoom <= minzoom <= maxzoom:
        raise ValueError("invalid zoom levels given")
    # tile buffers are not being taken into account
    unbuffered_pyramid = TilePyramid(pyramid.grid,
                                     tile_size=pyramid.tile_size,
                                     metatiling=pyramid.metatiling)
    # make sure no rounding errors occur
    geometry = geometry.buffer(-0.000000001)
    return _count_tiles([
        unbuffered_pyramid.tile(*tile_id)
        for tile_id in product([init_zoom],
                               range(pyramid.matrix_height(init_zoom)),
                               range(pyramid.matrix_width(init_zoom)))
    ], geometry, minzoom, maxzoom)
Example #3
0
 def __init__(self, output_params):
     """Initialize."""
     self.pixelbuffer = output_params["pixelbuffer"]
     self.pyramid = TilePyramid(output_params["type"],
                                metatiling=output_params["metatiling"])
     self.crs = self.pyramid.crs
     self.srid = self.pyramid.srid
Example #4
0
def test_tiles_from_polygon(polygon):
    """Get tiles from Polygon."""
    test_tiles = {
        (9, 116, 544),
        (9, 116, 545),
        (9, 116, 546),
        (9, 117, 540),
        (9, 117, 541),
        (9, 117, 542),
        (9, 117, 543),
        (9, 117, 544),
        (9, 117, 545),
        (9, 118, 536),
        (9, 118, 537),
        (9, 118, 538),
        (9, 118, 539),
        (9, 118, 540),
        (9, 118, 541),
        (9, 119, 535),
        (9, 119, 536),
        (9, 119, 537),
        (9, 119, 538),
    }
    tp = TilePyramid("geodetic")
    polygon_tiles = {tile.id for tile in tp.tiles_from_geom(polygon, 9)}
    assert polygon_tiles == test_tiles
Example #5
0
def test_tiles_from_bounds_batch_by_column():
    tp = TilePyramid("geodetic")
    bounds = (0, 0, 90, 90)
    zoom = 8

    tiles = tp.tiles_from_bounds(bounds, zoom, batch_by="column")
    assert isinstance(tiles, GeneratorType)
    assert list(tiles)

    previous_column = None
    tiles = 0
    for tile_column in tp.tiles_from_bounds(bounds, zoom, batch_by="column"):
        assert isinstance(tile_column, GeneratorType)
        previous_tile = None
        for tile in tile_column:
            tiles += 1
            if previous_column is None:
                if previous_tile is not None:
                    assert tile.row == previous_tile.row + 1
            else:
                if previous_tile is not None:
                    assert tile.row == previous_tile.row + 1
                    assert tile.col == previous_tile.col
                    assert tile.col == previous_column + 1

            previous_tile = tile

        previous_column = tile.col

    assert tiles == len(list(tp.tiles_from_bounds(bounds, zoom)))
Example #6
0
def test_tiles_from_bounds_batch_by_row_both_antimeridian_bounds():
    tp = TilePyramid("geodetic")
    bounds = (-185, 0, 185, 95)
    zoom = 8

    tiles = tp.tiles_from_bounds(bounds, zoom, batch_by="row")
    assert isinstance(tiles, GeneratorType)
    assert list(tiles)

    previous_row = None
    tiles = 0
    for tile_row in tp.tiles_from_bounds(bounds, zoom, batch_by="row"):
        assert isinstance(tile_row, GeneratorType)
        previous_tile = None
        for tile in tile_row:
            tiles += 1
            if previous_row is None:
                if previous_tile is not None:
                    assert tile.col == previous_tile.col + 1
            else:
                if previous_tile is not None:
                    assert tile.col == previous_tile.col + 1
                    assert tile.row == previous_tile.row
                    assert tile.row == previous_row + 1

            previous_tile = tile

        previous_row = tile.row

    assert tiles == len(list(tp.tiles_from_bounds(bounds, zoom)))
Example #7
0
def _run_tile(zoom,
              point,
              grid="geodetic",
              metatiling=1,
              pixelbuffer=0,
              tile_size=256,
              output_format="WKT"):
    x, y, = point
    tile = TilePyramid(grid, metatiling=metatiling,
                       tile_size=tile_size).tile_from_xy(x, y, zoom)
    result = CliRunner().invoke(tmx, [
        "--pixelbuffer",
        str(pixelbuffer), "--metatiling",
        str(metatiling), "--grid", grid, "--tile_size",
        str(tile_size), "--output_format", output_format, "tile",
        str(zoom),
        str(x),
        str(y)
    ])
    assert result.exit_code == 0
    if output_format == "Tile":
        assert result.output.strip() == " ".join(map(str, tile.id))
    elif output_format == "WKT":
        assert wkt.loads(result.output.strip()).almost_equals(
            tile.bbox(pixelbuffer))
    elif output_format == "GeoJSON":
        feature = geojson.loads(result.output.strip())["features"][0]
        assert shape(feature["geometry"]).almost_equals(tile.bbox(pixelbuffer))
Example #8
0
def _run_bbox_bounds(zoom,
                     row,
                     col,
                     command=None,
                     grid="geodetic",
                     metatiling=1,
                     pixelbuffer=0,
                     tile_size=256,
                     output_format="WKT"):
    tile = TilePyramid(grid, metatiling=metatiling,
                       tile_size=tile_size).tile(zoom, row, col)
    result = CliRunner().invoke(tmx, [
        "--pixelbuffer",
        str(pixelbuffer), "--metatiling",
        str(metatiling), "--grid", grid, "--tile_size",
        str(tile_size), "--output_format", output_format, command,
        str(zoom),
        str(row),
        str(col)
    ])
    assert result.exit_code == 0
    if command == "bounds":
        assert result.output.strip() == " ".join(
            map(str, tile.bounds(pixelbuffer)))
    elif output_format == "WKT":
        assert wkt.loads(result.output.strip()).almost_equals(
            tile.bbox(pixelbuffer))
    elif output_format == "GeoJSON":
        assert shape(geojson.loads(result.output.strip())).almost_equals(
            tile.bbox(pixelbuffer))
Example #9
0
def test_intersecting():
    """Get intersecting Tiles from other TilePyramid."""
    tp_source = TilePyramid("geodetic", metatiling=2)
    tp_target = TilePyramid("geodetic")
    tile = tp_source.tile(5, 2, 2)
    test_tiles = {(5, 4, 4), (5, 5, 4), (5, 4, 5), (5, 5, 5)}
    intersecting_tiles = {t.id for t in tile.intersecting(tp_target)}
    assert test_tiles == intersecting_tiles
Example #10
0
def test_tile_tuple():
    tp = TilePyramid("geodetic")
    a = tp.tile(5, 5, 5)
    assert tuple(a) == (
        5,
        5,
        5,
    )
Example #11
0
def test_metatiling():
    """Metatiling setting."""
    for metatiling in [1, 2, 4, 8, 16]:
        assert TilePyramid("geodetic", metatiling=metatiling)
    try:
        TilePyramid("geodetic", metatiling=5)
        raise Exception()
    except ValueError:
        pass
Example #12
0
def test_init():
    """Initialize TilePyramids."""
    for tptype in ["geodetic", "mercator"]:
        assert TilePyramid(tptype)
    with pytest.raises(ValueError):
        TilePyramid("invalid")
    with pytest.raises(ValueError):
        TilePyramid()
    assert hash(TilePyramid(tptype))
Example #13
0
def test_tiles_from_bounds(grid_definition_irregular):
    bounds = (755336.179, 300068.615, 791558.022, 319499.955)
    bbox = box(*bounds)
    tp = TilePyramid(grid_definition_irregular, metatiling=4)
    tiles_bounds = list(tp.tiles_from_bounds(bounds, 0))
    tiles_bbox = list(tp.tiles_from_bbox(bbox, 0))
    tiles_geom = list(tp.tiles_from_geom(bbox, 0))

    assert set(tiles_bounds) == set(tiles_bbox) == set(tiles_geom)
Example #14
0
def test_get_parent():
    """Get parent Tile."""
    tp = TilePyramid("geodetic")
    # default
    tile = tp.tile(8, 100, 100)
    assert tile.get_parent().id == (7, 50, 50)
    # from top of pyramid
    tile = tp.tile(0, 0, 0)
    assert tile.get_parent() is None
Example #15
0
 def __init__(self, output_params, readonly=False):
     """Initialize."""
     self.pixelbuffer = output_params["pixelbuffer"]
     self.pyramid = TilePyramid(output_params["type"],
                                metatiling=output_params["metatiling"])
     self.crs = self.pyramid.crs
     self.srid = self.pyramid.srid
     self._bucket = None
     if not readonly:
         write_output_metadata(output_params)
Example #16
0
def test_tile_compare():
    tp = TilePyramid("geodetic")
    a = tp.tile(5, 5, 5)
    b = tp.tile(5, 5, 5)
    c = tp.tile(5, 5, 6)
    assert a == b
    assert a != c
    assert b != c
    assert a != "invalid type"
    assert len(set([a, b, c])) == 2
Example #17
0
 def __init__(
     self, pyramid_type, metatiling=1, tile_size=256, pixelbuffer=0
 ):
     """Initialize."""
     TilePyramid.__init__(
         self, pyramid_type, metatiling=metatiling, tile_size=tile_size)
     self.tile_pyramid = TilePyramid(
         pyramid_type, metatiling=metatiling, tile_size=tile_size)
     self.metatiling = metatiling
     if isinstance(pixelbuffer, int) and pixelbuffer >= 0:
         self.pixelbuffer = pixelbuffer
     else:
         raise ValueError("pixelbuffer has to be a non-negative int")
Example #18
0
def test_tiles_from_point_batches(point):
    """Get tile from point."""
    tp = TilePyramid("geodetic")
    zoom = 9
    tiles = 0
    gen = tp.tiles_from_geom(point, zoom, batch_by="row")
    assert isinstance(gen, GeneratorType)
    for row in gen:
        assert isinstance(row, GeneratorType)
        for tile in row:
            tiles += 1
            assert isinstance(tile, Tile)
    assert tiles
    assert tiles == len(list(tp.tiles_from_geom(point, zoom)))
Example #19
0
def test_tiles_from_linestring_batches(linestring):
    """Get tiles from LineString."""
    tp = TilePyramid("geodetic")
    zoom = 9
    tiles = 0
    gen = tp.tiles_from_geom(linestring, zoom, batch_by="row")
    assert isinstance(gen, GeneratorType)
    for row in gen:
        assert isinstance(row, GeneratorType)
        for tile in row:
            tiles += 1
            assert isinstance(tile, Tile)
    assert tiles
    assert tiles == len(list(tp.tiles_from_geom(linestring, zoom)))
Example #20
0
def test_tiles_from_multipolygon_batches(multipolygon):
    """Get tiles from MultiPolygon."""
    tp = TilePyramid("geodetic")
    zoom = 9
    tiles = 0
    gen = tp.tiles_from_geom(multipolygon, zoom, batch_by="row")
    assert isinstance(gen, GeneratorType)
    for row in gen:
        assert isinstance(row, GeneratorType)
        for tile in row:
            tiles += 1
            assert isinstance(tile, Tile)
    assert tiles
    assert tiles == len(list(tp.tiles_from_geom(multipolygon, zoom)))
Example #21
0
def test_tiles_from_linestring(linestring):
    """Get tiles from LineString."""
    test_tiles = {
        (8, 58, 270),
        (8, 58, 271),
        (8, 58, 272),
        (8, 58, 273),
        (8, 59, 267),
        (8, 59, 268),
        (8, 59, 269),
        (8, 59, 270),
    }
    tp = TilePyramid("geodetic")
    linestring_tiles = {tile.id for tile in tp.tiles_from_geom(linestring, 8)}
    assert linestring_tiles == test_tiles
Example #22
0
def bounds(ctx, tile):
    """Print Tile bounds."""
    click.echo("%s %s %s %s" % TilePyramid(
        ctx.obj["grid"],
        tile_size=ctx.obj["tile_size"],
        metatiling=ctx.obj["metatiling"],
    ).tile(*tile).bounds(pixelbuffer=ctx.obj["pixelbuffer"]))
Example #23
0
def test_shape_error(grid_definition_epsg):
    """Raise error when shape aspect ratio is not bounds apsect ratio."""
    grid_definition_epsg.update(
        bounds=(2426378.0132, 1528101.2618, 6293974.6215, 5446513.5222)
    )
    with pytest.raises(ValueError):
        TilePyramid(grid_definition_epsg)
Example #24
0
def params_to_dump(params):
    # in case GridDefinition was not yet initialized
    if isinstance(params["type"], str):
        tp = TilePyramid(params["type"])
        params.update(type=tp.grid)
    return dict(pyramid=dict(
        grid=dict(
            type=params["type"].type,
            shape=list(params["type"].shape),
            bounds=list(params["type"].bounds),
            left=params["type"].left,
            bottom=params["type"].bottom,
            right=params["type"].right,
            top=params["type"].top,
            is_global=params["type"].is_global,
            srid=params["type"].srid,
            crs=params["type"].crs.to_string(),
        ),
        metatiling=params.get("metatiling", 1),
        pixelbuffer=params.get("pixelbuffer", 0),
    ),
                driver={
                    k: v
                    for k, v in params.items()
                    if k not in ["path", "type", "pixelbuffer", "metatiling"]
                })
Example #25
0
def tiles(ctx, bounds, zoom):
    """Print Tiles from bounds."""
    tiles = TilePyramid(
        ctx.obj["grid"],
        tile_size=ctx.obj["tile_size"],
        metatiling=ctx.obj["metatiling"],
    ).tiles_from_bounds(bounds, zoom=zoom)
    if ctx.obj["output_format"] == "Tile":
        for tile in tiles:
            click.echo("%s %s %s" % tile.id)
    elif ctx.obj["output_format"] == "WKT":
        for tile in tiles:
            click.echo(tile.bbox(pixelbuffer=ctx.obj["pixelbuffer"]))
    elif ctx.obj["output_format"] == "GeoJSON":
        click.echo("{\n" '  "type": "FeatureCollection",\n' '  "features": [')
        # print tiles as they come and only add comma if there is a next tile
        try:
            tile = next(tiles)
            while True:
                gj = "    %s" % geojson.Feature(
                    geometry=tile.bbox(pixelbuffer=ctx.obj["pixelbuffer"]),
                    properties=dict(zoom=tile.zoom, row=tile.row,
                                    col=tile.col),
                )
                try:
                    tile = next(tiles)
                    click.echo(gj + ",")
                except StopIteration:
                    click.echo(gj)
                    raise
        except StopIteration:
            pass
        click.echo("  ]\n" "}")
Example #26
0
def _run_tiles(zoom,
               bounds,
               grid="geodetic",
               metatiling=1,
               pixelbuffer=0,
               tile_size=256,
               output_format="WKT"):
    left, bottom, right, top = bounds
    tiles = list(
        TilePyramid(grid, metatiling=metatiling,
                    tile_size=tile_size).tiles_from_bounds(bounds, zoom))
    result = CliRunner().invoke(tmx, [
        "--pixelbuffer",
        str(pixelbuffer), "--metatiling",
        str(metatiling), "--grid", grid, "--tile_size",
        str(tile_size), "--output_format", output_format, "tiles",
        str(zoom),
        str(left),
        str(bottom),
        str(right),
        str(top)
    ])
    assert result.exit_code == 0
    if output_format == "Tile":
        assert result.output.count('\n') == len(tiles)
    elif output_format == "WKT":
        assert result.output.count('\n') == len(tiles)
    elif output_format == "GeoJSON":
        features = geojson.loads(result.output.strip())["features"]
        assert len(features) == len(tiles)
Example #27
0
def get_best_zoom_level(input_file, tile_pyramid_type):
    """
    Determine the best base zoom level for a raster.

    "Best" means the maximum zoom level where no oversampling has to be done.

    Parameters
    ----------
    input_file : path to raster file
    tile_pyramid_type : ``TilePyramid`` projection (``geodetic`` or
        ``mercator``)

    Returns
    -------
    zoom : integer
    """
    tile_pyramid = TilePyramid(tile_pyramid_type)
    with rasterio.open(input_file, "r") as src:
        if not src.crs.is_valid:
            raise IOError("CRS could not be read from %s" % input_file)
        bbox = box(
            src.bounds.left, src.bounds.bottom, src.bounds.right,
            src.bounds.top)
        if src.crs != tile_pyramid.crs:
            segmentize = raster_file._get_segmentize_value(
                input_file, tile_pyramid)
            ogr_bbox = ogr.CreateGeometryFromWkb(bbox.wkb)
            ogr_bbox.Segmentize(segmentize)
            segmentized_bbox = loads(ogr_bbox.ExportToWkt())
            bbox = segmentized_bbox
            xmin, ymin, xmax, ymax = reproject_geometry(
                bbox, src_crs=src.crs, dst_crs=tile_pyramid.crs).bounds
        else:
            xmin, ymin, xmax, ymax = bbox.bounds
        x_dif = xmax - xmin
        y_dif = ymax - ymin
        size = float(src.width + src.height)
        avg_resolution = (
            (x_dif / float(src.width)) * (float(src.width) / size) +
            (y_dif / float(src.height)) * (float(src.height) / size)
        )

    for zoom in range(0, 25):
        if tile_pyramid.pixel_x_size(zoom) <= avg_resolution:
            return zoom-1

    raise ValueError("no fitting zoom level found")
Example #28
0
 def __init__(self, tile, pixelbuffer=0):
     """Initialize."""
     if isinstance(tile, BufferedTile):
         tile = TilePyramid(tile.tp.grid,
                            tile_size=tile.tp.tile_size,
                            metatiling=tile.tp.metatiling).tile(*tile.id)
     Tile.__init__(self, tile.tile_pyramid, tile.zoom, tile.row, tile.col)
     self._tile = tile
     self.pixelbuffer = pixelbuffer
Example #29
0
 def __init__(self, mapchete_file, zoom=None, bounds=None):
     """
     Initialize with a .mapchete file and optional zoom & bound parameters.
     """
     try:
         self.config = get_clean_configuration(
             mapchete_file,
             zoom=zoom,
             bounds=bounds
             )
         base_tile_pyramid = TilePyramid(str(self.config["output_srs"]))
         base_tile_pyramid.set_format(self.config["output_format"])
         self.tile_pyramid = MetaTilePyramid(
             base_tile_pyramid,
             self.config["metatiling"]
         )
         self.format = self.tile_pyramid.format
     except Exception as e:
         raise
Example #30
0
def test_snap_bounds():
    bounds = (0, 1, 2, 3)
    tp = TilePyramid("geodetic")
    zoom = 8

    snapped = snap_bounds(bounds=bounds, tile_pyramid=tp, zoom=zoom)
    control = unary_union(
        [tile.bbox() for tile in tp.tiles_from_bounds(bounds, zoom)]).bounds
    assert snapped == control

    pixelbuffer = 10
    snapped = snap_bounds(bounds=bounds,
                          tile_pyramid=tp,
                          zoom=zoom,
                          pixelbuffer=pixelbuffer)
    control = unary_union([
        tile.bbox(pixelbuffer) for tile in tp.tiles_from_bounds(bounds, zoom)
    ]).bounds
    assert snapped == control
Example #31
0
def get_best_zoom_level(input_file, tile_pyramid_type):
    """
    Determines the best base zoom level for a raster. "Best" means the maximum
    zoom level where no oversampling has to be done.
    """
    tile_pyramid = TilePyramid(tile_pyramid_type)
    input_bbox = file_bbox(input_file, tile_pyramid)
    xmin, ymin, xmax, ymax = input_bbox.bounds
    with rasterio.open(input_file, "r") as src:
        x_dif = xmax - xmin
        y_dif = ymax - ymin
        size = float(src.width + src.height)
        avg_resolution = (
            (x_dif / float(src.width)) * (float(src.width) / size) +
            (y_dif / float(src.height)) * (float(src.height) / size)
        )

    for zoom in range(0, 25):
        if tile_pyramid.pixel_x_size(zoom) <= avg_resolution:
            return zoom-1

    raise ValueError("no fitting zoom level found")
Example #32
0
def main():

    scriptdir = os.path.dirname(os.path.realpath(__file__))


    # YAML configuration
    #===================

    # Load source process from python file and initialize.
    mapchete_file = os.path.join(scriptdir, "example.mapchete")
    mapchete = Mapchete(MapcheteConfig(mapchete_file))

    dummy1_abspath = os.path.join(scriptdir, "testdata/dummy1.tif")
    dummy2_abspath = os.path.join(scriptdir, "testdata/dummy2.tif")

    # Validate configuration constructor
    ## basic run through
    try:
        config = mapchete.config
        print "OK: basic configuraiton constructor run through"
    except:
        print "FAILED: basic configuraiton constructor run through"
        raise

    try:
        # Check configuration at zoom level 5
        zoom5 = config.at_zoom(5)
        input_files = zoom5["input_files"]
        assert input_files["file1"] == None
        assert input_files["file2"] == dummy2_abspath
        assert zoom5["some_integer_parameter"] == 12
        assert zoom5["some_float_parameter"] == 5.3
        assert zoom5["some_string_parameter"] == "string1"
        assert zoom5["some_bool_parameter"] == True

        # Check configuration at zoom level 11
        zoom11 = config.at_zoom(11)
        input_files = zoom11["input_files"]
        assert input_files["file1"] == dummy1_abspath
        assert input_files["file2"] == dummy2_abspath
        assert zoom11["some_integer_parameter"] == 12
        assert zoom11["some_float_parameter"] == 5.3
        assert zoom11["some_string_parameter"] == "string2"
        assert zoom11["some_bool_parameter"] == True
    except:
        print "FAILED: basic configuration parsing"
        print input_files
        raise
    else:
        print "OK: basic configuration parsing"

    ## read zoom level from config file
    mapchete_file = os.path.join(scriptdir, "testdata/zoom.mapchete")
    config = Mapchete(MapcheteConfig(mapchete_file)).config
    try:
        assert 5 in config.zoom_levels
        print "OK: read zoom level from config file"
    except:
        print "FAILED: read zoom level from config file"
        print mapchete_file
        raise
    ## read min/max zoom levels from config file
    mapchete_file = os.path.join(scriptdir, "testdata/minmax_zoom.mapchete")
    config = Mapchete(MapcheteConfig(mapchete_file)).config
    try:
        for zoom in [7, 8, 9, 10]:
            assert zoom in config.zoom_levels
        print "OK: read  min/max zoom levels from config file"
    except:
        print "FAILED: read  min/max zoom levels from config file"
        raise
    ## zoom levels override
    mapchete_file = os.path.join(scriptdir, "testdata/minmax_zoom.mapchete")
    config = Mapchete(MapcheteConfig(mapchete_file, zoom=[1, 4])).config
    try:
        for zoom in [1, 2, 3, 4]:
            assert zoom in config.zoom_levels
        print "OK: zoom levels override"
    except:
        print "FAILED: zoom levels override"
        raise
    ## read bounds from config file
    mapchete_file = os.path.join(scriptdir, "testdata/zoom.mapchete")
    config = Mapchete(MapcheteConfig(mapchete_file)).config
    try:
        test_polygon = Polygon([
            [3, 1.5], [3, 2], [3.5, 2], [3.5, 1.5], [3, 1.5]
            ])
        assert config.process_area(5).equals(test_polygon)
        print "OK: read bounds from config file"
    except:
        print "FAILED: read bounds from config file"
        print config.process_area(5), test_polygon
        raise
    ## override bounds
    mapchete_file = os.path.join(scriptdir, "testdata/zoom.mapchete")
    config = Mapchete(MapcheteConfig(
        mapchete_file,
        bounds=[3, 2, 3.5, 1.5]
        )).config
    try:
        test_polygon = Polygon([
            [3, 1.5], [3, 2], [3.5, 2], [3.5, 1.5], [3, 1.5]
            ])
        assert config.process_area(5).equals(test_polygon)
        print "OK: override bounds"
    except:
        print "FAILED: override bounds"
        print config.process_area(5)
        raise
    ## read bounds from input files
    mapchete_file = os.path.join(scriptdir, "testdata/files_bounds.mapchete")
    config = Mapchete(MapcheteConfig(mapchete_file)).config
    try:
        test_polygon = Polygon(
        [[3, 2], [4, 2], [4, 1], [3, 1], [2, 1], [2, 4], [3, 4], [3, 2]]
        )
        assert config.process_area(10).equals(test_polygon)
        print "OK: read bounds from input files"
    except:
        print "FAILED: read bounds from input files"
        print config.process_area(10), test_polygon
        raise
    ## read .mapchete files as input files
    mapchete_file = os.path.join(scriptdir, "testdata/mapchete_input.mapchete")
    config = Mapchete(MapcheteConfig(mapchete_file)).config
    area = config.process_area(5)
    testpolygon = "POLYGON ((3 2, 3.5 2, 3.5 1.5, 3 1.5, 3 1, 2 1, 2 4, 3 4, 3 2))"
    try:
        assert area.equals(loads(testpolygon))
        print "OK: read bounding box from .mapchete subfile"
    except:
        print "FAILED: read bounding box from .mapchete subfile"
        raise


    mapchete_file = os.path.join(scriptdir, "testdata/gtiff.mapchete")

    mapchete_file = os.path.join(scriptdir, "testdata/numpy.mapchete")
    mapchete = Mapchete(MapcheteConfig(mapchete_file))


    # test io module
    testdata_directory = os.path.join(scriptdir, "testdata")
    outdata_directory = os.path.join(testdata_directory, "out")

    dummy1 = os.path.join(testdata_directory, "dummy1.tif")
    # dummy1 = os.path.join(testdata_directory, "sentinel2.tif")
    dummy2 = os.path.join(testdata_directory, "dummy2.tif")
    zoom = 8
    tile_pyramid = TilePyramid("geodetic")

    dummy1_bbox = file_bbox(dummy1, tile_pyramid)

    tiles = tile_pyramid.tiles_from_geom(dummy1_bbox, zoom)
    resampling = "average"
    pixelbuffer=5
    for tile in tiles:
        for band in read_raster_window(
            dummy1,
            tile,
            resampling=resampling,
            pixelbuffer=pixelbuffer
            ):
            try:
                assert band.shape == (
                    tile_pyramid.tile_size + 2 * pixelbuffer,
                    tile_pyramid.tile_size + 2 * pixelbuffer
                )
                print "OK: read data size"
            except:
                print "FAILED: read data size"


        outname = str(tile.zoom) + str(tile.row) + str(tile.col) + ".tif"
        outfile = os.path.join(outdata_directory, outname)
Example #33
0
def main(args):

    parser = argparse.ArgumentParser()
    parser.add_argument("-d", "--debug", action="store_true")
    parsed = parser.parse_args(args)
    global debug
    debug = parsed.debug
    scriptdir = os.path.dirname(os.path.realpath(__file__))

    testdata_directory = os.path.join(scriptdir, "testdata")
    outdata_directory = os.path.join(testdata_directory, "out")
    wgs84 = TilePyramid("geodetic")
    wgs84_meta = MetaTilePyramid(wgs84, 16)

    # TilePyramid
    #===========

    # tiles per zoomlevel
    try:
        matrix_width = wgs84.matrix_width(5)
        matrix_height = wgs84.matrix_height(5)
        assert (matrix_width, matrix_height) == (64, 32)
        print "tiles per zoomlevel OK"
    except:
        print "tiles per zoomlevel FAILED"
        raise


    # top left coordinate
    try:
        tile = wgs84.tile(5, 3, 3)
        tl = (tile.left,tile.top)
        assert tl == (-163.125, 73.125)
        print "top left coordinate OK"
    except:
        print "top left coordinate FAILED"
        print tl
        raise


    # tile bounding box
    try:
        tile = wgs84.tile(5, 3, 3)
        bbox = tile.bbox()
        testpolygon = Polygon([[-163.125, 73.125], [-157.5, 73.125],
            [-157.5, 67.5], [-163.125, 67.5], [-163.125, 73.125]])
        assert bbox.equals(testpolygon)
        print "tile bounding box OK"
    except:
        print "tile bounding box FAILED"
        raise


    # tile bounding box with buffer
    try:
        tile = wgs84.tile(5, 3, 3)
        bbox = tile.bbox(1)
        testpolygon = Polygon([[-163.14697265625, 73.14697265625],
            [-157.47802734375, 73.14697265625],
            [-157.47802734375, 67.47802734375],
            [-163.14697265625, 67.47802734375],
            [-163.14697265625, 73.14697265625]])
        assert bbox.equals(testpolygon)
        print "tile bounding box with buffer OK"
    except:
        print "tile bounding box with buffer FAILED"
        print bbox
        raise


    # tile bounds
    try:
        tile = wgs84.tile(5, 3, 3)
        bounds = tile.bounds()
        testbounds = (-163.125, 67.5, -157.5, 73.125)
        assert bounds == testbounds
        print "tile bounds OK"
    except:
        print "tile bounds FAILED"
        raise


    # tile bounds buffer
    try:
        tile = wgs84.tile(5, 3, 3)
        bounds = tile.bounds(1)
        testbounds = (-163.14697265625, 67.47802734375, -157.47802734375,
            73.14697265625)
        assert bounds == testbounds
        print "tile bounds with buffer OK"
    except:
        print "tile bounds wigh buffer FAILED"
        raise


    # test bounding box
    bbox_location = os.path.join(testdata_directory, "bbox.geojson")
    tiled_out = os.path.join(outdata_directory, "bbox_tiles.geojson")
    zoom = 5
    testtiles = [(5, 5, 33), (5, 6, 33), (5, 7, 33), (5, 8, 33), (5, 9, 33), (5, 10, 33),
        (5, 5, 34), (5, 6, 34), (5, 7, 34), (5, 8, 34), (5, 9, 34), (5, 10, 34), (5, 5, 35),
        (5, 6, 35), (5, 7, 35), (5, 8, 35), (5, 9, 35), (5, 10, 35),(5, 5, 36), (5, 6, 36),
        (5, 7, 36), (5, 8, 36), (5, 9, 36), (5, 10, 36), (5, 5, 37), (5, 6, 37), (5, 7, 37),
        (5, 8, 37), (5, 9, 37), (5, 10, 37),(5, 5, 38), (5, 6, 38), (5, 7, 38), (5, 8, 38),
        (5, 9, 38), (5, 10, 38), (5, 5, 39), (5, 6, 39), (5, 7, 39), (5, 8, 39), (5, 9, 39),
        (5, 10, 39), (5, 5, 40), (5, 6, 40), (5, 7, 40), (5, 8, 40), (5, 9, 40), (5, 10, 40),
        (5, 5, 41), (5, 6, 41), (5, 7, 41), (5, 8, 41), (5, 9, 41), (5, 10, 41)]
    with fiona.open(bbox_location) as bbox_file:
        try:
            bbox_tiles = [
                (tile.zoom, tile.row, tile.col)
                for tile in wgs84.tiles_from_bbox(bbox_file, zoom)
            ]
            assert len(set(bbox_tiles).symmetric_difference(set(testtiles))) == 0
            print "bounding box OK"
        except:
            print "bounding box FAILED"
            raise
    if debug:
        ## write debug output
        schema = {
            'geometry': 'Polygon',
            'properties': {'col': 'int', 'row': 'int'}
        }
        try:
            os.remove(tiled_out)
        except:
            pass
        with fiona.open(tiled_out, 'w', 'GeoJSON', schema) as sink:
            for tile in bbox_tiles:
                zoom, row, col = tile
                feature = {}
                feature['geometry'] = mapping(wgs84.tile_bbox(zoom, row, col))
                feature['properties'] = {}
                feature['properties']['col'] = col
                feature['properties']['row'] = row
                sink.write(feature)


    # tiles from Point
    point_location = os.path.join(testdata_directory, "point.geojson")
    tiled_out = os.path.join(outdata_directory, "point_tiles.geojson")
    zoom = 6
    testtile = [(6, 14, 69)]
    with fiona.open(point_location) as point_file:
        point = shape(point_file[0]["geometry"])
        try:
            point_tile = [
                (tile.zoom, tile.row, tile.col)
                for tile in wgs84.tiles_from_geom(point, zoom)
            ]
            assert point_tile == testtile
            print "Point OK"
        except:
            print point_tile, testtile
            print "Point FAILED"
            raise
    if debug:
        ## write debug output
        schema = {
            'geometry': 'Polygon',
            'properties': {'col': 'int', 'row': 'int'}
        }
        try:
            os.remove(tiled_out)
        except:
            pass
        with fiona.open(tiled_out, 'w', 'GeoJSON', schema) as sink:
            zoom, row, col = point_tile[0]
            feature = {}
            feature['geometry'] = mapping(wgs84.tile_bbox(zoom, row, col))
            feature['properties'] = {}
            feature['properties']['col'] = col
            feature['properties']['row'] = row
            sink.write(feature)

    # tiles from MultiPoint
    multipoint_location = os.path.join(testdata_directory,
        "multipoint.geojson")
    tiled_out = os.path.join(outdata_directory, "multipoint_tiles.geojson")
    zoom = 9
    testtiles = [(9, 113, 553), (9, 118, 558)]
    with fiona.open(multipoint_location) as multipoint_file:
        multipoint = shape(multipoint_file[0]["geometry"])
        try:
            multipoint_tiles = [
                (tile.zoom, tile.row, tile.col)
                for tile in wgs84.tiles_from_geom(multipoint, zoom)
            ]
            assert multipoint_tiles == testtiles
            print "MultiPoint OK"
        except:
            print "MultiPoint FAILED"
            print multipoint_tiles
            print testtiles
            raise
    if debug:
        ## write debug output
        schema = {
            'geometry': 'Polygon',
            'properties': {'col': 'int', 'row': 'int'}
        }
        try:
            os.remove(tiled_out)
        except:
            pass
        with fiona.open(tiled_out, 'w', 'GeoJSON', schema) as sink:
            for tile in multipoint_tiles:
                zoom, row, col = tile
                feature = {}
                feature['geometry'] = mapping(wgs84.tile_bbox(zoom, row, col))
                feature['properties'] = {}
                feature['properties']['col'] = col
                feature['properties']['row'] = row
                sink.write(feature)

    # tiles from LineString
    linestring_location = os.path.join(testdata_directory,
        "linestring.geojson")
    tiled_out = os.path.join(outdata_directory, "linestring_tiles.geojson")
    zoom = 6
    testtiles = [(6, 14, 66), (6, 14, 67), (6, 14, 68), (6, 14, 69), (6, 14, 70), (6, 15, 70),
        (6, 15, 71), (6, 16, 71), (6, 16, 72), (6, 15, 73), (6, 16, 73), (6, 15, 74)]
    with fiona.open(linestring_location) as linestring_file:
        linestring = shape(linestring_file[0]["geometry"])
        try:
            linestring_tiles = [
                (tile.zoom, tile.row, tile.col)
                for tile in wgs84.tiles_from_geom(linestring, zoom)
                ]
            assert len(set(linestring_tiles).symmetric_difference(set(testtiles))) == 0
            print "LineString OK"
        except:
            print "LineString FAILED"
            raise
    if debug:
        ## write debug output
        schema = {
            'geometry': 'Polygon',
            'properties': {'col': 'int', 'row': 'int'}
        }
        try:
            os.remove(tiled_out)
        except:
            pass
        with fiona.open(tiled_out, 'w', 'GeoJSON', schema) as sink:
            for tile in linestring_tiles:
                zoom, row, col = tile
                feature = {}
                feature['geometry'] = mapping(wgs84.tile_bbox(zoom, row, col))
                feature['properties'] = {}
                feature['properties']['col'] = col
                feature['properties']['row'] = row
                sink.write(feature)

    # tiles from MultiLineString
    multilinestring_location = os.path.join(testdata_directory,
        "multilinestring.geojson")
    tiled_out = os.path.join(outdata_directory,
        "multilinestring_tiles.geojson")
    zoom = 6
    testtiles = [(6, 14, 66), (6, 14, 67), (6, 14, 68), (6, 14, 69), (6, 14, 70), (6, 15, 70),
       (6, 15, 71), (6, 16, 71), (6, 16, 72), (6, 15, 73), (6, 16, 73), (6, 15, 74), (6, 21, 74),
       (6, 22, 74), (6, 24, 74), (6, 25, 74), (6, 28, 74), (6, 29, 74), (6, 20, 75), (6, 21, 75),
       (6, 22, 75), (6, 23, 75), (6, 24, 75), (6, 25, 75), (6, 26, 75), (6, 27, 75), (6, 28, 75),
       (6, 29, 75), (6, 30, 75), (6, 31, 75), (6, 25, 76)]
    with fiona.open(multilinestring_location) as multilinestring_file:
        multilinestring = shape(multilinestring_file[0]["geometry"])
        try:
            multilinestring_tiles = [
                (tile.zoom, tile.row, tile.col)
                for tile in wgs84.tiles_from_geom(multilinestring, zoom)
                ]
            assert len(set(multilinestring_tiles).symmetric_difference(set(testtiles))) == 0
            print "MultiLineString OK"
        except:
            print "MultiLineString FAILED"
            raise
    if debug:
        ## write debug output
        schema = {
            'geometry': 'Polygon',
            'properties': {'col': 'int', 'row': 'int'}
        }
        try:
            os.remove(tiled_out)
        except:
            pass
        with fiona.open(tiled_out, 'w', 'GeoJSON', schema) as sink:
            for tile in multilinestring_tiles:
                zoom, row, col = tile
                feature = {}
                feature['geometry'] = mapping(wgs84.tile_bbox(zoom, row, col))
                feature['properties'] = {}
                feature['properties']['col'] = col
                feature['properties']['row'] = row
                sink.write(feature)

    # tiles from Polygon
    polygon_location = os.path.join(testdata_directory,
        "polygon.geojson")
    tiled_out = os.path.join(outdata_directory, "polygon_tiles.geojson")
    zoom = 8
    testtiles = [(8, 60, 269), (8, 61, 269), (8, 60, 270), (8, 61, 270), (8, 60, 271),
        (8, 61, 271), (8, 60, 272), (8, 61, 272), (8, 60, 273), (8, 61, 273), (8, 59, 274),
        (8, 60, 274), (8, 61, 274), (8, 58, 275), (8, 59, 275), (8, 60, 275), (8, 61, 275),
        (8, 58, 276), (8, 59, 276), (8, 60, 276), (8, 61, 276), (8, 62, 276), (8, 58, 277),
        (8, 59, 277), (8, 60, 277), (8, 61, 277), (8, 58, 278), (8, 59, 278), (8, 60, 278),
        (8, 61, 278), (8, 58, 279), (8, 59, 279), (8, 60, 279), (8, 61, 279), (8, 58, 280),
        (8, 59, 280), (8, 60, 280)]
    with fiona.open(polygon_location) as polygon_file:
        polygon = shape(polygon_file[0]["geometry"])
        polygon_tiles = [
            (tile.zoom, tile.row, tile.col)
            for tile in wgs84.tiles_from_geom(polygon, zoom)
            ]
        try:
            assert len(set(polygon_tiles).symmetric_difference(set(testtiles))) == 0
            print "Polygon OK"
        except:
            print "Polygon FAILED"
            raise
    if debug:
        ## write debug output
        schema = {
            'geometry': 'Polygon',
            'properties': {'col': 'int', 'row': 'int'}
        }
        try:
            os.remove(tiled_out)
        except:
            pass
        with fiona.open(tiled_out, 'w', 'GeoJSON', schema) as sink:
            for tile in polygon_tiles:
                zoom, row, col = tile
                feature = {}
                feature['geometry'] = mapping(wgs84.tile_bbox(zoom, row, col))
                feature['properties'] = {}
                feature['properties']['col'] = col
                feature['properties']['row'] = row
                sink.write(feature)

    # tiles from MultiPolygon
    multipolygon_location = os.path.join(testdata_directory,
        "multipolygon.geojson")
    tiled_out = os.path.join(outdata_directory, "multipolygon_tiles.geojson")
    zoom = 10
    testtiles = [(10, 243, 1081), (10, 244, 1081), (10, 245, 1081), (10, 242, 1082),
        (10, 243, 1082), (10, 244, 1082), (10, 245, 1082), (10, 241, 1083), (10, 242, 1083),
        (10, 243, 1083), (10, 244, 1083), (10, 245, 1083), (10, 241, 1084), (10, 242, 1084),
        (10, 243, 1084), (10, 244, 1084), (10, 245, 1084), (10, 241, 1085), (10, 242, 1085),
        (10, 243, 1085), (10, 244, 1085), (10, 245, 1085), (10, 241, 1086), (10, 242, 1086),
        (10, 243, 1086), (10, 244, 1086), (10, 245, 1086), (10, 242, 1087), (10, 243, 1087),
        (10, 244, 1087), (10, 245, 1087), (10, 241, 1088), (10, 242, 1088), (10, 243, 1088),
        (10, 244, 1088), (10, 241, 1089), (10, 242, 1089), (10, 243, 1089), (10, 244, 1089),
        (10, 241, 1090), (10, 242, 1090), (10, 243, 1090), (10, 244, 1090), (10, 241, 1091),
        (10, 242, 1091), (10, 243, 1091), (10, 244, 1091), (10, 241, 1092), (10, 242, 1092),
        (10, 243, 1092), (10, 244, 1092), (10, 240, 1093), (10, 241, 1093), (10, 242, 1093),
        (10, 244, 1093), (10, 245, 1093), (10, 240, 1094), (10, 241, 1094), (10, 242, 1094),
        (10, 243, 1094), (10, 244, 1094), (10, 245, 1094), (10, 246, 1094), (10, 240, 1095),
        (10, 241, 1095), (10, 242, 1095), (10, 243, 1095), (10, 244, 1095), (10, 245, 1095),
        (10, 246, 1095), (10, 241, 1096), (10, 244, 1096), (10, 245, 1096), (10, 246, 1096),
        (10, 245, 1097), (10, 246, 1097)]
    with fiona.open(multipolygon_location) as multipolygon_file:
        multipolygon = shape(multipolygon_file[0]["geometry"])
        multipolygon_tiles = [
            (tile.zoom, tile.row, tile.col)
            for tile in wgs84.tiles_from_geom(multipolygon, zoom)
            ]
        try:
            assert len(set(multipolygon_tiles).symmetric_difference(set(testtiles))) == 0
            print "MultiPolygon OK"
        except:
            print "MultiPolygon FAILED"
            raise
    if debug:
        ## write debug output
        schema = {
            'geometry': 'Polygon',
            'properties': {'col': 'int', 'row': 'int'}
        }
        try:
            os.remove(tiled_out)
        except:
            pass
        with fiona.open(tiled_out, 'w', 'GeoJSON', schema) as sink:
            for tile in multipolygon_tiles:
                zoom, row, col = tile
                feature = {}
                feature['geometry'] = mapping(wgs84.tile_bbox(zoom, row, col))
                feature['properties'] = {}
                feature['properties']['col'] = col
                feature['properties']['row'] = row
                sink.write(feature)


    if debug:
        # writing output test files
        col, row = 2, 2
        zoom = 5
        metatiling = 2
        wgs84_meta = MetaTilePyramid(wgs84, metatiling)
        antimeridian_location = os.path.join(testdata_directory,
            "antimeridian.geojson")
        with fiona.open(antimeridian_location) as antimeridian_file:
            geometries = []
            for feature in antimeridian_file:
                geometries.append(shape(feature["geometry"]))
        antimeridian = cascaded_union(geometries)
        print "top left tile coordinates:"
        print "metaTilePyramid: %s" %([wgs84_meta.top_left_tile_coords(zoom, row, col)])
        print "tile bounding box"
        print "metaTilePyramid: %s" %([mapping(wgs84.tile_bbox(zoom, row, col))])
        print "tile bounds"
        print "metaTilePyramid: %s" %([wgs84_meta.tile_bounds(zoom, row, col)])
        print "tiles from bbox"
        #print "metaTilePyramid: %s" %([wgs84_meta.tiles_from_bbox(antimeridian, zoom)])
        print "tiles from geometry"

        ## write debug output
        tiled_out = os.path.join(outdata_directory, "tile_antimeridian_tiles.geojson")
        schema = {
            'geometry': 'Polygon',
            'properties': {'col': 'int', 'row': 'int'}
        }
        try:
            os.remove(tiled_out)
        except:
            pass
        tiles = wgs84.tiles_from_geom(antimeridian, zoom)
        print "TilePyramid: %s" %(len(tiles))
        with fiona.open(tiled_out, 'w', 'GeoJSON', schema) as sink:
            for tile in tiles:
                zoom, row, col = tile
                feature = {}
                feature['geometry'] = mapping(wgs84.tile_bbox(zoom, row, col))
                feature['properties'] = {}
                feature['properties']['col'] = col
                feature['properties']['row'] = row
                sink.write(feature)
        ## write debug output
        metatiled_out = os.path.join(outdata_directory, "metatile_antimeridian_tiles.geojson")
        schema = {
            'geometry': 'Polygon',
            'properties': {'col': 'int', 'row': 'int'}
        }
        try:
            os.remove(metatiled_out)
        except:
            pass
        metatiles = wgs84_meta.tiles_from_geom(antimeridian, zoom)
        print "metaTilePyramid: %s" %(len(metatiles))
        with fiona.open(metatiled_out, 'w', 'GeoJSON', schema) as sink:
            for metatile in metatiles:
                zoom, row, col = metatile
                feature = {}
                feature['geometry'] = mapping(wgs84_meta.tile_bbox(zoom, row, col))
                feature['properties'] = {}
                feature['properties']['col'] = col
                feature['properties']['row'] = row
                sink.write(feature)


    for metatiling in (1, 2, 4, 8, 16):
        wgs84_meta = MetaTilePyramid(wgs84, metatiling)
        for zoom in range(22):
            tilepyramid_width = wgs84.matrix_width(zoom)
            tilepyramid_height = wgs84.matrix_height(zoom)
            metatilepyramid_width = wgs84_meta.matrix_width(zoom)
            metatilepyramid_height = wgs84_meta.matrix_height(zoom)
            control_width = int(math.ceil(
                float(tilepyramid_width)/float(metatiling)
            ))
            control_height = int(math.ceil(
                float(tilepyramid_height)/float(metatiling)
            ))
            try:
                assert metatilepyramid_width == control_width
                assert metatilepyramid_height == control_height
            except:
                print "ERROR: metatile number"
                print "metatiling, zoom:", metatiling, zoom
                print "width:", metatilepyramid_width, control_width
                print "height:", metatilepyramid_height, control_height
                raise


    for metatiling in (1, 2, 4, 8, 16):
        wgs84_meta = MetaTilePyramid(wgs84, metatiling)
        for zoom in range(21):
            # check tuple
            assert isinstance(wgs84_meta.matrix_width(zoom), int)
            assert isinstance(wgs84_meta.matrix_height(zoom), int)

            # check metatile size
            metatile_x_size = round(wgs84_meta.metatile_x_size(zoom), ROUND)
            metatile_y_size = round(wgs84_meta.metatile_y_size(zoom), ROUND)
            # assert metatile size equals TilePyramid width and height at zoom 0
            if zoom == 0:
                try:
                    if metatiling == 1:
                        assert (metatile_x_size * 2) == wgs84.x_size
                    else:
                        assert metatile_x_size == wgs84.x_size
                    assert metatile_y_size == wgs84.y_size
                except:
                    print "ERROR: zoom 0 metatile size not correct"
                    print "metatiling, zoom:", metatiling, zoom
                    print "metatile_x_size:", wgs84_meta.metatiling, metatile_x_size, wgs84.x_size
                    print "metatile_y_size:", metatile_y_size, wgs84.y_size
                    raise
            ## assert metatile size within TilePyramid bounds
            try:
                assert (metatile_x_size > 0.0) and (
                    metatile_x_size <= wgs84.x_size)
                assert (metatile_y_size > 0.0) and (
                    metatile_y_size <= wgs84.y_size)
            except:
                print "ERROR: metatile size"
                print zoom
                print "metatile_x_size:", metatile_x_size, wgs84_meta.x_size
                print "metatile_y_size:", metatile_y_size, wgs84_meta.x_size
                raise
            ## calculate control size from tiles
            tile_x_size = wgs84.tile_x_size(zoom)
            tile_y_size = wgs84.tile_y_size(zoom)
            we_control_size = round(tile_x_size * float(metatiling), ROUND)
            if we_control_size > wgs84.x_size:
                we_control_size = wgs84.x_size
            ns_control_size = round(tile_y_size * float(metatiling), ROUND)

            if ns_control_size > wgs84.y_size:
                ns_control_size = wgs84.y_size
            try:
                assert metatile_x_size == we_control_size
                assert metatile_y_size == ns_control_size
            except:
                print "ERROR: metatile size and control sizes"
                print "zoom, metatiling:", zoom, metatiling
                print metatile_x_size, we_control_size
                print metatile_y_size, ns_control_size
                raise

            # check metatile pixelsize (resolution)
            pixel_x_size = round(wgs84.pixel_x_size(zoom), ROUND)
            ctr_pixel_x_size = round(wgs84_meta.pixel_x_size(zoom), ROUND)
            pixel_y_size = round(wgs84.pixel_y_size(zoom), ROUND)
            ctr_pixel_y_size = round(wgs84_meta.pixel_y_size(zoom), ROUND)
            try:
                assert pixel_x_size == ctr_pixel_x_size
                assert pixel_y_size == ctr_pixel_y_size
            except:
                print "ERROR: metatile pixel size"
                print "zoom, metatiling:", zoom, metatiling
                print "pixel_x_size:", pixel_x_size, ctr_pixel_x_size
                print "pixel_y_size:", pixel_y_size, ctr_pixel_y_size
                raise

    if debug:
        fiji_borders = os.path.join(testdata_directory, "fiji.geojson")
        with fiona.open(fiji_borders, "r") as fiji:
            geometries = []
            for feature in fiji:
                geometry = shape(feature['geometry'])
                geometries.append(geometry)
        union = cascaded_union(geometries)
        # tiles
        fiji_tiles = os.path.join(outdata_directory, "fiji_tiles.geojson")
        schema = {
            'geometry': 'Polygon',
            'properties': {'col': 'int', 'row': 'int'}
        }
        try:
            os.remove(fiji_tiles)
        except:
            pass
        metatiling = 4
        zoom = 10
        tiles = wgs84.tiles_from_geom(union, zoom)
        with fiona.open(fiji_tiles, 'w', 'GeoJSON', schema) as sink:
            for tile in tiles:
                zoom, row, col = tile
                feature = {}
                feature['geometry'] = mapping(wgs84.tile_bbox(zoom, row, col))
                feature['properties'] = {}
                feature['properties']['col'] = col
                feature['properties']['row'] = row
                sink.write(feature)

        # metatiles
        fiji_metatiles = os.path.join(outdata_directory, "fiji_metatiles.geojson")
        schema = {
            'geometry': 'Polygon',
            'properties': {'col': 'int', 'row': 'int'}
        }
        try:
            os.remove(fiji_metatiles)
        except:
            pass
        wgs84_meta = MetaTilePyramid(wgs84, metatiling)
        metatiles = wgs84_meta.tiles_from_geom(union, zoom)
        with fiona.open(fiji_metatiles, 'w', 'GeoJSON', schema) as sink:
            for metatile in metatiles:
                zoom, row, col = metatile
                feature = {}
                feature['geometry'] = mapping(wgs84_meta.tile_bbox(zoom, row, col))
                feature['properties'] = {}
                feature['properties']['col'] = col
                feature['properties']['row'] = row
                sink.write(feature)

    ## test get neighbors
    metatiling = 1
    wgs84_meta = MetaTilePyramid(wgs84, metatiling)
    tile = wgs84_meta.tile(5, 4, 3)
    assert len(tile.get_neighbors()) == 8

    ## test tile <--> metatile conversion
    metatile = [(10, 44, 33)]
    metatiling = 4
    wgs84_meta = MetaTilePyramid(wgs84, metatiling)
    small_tile = wgs84_meta.tilepyramid.tile(10, 178, 133)
    test_metatile = [
        (tile.zoom, tile.row, tile.col)
        for tile in wgs84_meta.tiles_from_bbox(
            small_tile.bbox(),
            10
        )
    ]
    try:
        assert metatile == test_metatile
        print "OK: metatile <-> tile conversion"
    except:
        print metatile, test_metatile
        raise
        print "ERROR: metatile <-> tile conversion"

    # test mercator tile pyramid
    tile_pyramid = TilePyramid("mercator")
    assert tile_pyramid.srid == 3857
    try:
        for zoom in range(15):
            assert (
                (tile_pyramid.matrix_width(zoom), tile_pyramid.matrix_height(zoom)
                ) == (2**zoom, 2**zoom)
            )
        print "OK: mercator tile matrix widths"
    except:
        print "ERROR: mercator tile matrix widths"


    from shapely.ops import transform
    from functools import partial
    import pyproj
    import mercantile

    example_tiles = [
        (12, 1024, 512),
        (6, 32, 16),
        (0, 0, 0),
        ]

    for tile_idx in example_tiles:
        (zoom, row, col) = tile_idx
        mercantile_ul = Point(
            mercantile.ul(col, row, zoom).lng,
            mercantile.ul(col, row, zoom).lat,
            )
        m_bounds = mercantile.bounds(col, row, zoom)
        mercantile_bbox = box(*m_bounds)
        project = partial(
            pyproj.transform,
            pyproj.Proj({"init": "epsg:3857"}),
            pyproj.Proj({"init": "epsg:4326"})
        )
        tilematrix_ul = transform(
            project,
            Point(
                Tile(tile_pyramid, zoom, row, col).left,
                Tile(tile_pyramid, zoom, row, col).top,
                )
            )
        tilematrix_bbox = transform(
            project,
            Tile(tile_pyramid, zoom, row, col).bbox()
            )

        try:
            assert mercantile_ul.almost_equals(
                tilematrix_ul,
                decimal=GEOM_EQUALS_ROUND
                )
            assert mercantile_bbox.almost_equals(
                tilematrix_bbox,
                decimal=GEOM_EQUALS_ROUND
                )
            print "OK: mercator tile coordinates"
        except AssertionError:
            print "ERROR: mercator tile coordinates"
            print tile_idx, mercantile_ul, tilematrix_ul
            print tile_idx, mercantile_bbox, tilematrix_bbox


    tile_idx = (12, 1024, 512)
    tile = Tile(tile_pyramid, *tile_idx)
    for child in tile.get_children():
        try:
            assert child.get_parent().id == tile.id
            print "OK: tile children and parent"
        except AssertionError:
            print child.parent.id, tile.id
            print "ERROR: tile children and parent"

    # get tiles over antimeridian:
    tile_pyramid = TilePyramid("geodetic")
    tile = tile_pyramid.tile(5, 0, 63)
    target_tiles = set(
        target_tile.id
        for target_tile in tile_pyramid.tiles_from_bounds(tile.bounds(256), 5)
    )
    control_tiles = set(
        [
            # tiles west of antimeridian
            (5, 0, 62),
            (5, 0, 63),
            (5, 1, 62),
            (5, 1, 63),
            # tiles east of antimeridian
            (5, 0, 0),
            (5, 1, 0)
        ]
    )
    try:
        diff = control_tiles.difference(target_tiles)
        assert len(diff) == 0
        print "OK: tiles over antimeridian"
    except AssertionError:
        print "ERROR: tiles over antimeridian"
        print diff
    tile = tile_pyramid.tile(5, 0, 0)
    target_tiles = set(
        target_tile.id
        for target_tile in tile_pyramid.tiles_from_bounds(tile.bounds(256), 5)
    )
    control_tiles = set(
        [
            # tiles west of antimeridian
            (5, 0, 63),
            (5, 1, 63),
            # tiles east of antimeridian
            (5, 0, 0),
            (5, 1, 0),
            (5, 0, 1),
            (5, 1, 1)
        ]
    )
    try:
        diff = control_tiles.difference(target_tiles)
        assert len(diff) == 0
        print "OK: tiles over antimeridian"
    except AssertionError:
        print "ERROR: tiles over antimeridian"
        print diff

    # get tiles over antimeridian:
    tile_pyramid = TilePyramid("mercator")
    tile = tile_pyramid.tile(5, 0, 31)
    target_tiles = set(
        target_tile.id
        for target_tile in tile_pyramid.tiles_from_bounds(tile.bounds(256), 5)
    )
    control_tiles = set(
        [
            # tiles west of antimeridian
            (5, 0, 30),
            (5, 0, 31),
            (5, 1, 30),
            (5, 1, 31),
            # tiles east of antimeridian
            (5, 0, 0),
            (5, 1, 0)
        ]
    )
    try:
        diff = control_tiles.difference(target_tiles)
        assert len(diff) == 0
        print "OK: tiles over antimeridian"
    except AssertionError:
        print "ERROR: tiles over antimeridian"
        print diff
    tile = tile_pyramid.tile(5, 0, 0)
    target_tiles = set(
        target_tile.id
        for target_tile in tile_pyramid.tiles_from_bounds(tile.bounds(256), 5)
    )
    control_tiles = set(
        [
            # tiles west of antimeridian
            (5, 0, 31),
            (5, 1, 31),
            # tiles east of antimeridian
            (5, 0, 0),
            (5, 1, 0),
            (5, 0, 1),
            (5, 1, 1)
        ]
    )
    try:
        diff = control_tiles.difference(target_tiles)
        assert len(diff) == 0
        print "OK: tiles over antimeridian"
    except AssertionError:
        print "ERROR: tiles over antimeridian"
        print diff

    # geometry over antimeridian
    tile_pyramid = TilePyramid("geodetic")
    geometry = loads("POLYGON ((184.375 90, 190 90, 180 84.375, 174.375 84.375, 184.375 90))")
    target_tiles = set(
        tile.id
        for tile in tile_pyramid.tiles_from_geom(geometry, 6)
        )
    control_tiles = set(
        [
            (6, 0, 127),
            (6, 1, 126),
            (6, 1, 127),
            (6, 0, 0),
            (6, 0, 1),
            (6, 0, 2),
            (6, 0, 3),
            (6, 1, 0),
            (6, 1, 1)
        ]
        )
    try:
        diff = control_tiles.difference(target_tiles)
        assert len(diff) == 0
        print "OK: geometry over antimeridian"
    except AssertionError:
        print "ERROR: geometry over antimeridian"
        print diff

    tile_pyramid = TilePyramid("mercator")
    geometry = loads("POLYGON ((-22037508.3427892 20037508.3427892, -20785164.07136488 20037508.3427892, -18785164.07136488 18785164.07136488, -20037508.3427892 18785164.07136488, -22037508.3427892 20037508.3427892))")
    target_tiles = set(
        tile.id
        for tile in tile_pyramid.tiles_from_geom(geometry, 6)
        )
    tile_pyramid_bbox = box(
        tile_pyramid.left,
        tile_pyramid.bottom,
        tile_pyramid.right,
        tile_pyramid.top
    )
    for tile in target_tiles:
        assert tile_pyramid.tile(*tile).bbox().within(tile_pyramid_bbox)
    control_tiles = set(
        [
            # west of antimeridian
            (6, 0, 60),
            (6, 0, 61),
            (6, 0, 62),
            (6, 0, 63),
            (6, 1, 62),
            (6, 1, 63),
            # east of antimeridian
            (6, 0, 0),
            (6, 1, 0),
            (6, 1, 1)
        ]
        )
    try:
        diff = control_tiles.difference(target_tiles)
        assert len(diff) == 0
        print "OK: geometry over antimeridian"
    except AssertionError:
        print "ERROR: geometry over antimeridian"
        print target_tiles
        print diff

    # tile shapes
    tile_pyramid = TilePyramid("mercator")
    col, row = (0, 0)
    for zoom in range(10):
        tile = Tile(tile_pyramid, zoom, col, row)
        assert tile.shape() == (256, 256)
    metatile_pyramid = MetaTilePyramid(tile_pyramid, metatiles=8)
    control_shapes = [
        (256, 256),
        (512, 512),
        (1024, 1024),
        (2048, 2048),
        (2048, 2048),
        (2048, 2048),
        (2048, 2048),
        (2048, 2048),
        (2048, 2048),
        (2048, 2048)
        ]
    for zoom, control_shape in zip(range(9), control_shapes):
        tile = Tile(metatile_pyramid, zoom, col, row)
        try:
            assert tile.shape() == control_shape
            print "OK: metatile shape at zoom ", zoom
        except AssertionError:
            print "ERROR: metatile shape at zoom", zoom
            print tile.id, tile.shape(), control_shape
            raise

    # tile shapes with pixelbuffer
    tile_pyramid = TilePyramid("mercator")
    test_tiles = [
        (0, 0, 0),
        (1, 0, 0), # top left
        (2, 0, 1), # top middle
        (2, 0, 3), # top right
        (2, 3, 3), # bottom right
        (2, 3, 0), # bottom left
        (2, 3, 2), # bottom middle
        (2, 2, 0), # left middle
        (2, 2, 3), # right middle
        ]
    pixelbuffer = 2
    tile_size = tile_pyramid.tile_size
    control_shapes = [
        (tile_size, tile_size+2*pixelbuffer),
        (tile_size+1*pixelbuffer, tile_size+2*pixelbuffer),  # top left
        (tile_size+1*pixelbuffer, tile_size+2*pixelbuffer),  # top middle
        (tile_size+1*pixelbuffer, tile_size+2*pixelbuffer),  # top right
        (tile_size+1*pixelbuffer, tile_size+2*pixelbuffer),  # bottom right
        (tile_size+1*pixelbuffer, tile_size+2*pixelbuffer),  # bottom left
        (tile_size+1*pixelbuffer, tile_size+2*pixelbuffer),  # bottom middle
        (tile_size+2*pixelbuffer, tile_size+2*pixelbuffer),  # left middle
        (tile_size+2*pixelbuffer, tile_size+2*pixelbuffer),  # right middle
        ]
    for test_tile, control_shape in zip(test_tiles, control_shapes):
        tile = Tile(tile_pyramid, *test_tile)
        try:
            assert tile.shape(pixelbuffer) == control_shape
            print "OK: tile pixelbuffer shape for tile ", test_tile
        except AssertionError:
            print "ERROR: tile pixelbuffer shape for tile", test_tile
            print tile.id, tile.shape(pixelbuffer), control_shape
    # metatile shapes with pixelbuffer
    pixelbuffer = 2
    metatiling = 8
    metatile_pyramid = MetaTilePyramid(tile_pyramid, metatiles=metatiling)
    tile_size = tile_pyramid.tile_size
    test_tiles = [
        (0, 0, 0),
        (1, 0, 0),
        (2, 0, 0),
        (3, 0, 0),
        (4, 0, 0),
        (5, 0, 0),  # top left
        (5, 0, 1),  # top middle
        (5, 0, 3),  # top right
        (5, 3, 3),  # bottom right
        (5, 3, 0),  # bottom left
        (5, 3, 2),  # bottom middle
        (5, 2, 0),  # left middle
        (5, 2, 3),  # right middle
        ]
    for test_tile in test_tiles:
        tile = Tile(metatile_pyramid, *test_tile)
        left, bottom, right, top = tile.bounds(pixelbuffer)
        control_shape = (
            int(round((top-bottom)/tile.pixel_y_size)),
            int(round((right-left)/tile.pixel_x_size))
            )
        try:
            assert tile.shape(pixelbuffer) == control_shape
            print "OK: tile pixelbuffer shape for metatile ", test_tile
        except AssertionError:
            print "ERROR: tile pixelbuffer shape for metatile", test_tile
            print tile.id, tile.shape(pixelbuffer), control_shape

    """intersecting function"""
    try:
        # equal metatiling:
        tile = Tile(TilePyramid("geodetic", metatiling=1), 3, 4, 5)
        pyramid = TilePyramid("geodetic", metatiling=1)
        assert len(tile.intersecting(pyramid)) == 1
        assert tile.intersecting(pyramid)[0].id == tile.id

        # different metatiling:
        tile = Tile(TilePyramid("geodetic", metatiling=1), 3, 4, 5)
        pyramid_metatiling = 2
        pyramid = TilePyramid("geodetic", metatiling=pyramid_metatiling)
        assert len(tile.intersecting(pyramid)) == 1
        intersect = tile.intersecting(pyramid)[0]
        assert tile.bbox().within(intersect.bbox())
        tile = Tile(TilePyramid("geodetic", metatiling=8), 13, 4, 5)
        pyramid_metatiling = 2
        pyramid = TilePyramid("geodetic", metatiling=pyramid_metatiling)
        assert len(tile.intersecting(pyramid)) == 16
        for intersect in tile.intersecting(pyramid):
            assert intersect.bbox().within(tile.bbox())
        tile_list = set(
            tile.id
            for tile in tile.intersecting(pyramid)
        )
        reversed_list = set(
            tile.id
            for tile in pyramid.intersecting(tile)
        )
        assert not len(tile_list.symmetric_difference(reversed_list))
        print "OK: intersecting function"
    except:
        print "ERROR: intersecting function"
        raise

    """affine objects"""
    try:
        for metatiling in [1, 2, 4, 8, 16]:
            pyramid = TilePyramid("geodetic", metatiling=metatiling)
            for zoom in range(22):
                tile = pyramid.tile(zoom, 0, 0)
                assert tile.pixel_x_size == tile.pixel_y_size
                assert tile.affine()[0] == -tile.affine()[4]
                assert tile.affine(pixelbuffer=10)[0] == \
                    -tile.affine(pixelbuffer=10)[4]
        print "OK: Affine objects"
    except:
        print "ERROR: Affine objects"
        raise