From 46b5e9c716d7a618cb03cf9cb917804454696511 Mon Sep 17 00:00:00 2001 From: Brandon Liu Date: Tue, 10 Feb 2026 10:48:57 -0500 Subject: [PATCH] python v3.7 (#639) * add unit test for mbtiles missing center/bounds * code and file cleanup * more details in python README.md --- python/pmtiles/README.md | 17 ++++++++++++++- python/pmtiles/pmtiles/convert.py | 25 +++++++++++++--------- python/pmtiles/setup.py | 2 +- python/pmtiles/test/test_convert.py | 33 +++++++++++++++++++++++------ 4 files changed, 59 insertions(+), 18 deletions(-) diff --git a/python/pmtiles/README.md b/python/pmtiles/README.md index 7e3fdb5..7007905 100644 --- a/python/pmtiles/README.md +++ b/python/pmtiles/README.md @@ -1,6 +1,21 @@ # pmtiles -`pip install pmtiles` +```sh +pip install pmtiles +``` + +## Running Tests + +```sh +python -m unittest test/test_* +``` + +## Uploading build + +```sh +python -m build +twine upload dist/* +``` ## Status diff --git a/python/pmtiles/pmtiles/convert.py b/python/pmtiles/pmtiles/convert.py index 147cd3f..702ea0d 100644 --- a/python/pmtiles/pmtiles/convert.py +++ b/python/pmtiles/pmtiles/convert.py @@ -223,9 +223,11 @@ def disk_to_pmtiles(directory_path, output, maxzoom, **kwargs): Licensed under BSD 3-Clause """ verbose = kwargs.get("verbose") + try: - metadata = json.load(open(os.path.join(directory_path, 'metadata.json'), 'r')) - except IOError: + with open(os.path.join(directory_path, "metadata.json")) as f: + metadata = json.load(f) + except FileNotFoundError: raise Exception("metadata.json not found in directory") tile_format = kwargs.get('tile_format', metadata.get("format")) @@ -321,14 +323,17 @@ def disk_to_pmtiles(directory_path, output, maxzoom, **kwargs): print(" Begin writing %s to .pmtiles ..." % (n_tiles), flush=True) for tileid, filepath in tileid_path_set: f = open(filepath, 'rb') - data = f.read() - # force gzip compression only for vector - if is_pbf and data[0:2] != b"\x1f\x8b": - data = gzip.compress(data) - writer.write_tile(tileid, data) - count = count + 1 - if verbose and (count % count_step) == 0: - print(" %s tiles inserted of %s" % (count, n_tiles), flush=True) + try: + data = f.read() + # force gzip compression only for vector + if is_pbf and data[0:2] != b"\x1f\x8b": + data = gzip.compress(data) + writer.write_tile(tileid, data) + count = count + 1 + if verbose and (count % count_step) == 0: + print(" %s tiles inserted of %s" % (count, n_tiles), flush=True) + finally: + f.close() if verbose and (count % count_step) != 0: print(" %s tiles inserted of %s" % (count, n_tiles)) diff --git a/python/pmtiles/setup.py b/python/pmtiles/setup.py index 6e07905..655608a 100644 --- a/python/pmtiles/setup.py +++ b/python/pmtiles/setup.py @@ -5,7 +5,7 @@ with open("README.md", "r") as fh: setuptools.setup( name="pmtiles", - version="3.6.0", + version="3.7.0", author="Brandon Liu", author_email="brandon@protomaps.com", description="Library and utilities to write and read PMTiles archives - cloud-optimized archives of map tiles.", diff --git a/python/pmtiles/test/test_convert.py b/python/pmtiles/test/test_convert.py index 7013176..83139b9 100644 --- a/python/pmtiles/test/test_convert.py +++ b/python/pmtiles/test/test_convert.py @@ -116,14 +116,14 @@ class TestConvert(unittest.TestCase): "json": '{"vector_layers":[{"abc":123}],"tilestats":{"def":456}}', } ) - self.assertEqual(header["min_lon_e7"], -180 * 10000000) + self.assertEqual(header["min_lon_e7"], -180 * 1e7) self.assertTrue(isinstance(header["min_lon_e7"], int)) - self.assertEqual(header["min_lat_e7"], -85 * 10000000) - self.assertEqual(header["max_lon_e7"], 180 * 10000000) - self.assertEqual(header["max_lat_e7"], 85 * 10000000) + self.assertEqual(header["min_lat_e7"], -85 * 1e7) + self.assertEqual(header["max_lon_e7"], 180 * 1e7) + self.assertEqual(header["max_lat_e7"], 85 * 1e7) self.assertEqual(header["tile_type"], TileType.MVT) - self.assertEqual(header["center_lon_e7"], -122.1906 * 10000000) - self.assertEqual(header["center_lat_e7"], 37.7599 * 10000000) + self.assertEqual(header["center_lon_e7"], -122.1906 * 1e7) + self.assertEqual(header["center_lat_e7"], 37.7599 * 1e7) self.assertEqual(header["center_zoom"], 11) self.assertEqual(header["min_zoom"], 1) self.assertEqual(header["max_zoom"], 2) @@ -132,3 +132,24 @@ class TestConvert(unittest.TestCase): self.assertTrue("name" in json_metadata) self.assertTrue("format" in json_metadata) self.assertTrue("compression" in json_metadata) + + def test_mbtiles_missing_bounds_center(self): + header, json_metadata = mbtiles_to_header_json( + { + "name": "test_name", + "format": "pbf", + "minzoom": "1", + "maxzoom": "2", + "attribution": "
abc
", + "compression": "gzip", + "json": '{"vector_layers":[{"abc":123}],"tilestats":{"def":456}}', + } + ) + self.assertEqual(header["min_lon_e7"], -180 * 1e7) + self.assertTrue(isinstance(header["min_lon_e7"], int)) + self.assertEqual(header["min_lat_e7"], -850511287) + self.assertEqual(header["max_lon_e7"], 180 * 1e7) + self.assertEqual(header["max_lat_e7"], 850511287) + self.assertEqual(header["center_lon_e7"], 0) + self.assertEqual(header["center_lat_e7"], 0) + self.assertEqual(header["center_zoom"], 1)