Example #1
0
def test_overwite_metadata(tmpdir):
    filename = str(tmpdir.join("test.mbtiles"))

    metadata = {"name": "test tiles", "version": "1.0.0"}
    new_metadata = {"name": "new test tiles", "version": "100000.0.0"}

    with MBtiles(filename, mode="w") as out:
        out.meta = metadata
        out.meta = new_metadata

    with sqlite3.connect(filename) as db:
        cursor = db.cursor()
        cursor.execute("SELECT name, value from metadata")
        out = {row[0]: row[1] for row in cursor.fetchall()}
        assert out == new_metadata

    # overwrite existing key, value
    with MBtiles(filename, mode="r+") as out:
        out.meta["name"] = "new new test tiles"

    with sqlite3.connect(filename) as db:
        cursor = db.cursor()
        cursor.execute("SELECT value from metadata WHERE name='name' LIMIT 1")
        row = cursor.fetchone()
        assert row is not None
        assert row[0] == "new new test tiles"
Example #2
0
def difference(leftfilename, rightfilename, outfilename, batch_size=1000):
    """Create new tileset from tiles in left that are not in right.
    
    Note: caller is responsible for updating metadata as needed.  Metadata is copied from target.

    Parameters
    ----------
    leftfilename : str
        first tileset filename
    rightfilename : str
        second tileset filename
    outfilename : str
        output tileset filename
    batch_size : int, optional (default: BATCH_SIZE)
        size of each batch to read from the source and write to the target
    """

    with MBtiles(leftfilename) as left, MBtiles(
            rightfilename) as right, MBtiles(outfilename, "w") as out:
        out.meta = left.meta

        for batch in left.list_tiles_batched(batch_size):
            tiles_to_copy = [
                tile for tile in batch if not right.has_tile(*tile)
            ]

            if tiles_to_copy:
                out.write_tiles(
                    Tile(*tile, data=left.read_tile(*tile))
                    for tile in tiles_to_copy)
Example #3
0
def extend(source_filename, target_filename, batch_size=1000):
    """
    Add tiles from source_filename to target_tileset that are not already in target.

    Equivalent to the union of tiles from source and target, but
    written to the target to cut down on additional IO of copying
    both to a new file.

    Note: caller is responsible for updating metadata as needed.

    Parameters
    ----------
    source_filename : str
        name of source tiles mbtiles file for additional tiles
    target_tileset : str
        name of target tiles mbtiles file for adding tiles to
    batch_size : int, optional (default: 1000)
        size of each batch to read from the source and write to the target
    """

    with MBtiles(target_filename,
                 "r+") as target, MBtiles(source_filename) as source:
        for batch in source.list_tiles_batched(batch_size):
            tiles_to_copy = [
                tile for tile in batch if not target.has_tile(*tile)
            ]

            if tiles_to_copy:
                target.write_tiles(
                    Tile(*tile, data=source.read_tile(*tile))
                    for tile in tiles_to_copy)
Example #4
0
def test_list_tiles_batched(tmpdir):
    zoom = 2

    filename = str(tmpdir.join("test.mbtiles"))
    with MBtiles(filename, mode="w") as out:
        tiles = []
        num_per_edge = 2**zoom
        for x in range(0, num_per_edge):
            for y in range(0, num_per_edge):
                tiles.append(Tile(zoom, x, y, b""))

        out.write_tiles(tiles)

    with MBtiles(filename, mode="r") as src:
        batch = list(src.list_tiles_batched())
        print("batch", batch)
        assert len(batch) == 1
        tiles = batch[0]
        assert len(tiles) == (2**zoom)**2
        assert isinstance(tiles[0], TileCoordinate)

        # test batch size
        batch = list(src.list_tiles_batched(2))
        assert len(batch) == ((2**zoom)**2) / 2
        tiles = batch[0]
        assert len(tiles) == 2
Example #5
0
def test_ranges(tmpdir):
    zooms = [0, 1, 2]
    filename = str(tmpdir.join("test.mbtiles"))
    with MBtiles(filename, mode="w") as out:
        tiles = []
        for zoom in zooms:
            num_per_edge = 2**zoom
            for x in range(0, num_per_edge):
                for y in range(0, num_per_edge):
                    tiles.append(Tile(zoom, x, y, b""))

        out.write_tiles(tiles)

    with MBtiles(filename, mode="r") as src:
        assert src.zoom_range() == (0, 2)

        assert src.row_range(0) == (0, 0)
        assert src.col_range(0) == (0, 0)

        assert src.row_range(1) == (0, 1)
        assert src.col_range(1) == (0, 1)

        assert src.row_range(2) == (0, 3)
        assert src.col_range(2) == (0, 3)

        # ranges beyond available zoom levels should be none
        assert src.row_range(3) == (None, None)
        assert src.col_range(3) == (None, None)
Example #6
0
def test_read_missing_tile(tmpdir, blank_png_tile):
    filename = str(tmpdir.join("test.mbtiles"))

    # Create mbtiles file with a tile to read
    with MBtiles(filename, mode="w") as out:
        out.write_tile(0, 0, 0, blank_png_tile)

    with MBtiles(filename, mode="r") as src:
        assert src.read_tile(1, 0, 0) is None
Example #7
0
def test_has_tile(tmpdir, blank_png_tile):
    filename = str(tmpdir.join("test.mbtiles"))

    # Create mbtiles file with a tile to read
    with MBtiles(filename, mode="w") as out:
        out.write_tile(0, 0, 0, blank_png_tile)

    with MBtiles(filename, mode="r") as src:
        assert src.has_tile(0, 0, 0) == True
        assert src.has_tile(10, 10, 10) == False
Example #8
0
def test_read_metadata(tmpdir):
    filename = str(tmpdir.join("test.mbtiles"))

    metadata = {"name": "test tiles", "version": "1.0.0"}

    with MBtiles(filename, mode="w") as out:
        out.meta = metadata

    with MBtiles(filename, mode="r") as src:
        src.meta == metadata
Example #9
0
def test_overwrite_tile(tmpdir, blank_png_tile):
    # Should not fail if we send in a duplicate tile
    filename = str(tmpdir.join("test.mbtiles"))
    with MBtiles(filename, mode="w") as out:
        out.write_tile(0, 0, 0, blank_png_tile)

        # overwrite tile previously written
        out.write_tile(0, 0, 0, b"123")

    with MBtiles(filename, mode="r") as src:
        assert src.read_tile(0, 0, 0) == b"123"
Example #10
0
def test_existing_file(tmpdir, blank_png_tile):
    filename = str(tmpdir.join("test.mbtiles"))

    # create a file first
    with MBtiles(filename, mode="w") as out:
        out.write_tile(0, 0, 0, blank_png_tile)

    assert os.path.exists(filename)

    # this should overwrite the previous
    with MBtiles(filename, mode="w") as out:
        out.write_tile(1, 0, 0, blank_png_tile)

    with MBtiles(filename, mode="r") as src:
        assert src.read_tile(0, 0, 0) is None
        assert src.read_tile(1, 0, 0) == blank_png_tile
Example #11
0
def test_difference(tmpdir):
    left = str(tmpdir.join("left.mbtiles"))
    right = str(tmpdir.join("right.mbtiles"))
    outfilename = str(tmpdir.join("out.mbtiles"))

    with MBtiles(left, mode="w") as out:
        out.write_tiles([Tile(0, 0, 0, b""), Tile(1, 0, 0, b"")])

    with MBtiles(right, mode="w") as out:
        out.write_tiles([Tile(0, 0, 0, b""), Tile(2, 0, 0, b"")])

    difference(left, right, outfilename)

    with MBtiles(outfilename) as src:
        tiles = set(src.list_tiles())
        assert tiles == {(1, 0, 0)}
Example #12
0
def test_extend(tmpdir):
    source = str(tmpdir.join("source.mbtiles"))
    target = str(tmpdir.join("target.mbtiles"))
    with MBtiles(source, mode="w") as out:
        out.write_tiles([Tile(0, 0, 0, b""), Tile(1, 0, 0, b"")])

    with MBtiles(target, mode="w") as out:
        out.write_tiles([Tile(0, 0, 0, b"123"), Tile(2, 0, 0, b"")])

    extend(source, target)

    with MBtiles(target) as src:
        tiles = set(src.list_tiles())
        assert tiles == {(0, 0, 0), (1, 0, 0), (2, 0, 0)}

        # Make sure we didn't overwrite a tile
        assert src.read_tile(0, 0, 0) == b"123"
Example #13
0
def test_list_tiles(tmpdir):
    zoom = 2

    filename = str(tmpdir.join("test.mbtiles"))
    with MBtiles(filename, mode="w") as out:
        tiles = []
        num_per_edge = 2**zoom
        for x in range(0, num_per_edge):
            for y in range(0, num_per_edge):
                tiles.append(Tile(zoom, x, y, b""))

        out.write_tiles(tiles)

    with MBtiles(filename, mode="r") as src:
        tiles = src.list_tiles()
        assert len(tiles) == (2**zoom)**2
        assert isinstance(tiles[0], TileCoordinate)
Example #14
0
def test_ranges_nonstandard(tmpdir):
    filename = str(tmpdir.join("test.mbtiles"))
    with MBtiles(filename, mode="w") as out:

        # outer bounds of tiles are rows [2, 5], columns [1, 7]
        tiles = [
            Tile(3, 1, 2, b""),
            Tile(3, 3, 5, b""),
            Tile(3, 6, 2, b""),
            Tile(3, 7, 3, b""),
        ]
        out.write_tiles(tiles)

    with MBtiles(filename, mode="r") as src:
        assert src.zoom_range() == (3, 3)
        assert src.row_range(3) == (2, 5)
        assert src.col_range(3) == (1, 7)
Example #15
0
def test_union(tmpdir):
    left = str(tmpdir.join("left.mbtiles"))
    right = str(tmpdir.join("right.mbtiles"))
    outfilename = str(tmpdir.join("out.mbtiles"))

    with MBtiles(left, mode="w") as out:
        out.write_tiles([Tile(0, 0, 0, b""), Tile(1, 0, 0, b"")])

    with MBtiles(right, mode="w") as out:
        out.write_tiles([Tile(0, 0, 0, b"123"), Tile(2, 0, 0, b"")])

    union(left, right, outfilename)

    with MBtiles(outfilename) as src:
        tiles = set(src.list_tiles())
        assert tiles == {(0, 0, 0), (1, 0, 0), (2, 0, 0)}

        # Make sure we didn't overwrite a tile
        assert src.read_tile(0, 0, 0) == b""
Example #16
0
def tif_to_mbtiles(
    infilename,
    outfilename,
    min_zoom,
    max_zoom,
    tile_size=256,
    metadata=None,
    tile_renderer=to_smallest_png,
):
    """Convert a tif to mbtiles, rendering each tile using tile_renderer.

    By default, this renders tiles as data using the smallest PNG image type
    for the data type of infilename.
    
    Parameters
    ----------
    infilename : path to input GeoTIFF file
    outfilename : path to output mbtiles file
    
    min_zoom : int
    max_zoom : int
    tile_size : int, optional (default: 256)
    metadata : dict, optional
        metadata dictionary to add to the mbtiles metadata
    tile_renderer : function, optional (default: to_smallest_png)
        function that takes as input the data array for the tile and returns a PNG
    """

    with rasterio.Env() as env:
        with rasterio.open(infilename) as src:
            with MBtiles(outfilename, mode="w") as mbtiles:
                meta = {
                    "tilejson": "2.0.0",
                    "version": "1.0.0",
                    "minzoom": min_zoom,
                    "maxzoom": max_zoom,
                }
                meta.update(get_mbtiles_meta(src, min_zoom))

                if metadata is not None:
                    meta.update(metadata)

                mbtiles.meta = meta

                for tile, data, transform in read_tiles(src,
                                                        min_zoom=min_zoom,
                                                        max_zoom=max_zoom,
                                                        tile_size=tile_size):
                    # Only write out non-empty tiles
                    if not np.all(data == src.nodata):
                        # flip tile Y to match xyz scheme
                        tiley = int(math.pow(2, tile.z)) - tile.y - 1
                        mbtiles.write_tile(tile.z, tile.x, tiley,
                                           tile_renderer(data))
Example #17
0
def test_tile_bounds(tmpdir):
    mbtiles_filename = str(tmpdir.join("test.mbtiles"))

    with TPK("tests/data/states_filled.tpk") as tpk:
        tpk.to_mbtiles(mbtiles_filename, zoom=[0], tile_bounds=True)

    with MBtiles(mbtiles_filename) as mbtiles:
        assert mbtiles.zoom_range() == (0, 0)

        # bounds calculated from tile 0 should be world bounds in web mercator coordinates
        assert mbtiles.meta[
            "bounds"] == "-180.000000,-85.051129,180.000000,85.051129"
Example #18
0
def test_write_tile(tmpdir, blank_png_tile):
    filename = str(tmpdir.join("test.mbtiles"))
    with MBtiles(filename, mode="w") as out:
        out.write_tile(0, 0, 0, blank_png_tile)

    with sqlite3.connect(filename) as db:
        cursor = db.cursor()
        cursor.execute(
            "SELECT tile_data FROM tiles "
            "where zoom_level=0 and tile_column=0 and tile_row=0 LIMIT 1")
        row = cursor.fetchone()
        assert row is not None
        assert blank_png_tile == str(row[0]) if IS_PY2 else row[0]
Example #19
0
def test_write_tiles(tmpdir, blank_png_tile):
    filename = str(tmpdir.join("test.mbtiles"))
    tiles = (Tile(1, 0, 0, blank_png_tile), Tile(1, 0, 1, blank_png_tile))

    with MBtiles(filename, mode="w") as out:
        out.write_tiles(tiles)

    with sqlite3.connect(filename) as db:
        cursor = db.cursor()
        cursor.execute("SELECT tile_data FROM tiles")
        rows = cursor.fetchall()
        assert len(rows) == 2
        for i, row in enumerate(rows):
            assert tiles[i].data == str(row[0]) if IS_PY2 else row[0]
Example #20
0
def test_write_metadata(tmpdir):
    filename = str(tmpdir.join("test.mbtiles"))

    metadata = {"name": "test tiles", "version": "1.0.0"}

    with MBtiles(filename, mode="w") as out:
        out.meta = metadata

    with sqlite3.connect(filename) as db:
        cursor = db.cursor()
        cursor.execute("SELECT name, value from metadata")
        out = {row[0]: row[1] for row in cursor.fetchall()}
        assert out == metadata

    # add a new key, value
    with MBtiles(filename, mode="r+") as out:
        out.meta["foo"] = "bar"

    with sqlite3.connect(filename) as db:
        cursor = db.cursor()
        cursor.execute('SELECT value from metadata WHERE name="foo" LIMIT 1')
        row = cursor.fetchone()
        assert row is not None
        assert row[0] == "bar"
Example #21
0
def test_export_mbtiles_tile_bounds(runner, tmpdir):
    tpk = "tests/data/states_filled.tpk"
    mbtiles_filename = str(tmpdir.join("test.mbtiles"))

    result = runner.invoke(cli, [
        "export", "mbtiles", tpk, mbtiles_filename, "-z", "0", "--tile-bounds"
    ])
    assert result.exit_code == 0
    print(result.output)
    assert os.path.exists(mbtiles_filename)

    with MBtiles(mbtiles_filename) as mbtiles:
        assert mbtiles.zoom_range() == (0, 0)
        assert mbtiles.meta[
            "bounds"] == "-180.000000,-85.051129,180.000000,85.051129"
Example #22
0
def test_nonstandard_zoom_levels(tmpdir):
    """Not all TPK file are created with zoom levels that start at 0.
    Make sure that we handle those where zoom level != level of detail ordinal value.

    Also verify that it writes a proper mbtiles file.
    """
    mbtiles_filename = str(tmpdir.join("test.mbtiles"))

    with TPK("tests/data/nonstandard_zoom_levels.tpk") as tpk:
        assert tpk.lods == [0, 1, 2]
        assert tpk.zoom_levels == [2, 3, 4]

        assert len(list(tpk.read_tiles())) == 22

        tpk.to_mbtiles(mbtiles_filename)

    with MBtiles(mbtiles_filename) as mbtiles:
        assert mbtiles.zoom_range() == (2, 4)
        assert mbtiles.row_range(2) == (2, 2)
        assert mbtiles.col_range(2) == (0, 1)
        assert mbtiles.row_range(3) == (4, 5)
        assert mbtiles.col_range(3) == (1, 2)
        assert mbtiles.row_range(4) == (8, 11)
        assert mbtiles.col_range(4) == (2, 5)
Example #23
0
def test_read_missing_file(tmpdir):
    mbtiles_filename = str(tmpdir.join("test.mbtiles"))
    with pytest.raises(IOError):
        MBtiles(mbtiles_filename)
min_zoom = 0
max_zoom = 0
mode = "r+"

# Approx bounds of South America
bounds = [-95.273438, -57.326521, -32.695313, 13.239945]

start = time()

# clear out any progress files
if mode == "w":
    for filename in glob.glob("progress-*.pickle"):
        os.remove(filename)


with MBtiles("../data/elevation2.mbtiles", mode) as mbtiles:  # FIXME: w => r+
    mbtiles.meta = {
        "name": "elevation",
        "description": "Mapzen Terrarium Elevation Tiles",
        "version": "1.0",
        "attribution": "Mapzen",
        "credits": "Mapzen",
        "type": "overlay",
        "format": "png",
        "bounds": ",".join(str(x) for x in bounds or WORLD_BOUNDS),
        "center": ",".join(str(x) for x in get_center(bounds or WORLD_BOUNDS)),
        "minzoom": str(min_zoom),
        "maxzoom": str(max_zoom),
    }

    download(
Example #25
0
    def to_mbtiles(self,
                   filename,
                   zoom=None,
                   tile_bounds=False,
                   drop_empty=False):
        """
        Export tile package to mbtiles v1.1 file, optionally limited to zoom
        levels.  If filename exists, it will be overwritten.  If filename
        does not include the suffix '.mbtiles' it will be added.

        Parameters
        ----------
        filename: string
            name of mbtiles file
        zoom: int or list-like of ints, default: None (all tiles exported)
            zoom levels to export to mbtiles
        tile_bounds: bool
            if True, will use the tile bounds of the highest zoom level exported to determine tileset bounds
        drop_empty: bool, default False
            if True, tiles that are empty will not be output
        """

        if self.format.lower() == "mixed":
            raise ValueError(
                "Mixed format tiles are not supported for export to mbtiles")

        if not filename.endswith(".mbtiles"):
            filename = "{0}.mbtiles".format(filename)

        with MBtiles(filename, "w") as mbtiles:
            if zoom is None:
                zoom = self.zoom_levels
            elif isinstance(zoom, int):
                zoom = [zoom]

            zoom = list(zoom)
            zoom.sort()

            tiles = (tile for tile in self.read_tiles(zoom, flip_y=True)
                     if not (drop_empty and hashlib.sha1(
                         tile.data).hexdigest() in EMPTY_TILES))

            mbtiles.write_tiles(tiles)

            if tile_bounds:
                # Calculate bounds based on maximum zoom to be exported
                highest_zoom = zoom[-1]
                min_row, max_row = mbtiles.row_range(highest_zoom)
                min_col, max_col = mbtiles.col_range(highest_zoom)

                # get upper left coordinate
                xmin, ymax = mercantile.ul(min_col, min_row, highest_zoom)

                # get bottom right coordinate
                # since we are using ul(), we need to go 1 tile beyond the range to get the right side of the
                # tiles we have
                xmax, ymin = mercantile.ul(max_col + 1, max_row + 1,
                                           highest_zoom)

                bounds = (xmin, ymin, xmax, ymax)

            else:
                bounds = self.bounds

            # Center zoom level is middle zoom level
            center = "{0:4f},{1:4f},{2}".format(
                bounds[0] + (bounds[2] - bounds[0]) / 2.0,
                bounds[1] + (bounds[3] - bounds[1]) / 2.0,
                (zoom[0] + zoom[-1]) // 2,
            )

            mbtiles.meta.update({
                "name":
                self.name,
                "description":
                self.summary,  # not description, which is optional
                "version":
                self.version,
                "attribution":
                self.attribution,
                "tags":
                self.tags,
                "credits":
                self.credits,
                "use_constraints":
                self.use_constraints,
                "type":
                "overlay",
                "format":
                self.format.lower().replace("jpeg", "jpg")[:3],
                "bounds":
                ",".join("{0:4f}".format(v) for v in bounds),
                "center":
                center,
                "minzoom":
                zoom[0],
                "maxzoom":
                zoom[-1],
                "legend":
                json.dumps(self.legend) if self.legend else "",
            })
Example #26
0
def test_invalid_mode(tmpdir):
    mbtiles_filename = str(tmpdir.join("test.mbtiles"))

    with pytest.raises(ValueError):
        MBtiles(mbtiles_filename, mode="r+w")
Example #27
0
    def to_mbtiles(self, filename, zoom=None):
        """
        Export tile package to mbtiles v1.1 file, optionally limited to zoom
        levels.  If filename exists, it will be overwritten.  If filename
        does not include the suffix '.mbtiles' it will be added.

        Parameters
        ----------
        filename: string
            name of mbtiles file
        zoom: int or list-like of ints, default: None (all tiles exported)
            zoom levels to export to mbtiles
        """

        if self.format.lower() == 'mixed':
            raise ValueError(
                'Mixed format tiles are not supported for export to mbtiles')

        if not filename.endswith('.mbtiles'):
            filename = '{0}.mbtiles'.format(filename)

        with MBtiles(filename, 'w') as mbtiles:
            if zoom is None:
                zoom = self.zoom_levels
            elif isinstance(zoom, int):
                zoom = [zoom]

            zoom = list(zoom)
            zoom.sort()

            mbtiles.write_tiles(self.read_tiles(zoom, flip_y=True))

            bounds = self.bounds
            center = '{0:4f},{1:4f},{2}'.format(
                bounds[0] + (bounds[2] - bounds[0]) / 2.0,
                bounds[1] + (bounds[3] - bounds[1]) / 2.0,
                max(zoom[0], int((zoom[-1] - zoom[0]) / 4.0))  # Tune this
            )

            mbtiles.meta.update({
                'name':
                self.name,
                'description':
                self.summary,  # not description, which is optional
                'version':
                self.version,
                'attribution':
                self.attribution,
                'tags':
                self.tags,
                'credits':
                self.credits,
                'use_constraints':
                self.use_constraints,
                'type':
                'overlay',
                'format':
                self.format.lower().replace('jpeg', 'jpg')[:3],
                'bounds':
                ','.join('{0:4f}'.format(v) for v in self.bounds),
                'center':
                center,
                'minzoom':
                str(zoom[0]),
                'maxzoom':
                str(zoom[-1]),
                'legend':
                json.dumps(self.legend) if self.legend else ''
            })