mirror of
https://github.com/protomaps/PMTiles.git
synced 2026-02-04 10:51:07 +00:00
rio-pmtiles improvements to defaults (#545)
* rio-pmtiles: show progress by default * change --progress-bar to --silent [#338] * rio-pmtiles: change defaults from JPEG/256/nearest to WEBP/512/bilinear [#338] * rename title to name [#338] * rio-pmtiles: automatic guessing of maxzoom [#338] * determine maxzoom based on image dimensions and tile_size. * rio-pmtiles: 1.0.0
This commit is contained in:
@@ -1,5 +1,3 @@
|
|||||||
"""rio-pmtiles package"""
|
"""rio-pmtiles package"""
|
||||||
|
|
||||||
import sys
|
__version__ = "1.0.0"
|
||||||
|
|
||||||
__version__ = "0.0.6"
|
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ from rio_pmtiles.worker import init_worker, process_tile
|
|||||||
DEFAULT_NUM_WORKERS = None
|
DEFAULT_NUM_WORKERS = None
|
||||||
RESAMPLING_METHODS = [method.name for method in Resampling]
|
RESAMPLING_METHODS = [method.name for method in Resampling]
|
||||||
TILES_CRS = "EPSG:3857"
|
TILES_CRS = "EPSG:3857"
|
||||||
|
WEBMERC_EXTENT = 40075016.68
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -96,6 +97,17 @@ def extract_features(ctx, param, value):
|
|||||||
else:
|
else:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
def guess_maxzoom(crs, bounds, width, height, tile_size):
|
||||||
|
(west, east), (south, north) = transform(
|
||||||
|
crs, "EPSG:3857", bounds[::2], bounds[1::2]
|
||||||
|
)
|
||||||
|
if east <= -WEBMERC_EXTENT/2:
|
||||||
|
east = -east
|
||||||
|
res_x = (east - west) / width
|
||||||
|
res_y = (north - south) / height
|
||||||
|
raster_resolution = min(res_x, res_y)
|
||||||
|
return math.ceil(math.log2(WEBMERC_EXTENT / (tile_size * raster_resolution)))
|
||||||
|
|
||||||
|
|
||||||
@click.command(short_help="Export a dataset to PMTiles.")
|
@click.command(short_help="Export a dataset to PMTiles.")
|
||||||
@click.argument(
|
@click.argument(
|
||||||
@@ -106,8 +118,8 @@ def extract_features(ctx, param, value):
|
|||||||
metavar="INPUT [OUTPUT]",
|
metavar="INPUT [OUTPUT]",
|
||||||
)
|
)
|
||||||
@output_opt
|
@output_opt
|
||||||
@click.option("--title", help="PMTiles dataset title.")
|
@click.option("--name", help="PMTiles metadata name.")
|
||||||
@click.option("--description", help="PMTiles dataset description.")
|
@click.option("--description", help="PMTiles metadata description.")
|
||||||
@click.option(
|
@click.option(
|
||||||
"--overlay",
|
"--overlay",
|
||||||
"layer_type",
|
"layer_type",
|
||||||
@@ -123,12 +135,12 @@ def extract_features(ctx, param, value):
|
|||||||
"--format",
|
"--format",
|
||||||
"img_format",
|
"img_format",
|
||||||
type=click.Choice(["JPEG", "PNG", "WEBP"]),
|
type=click.Choice(["JPEG", "PNG", "WEBP"]),
|
||||||
default="JPEG",
|
default="WEBP",
|
||||||
help="Tile image format.",
|
help="Tile image format.",
|
||||||
)
|
)
|
||||||
@click.option(
|
@click.option(
|
||||||
"--tile-size",
|
"--tile-size",
|
||||||
default=256,
|
default=512,
|
||||||
show_default=True,
|
show_default=True,
|
||||||
type=int,
|
type=int,
|
||||||
help="Width and height of individual square tiles to create.",
|
help="Width and height of individual square tiles to create.",
|
||||||
@@ -166,7 +178,7 @@ def extract_features(ctx, param, value):
|
|||||||
@click.option(
|
@click.option(
|
||||||
"--resampling",
|
"--resampling",
|
||||||
type=click.Choice(RESAMPLING_METHODS),
|
type=click.Choice(RESAMPLING_METHODS),
|
||||||
default="nearest",
|
default="bilinear",
|
||||||
show_default=True,
|
show_default=True,
|
||||||
help="Resampling method to use.",
|
help="Resampling method to use.",
|
||||||
)
|
)
|
||||||
@@ -175,7 +187,7 @@ def extract_features(ctx, param, value):
|
|||||||
"--rgba", default=False, is_flag=True, help="Select RGBA output. For PNG or WEBP only."
|
"--rgba", default=False, is_flag=True, help="Select RGBA output. For PNG or WEBP only."
|
||||||
)
|
)
|
||||||
@click.option(
|
@click.option(
|
||||||
"--progress-bar", "-#", default=False, is_flag=True, help="Don't display progress bar."
|
"--silent", default=False, is_flag=True, help="Don't display progress bar."
|
||||||
)
|
)
|
||||||
@click.option(
|
@click.option(
|
||||||
"--cutline",
|
"--cutline",
|
||||||
@@ -212,7 +224,7 @@ def pmtiles(
|
|||||||
ctx,
|
ctx,
|
||||||
files,
|
files,
|
||||||
output,
|
output,
|
||||||
title,
|
name,
|
||||||
description,
|
description,
|
||||||
layer_type,
|
layer_type,
|
||||||
img_format,
|
img_format,
|
||||||
@@ -223,7 +235,7 @@ def pmtiles(
|
|||||||
dst_nodata,
|
dst_nodata,
|
||||||
resampling,
|
resampling,
|
||||||
rgba,
|
rgba,
|
||||||
progress_bar,
|
silent,
|
||||||
cutline,
|
cutline,
|
||||||
open_options,
|
open_options,
|
||||||
creation_options,
|
creation_options,
|
||||||
@@ -251,7 +263,7 @@ def pmtiles(
|
|||||||
nearest to the one at which one tile may contain the entire source
|
nearest to the one at which one tile may contain the entire source
|
||||||
dataset.
|
dataset.
|
||||||
|
|
||||||
If a title or description for the output file are not provided,
|
If a name or description for the output file are not provided,
|
||||||
they will be taken from the input dataset's filename.
|
they will be taken from the input dataset's filename.
|
||||||
|
|
||||||
This command is suited for small to medium (~1 GB) sized sources.
|
This command is suited for small to medium (~1 GB) sized sources.
|
||||||
@@ -286,7 +298,7 @@ def pmtiles(
|
|||||||
base_kwds.update(nodata=dst_nodata)
|
base_kwds.update(nodata=dst_nodata)
|
||||||
|
|
||||||
# Name and description.
|
# Name and description.
|
||||||
title = title or os.path.basename(src.name)
|
name = name or os.path.basename(src.name)
|
||||||
description = description or src.name
|
description = description or src.name
|
||||||
|
|
||||||
# Compute the geographic bounding box of the dataset.
|
# Compute the geographic bounding box of the dataset.
|
||||||
@@ -323,10 +335,8 @@ def pmtiles(
|
|||||||
if zoom_levels:
|
if zoom_levels:
|
||||||
minzoom, maxzoom = map(int, zoom_levels.split(".."))
|
minzoom, maxzoom = map(int, zoom_levels.split(".."))
|
||||||
else:
|
else:
|
||||||
zw = int(round(math.log(360.0 / (east - west), 2.0)))
|
minzoom = 0
|
||||||
zh = int(round(math.log(170.1022 / (north - south), 2.0)))
|
maxzoom = guess_maxzoom(src.crs, src.bounds, src.width, src.height, tile_size)
|
||||||
minzoom = min(zw, zh)
|
|
||||||
maxzoom = max(zw, zh)
|
|
||||||
|
|
||||||
log.debug("Zoom range: %d..%d", minzoom, maxzoom)
|
log.debug("Zoom range: %d..%d", minzoom, maxzoom)
|
||||||
|
|
||||||
@@ -364,7 +374,7 @@ def pmtiles(
|
|||||||
outfile.write(b"\x00" * 16384)
|
outfile.write(b"\x00" * 16384)
|
||||||
entries = []
|
entries = []
|
||||||
|
|
||||||
metadata = gzip.compress(json.dumps({'name':title,'type':layer_type,'description':description,'writer':f'rio-pmtiles {rio_pmtiles_version}'}).encode())
|
metadata = gzip.compress(json.dumps({'name':name,'type':layer_type,'description':description,'writer':f'rio-pmtiles {rio_pmtiles_version}'}).encode())
|
||||||
outfile.write(metadata)
|
outfile.write(metadata)
|
||||||
|
|
||||||
header = {}
|
header = {}
|
||||||
@@ -409,10 +419,10 @@ def pmtiles(
|
|||||||
z, x, y = tileid_to_zxy(tile_id)
|
z, x, y = tileid_to_zxy(tile_id)
|
||||||
yield mercantile.Tile(x,y,z)
|
yield mercantile.Tile(x,y,z)
|
||||||
|
|
||||||
if progress_bar:
|
if silent:
|
||||||
pbar = tqdm(total=len(tiles))
|
|
||||||
else:
|
|
||||||
pbar = None
|
pbar = None
|
||||||
|
else:
|
||||||
|
pbar = tqdm(total=len(tiles))
|
||||||
|
|
||||||
tile_data_offset = 0
|
tile_data_offset = 0
|
||||||
|
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ from rasterio.rio.main import main_group
|
|||||||
|
|
||||||
from pmtiles.reader import Reader, MmapSource, all_tiles
|
from pmtiles.reader import Reader, MmapSource, all_tiles
|
||||||
import rio_pmtiles.scripts.cli
|
import rio_pmtiles.scripts.cli
|
||||||
|
from rio_pmtiles.scripts.cli import guess_maxzoom
|
||||||
|
|
||||||
from conftest import mock
|
from conftest import mock
|
||||||
|
|
||||||
@@ -72,7 +73,7 @@ def test_export_tiles(tmpdir, data):
|
|||||||
|
|
||||||
with open(outputfile, 'rb') as f:
|
with open(outputfile, 'rb') as f:
|
||||||
src = MmapSource(f)
|
src = MmapSource(f)
|
||||||
assert len(list(all_tiles(src))) == 6
|
assert len(list(all_tiles(src))) == 19
|
||||||
|
|
||||||
def test_export_zoom(tmpdir, data):
|
def test_export_zoom(tmpdir, data):
|
||||||
inputfile = str(data.join("RGB.byte.tif"))
|
inputfile = str(data.join("RGB.byte.tif"))
|
||||||
@@ -96,7 +97,7 @@ def test_export_jobs(tmpdir, data):
|
|||||||
|
|
||||||
with open(outputfile, 'rb') as f:
|
with open(outputfile, 'rb') as f:
|
||||||
src = MmapSource(f)
|
src = MmapSource(f)
|
||||||
assert len(list(all_tiles(src))) == 6
|
assert len(list(all_tiles(src))) == 19
|
||||||
|
|
||||||
|
|
||||||
def test_export_src_nodata(tmpdir, data):
|
def test_export_src_nodata(tmpdir, data):
|
||||||
@@ -111,7 +112,7 @@ def test_export_src_nodata(tmpdir, data):
|
|||||||
|
|
||||||
with open(outputfile, 'rb') as f:
|
with open(outputfile, 'rb') as f:
|
||||||
src = MmapSource(f)
|
src = MmapSource(f)
|
||||||
assert len(list(all_tiles(src))) == 6
|
assert len(list(all_tiles(src))) == 19
|
||||||
|
|
||||||
|
|
||||||
def test_export_bilinear(tmpdir, data):
|
def test_export_bilinear(tmpdir, data):
|
||||||
@@ -125,7 +126,7 @@ def test_export_bilinear(tmpdir, data):
|
|||||||
|
|
||||||
with open(outputfile, 'rb') as f:
|
with open(outputfile, 'rb') as f:
|
||||||
src = MmapSource(f)
|
src = MmapSource(f)
|
||||||
assert len(list(all_tiles(src))) == 6
|
assert len(list(all_tiles(src))) == 19
|
||||||
|
|
||||||
|
|
||||||
def test_skip_empty(tmpdir, empty_data):
|
def test_skip_empty(tmpdir, empty_data):
|
||||||
@@ -153,7 +154,7 @@ def test_include_empty(tmpdir, empty_data):
|
|||||||
|
|
||||||
with open(outputfile, 'rb') as f:
|
with open(outputfile, 'rb') as f:
|
||||||
src = MmapSource(f)
|
src = MmapSource(f)
|
||||||
assert len(list(all_tiles(src))) == 6
|
assert len(list(all_tiles(src))) == 19
|
||||||
|
|
||||||
|
|
||||||
def test_invalid_format_rgba(tmpdir, empty_data):
|
def test_invalid_format_rgba(tmpdir, empty_data):
|
||||||
@@ -217,7 +218,6 @@ def test_progress_bar(tmpdir, data, filename):
|
|||||||
main_group,
|
main_group,
|
||||||
[
|
[
|
||||||
"pmtiles",
|
"pmtiles",
|
||||||
"-#",
|
|
||||||
"--zoom-levels",
|
"--zoom-levels",
|
||||||
"4..11",
|
"4..11",
|
||||||
"--rgba",
|
"--rgba",
|
||||||
@@ -249,7 +249,6 @@ def test_cutline_progress_bar(tmpdir, data, rgba_cutline_path, filename):
|
|||||||
main_group,
|
main_group,
|
||||||
[
|
[
|
||||||
"pmtiles",
|
"pmtiles",
|
||||||
"-#",
|
|
||||||
"--zoom-levels",
|
"--zoom-levels",
|
||||||
"4..11",
|
"4..11",
|
||||||
"--rgba",
|
"--rgba",
|
||||||
@@ -275,7 +274,6 @@ def test_invalid_cutline(tmpdir, data, rgba_points_path, filename):
|
|||||||
main_group,
|
main_group,
|
||||||
[
|
[
|
||||||
"pmtiles",
|
"pmtiles",
|
||||||
"-#",
|
|
||||||
"--zoom-levels",
|
"--zoom-levels",
|
||||||
"4..11",
|
"4..11",
|
||||||
"--rgba",
|
"--rgba",
|
||||||
@@ -288,3 +286,14 @@ def test_invalid_cutline(tmpdir, data, rgba_points_path, filename):
|
|||||||
],
|
],
|
||||||
)
|
)
|
||||||
assert result.exit_code == 1
|
assert result.exit_code == 1
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"crs,bounds,width,height,tile_size,maxzoom",
|
||||||
|
[
|
||||||
|
("EPSG:4326",[-180,-90,180,90],256,256,256,0),
|
||||||
|
("EPSG:4326",[-180,-90,180,90],512,512,256,1),
|
||||||
|
("EPSG:4326",[-180,-90,180.00000000007202,90],512,1,256,1),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
def test_guess_maxzoom(crs, bounds, width, height, tile_size, maxzoom):
|
||||||
|
assert guess_maxzoom(crs, bounds, width, height, tile_size) == maxzoom
|
||||||
|
|||||||
Reference in New Issue
Block a user