How to Inspect PMTiles Metadata with CLI Tools

To inspect PMTiles metadata with CLI tools, run pmtiles show <file.pmtiles> to dump the complete JSON header, or use the Python pmtiles package for programmatic access. The header exposes tile coverage bounds, min/max zoom levels, tile format, compression algorithm, and an embedded metadata JSON object. For CI/CD validation, pipe the output to jq or parse it directly with Python’s built-in json module.

Installation & Prerequisites

The official CLI is distributed via npm and requires Node.js 18+. Install it globally to access the pmtiles binary:

bash
npm install -g pmtiles

Verify the installation and check your version:

bash
pmtiles --version

The CLI reads the v3 binary header directly from disk using memory-mapped I/O, meaning it validates archives instantly without loading the entire file into RAM.

Core CLI Commands & jq Extraction

Once installed, you can inspect any .pmtiles archive with a single command. The raw output is minified JSON by default, making it ideal for piping into downstream tools.

bash
# Dump full header + embedded metadata as formatted JSON
pmtiles show archive.pmtiles | jq .

# Extract only the embedded metadata object
pmtiles show archive.pmtiles | jq '.metadata'

# Validate tile coverage, zoom range, and format
pmtiles show archive.pmtiles | jq '{
  bounds: .bounds,
  center: .center,
  min_zoom: .minZoom,
  max_zoom: .maxZoom,
  tile_type: .tileType,
  compression: .tileCompression
}'

For developers unfamiliar with JSON query syntax, the official jq manual provides comprehensive examples for filtering nested structures.

Decoding Header Fields & Numeric Codes

The PMTiles v3 specification uses compact integer enums to minimize header size. Misinterpreting these values is the most common cause of broken tile requests in production.

Field Numeric Code Meaning
tileType 1 PNG (Raster)
tileType 2 JPEG (Raster)
tileType 3 MVT (Vector Tile)
tileCompression 0 None
tileCompression 1 GZIP
tileCompression 2 Brotli
tileCompression 3 ZSTD

When designing Vector Tile Architecture & Format Fundamentals pipelines, verifying these fields early prevents mismatched decoder errors at runtime. For example, a tile server expecting Brotli-compressed MVTs will return 500 errors if the archive actually uses GZIP or contains raster tiles.

Bounds are stored as [min_lon, min_lat, max_lon, max_lat] in WGS84 decimal degrees. The center field dictates the initial viewport when loading the archive in web map libraries. Always cross-reference these values against your project’s geographic scope before deployment. For a complete breakdown of the binary layout and directory indexing, consult the PMTiles Specification Deep Dive.

Python Automation (Memory-Mapped Reader)

For Python automation builders, the pmtiles package provides a zero-copy reader that bypasses subprocess overhead. This approach is ideal for batch validation, metadata extraction, or custom tile server middleware.

python
from pmtiles.reader import Reader, MmapSource
import json
import sys

def inspect_pmtiles(filepath: str) -> dict:
    """Parse PMTiles v3 header and metadata without loading the full archive."""
    try:
        with open(filepath, "r+b") as f:
            source = MmapSource(f)
            reader = Reader(source)
            
            header = reader.header()
            metadata = json.loads(reader.metadata())
            
            # Convert E7 integer coordinates back to decimal degrees
            bounds = [
                header["min_lon_e7"] / 1e7,
                header["min_lat_e7"] / 1e7,
                header["max_lon_e7"] / 1e7,
                header["max_lat_e7"] / 1e7
            ]
            
            return {
                "spec_version": header["spec_version"],
                "tile_type": header["tile_type"],
                "tile_compression": header["tile_compression"],
                "min_zoom": header["min_zoom"],
                "max_zoom": header["max_zoom"],
                "bounds": bounds,
                "metadata_keys": list(metadata.keys()),
                "vector_layers": metadata.get("vector_layers", [])
            }
    except FileNotFoundError:
        print(f"Error: {filepath} not found.", file=sys.stderr)
        sys.exit(1)
    except Exception as e:
        print(f"Inspection failed: {e}", file=sys.stderr)
        sys.exit(1)

if __name__ == "__main__":
    result = inspect_pmtiles("data.pmtiles")
    print(json.dumps(result, indent=2))

The script safely handles missing files and malformed archives, printing diagnostics to stderr while returning structured JSON to stdout. Note that reader.metadata() returns a raw JSON string; you must call json.loads() to access nested keys like vector_layers or attribution.

CI/CD Validation Patterns

Integrating PMTiles inspection into automated workflows catches packaging errors before they reach staging or production. Below are two common patterns:

GitHub Actions / GitLab CI

yaml
- name: Validate PMTiles Archive
  run: |
    npm install -g pmtiles
    pmtiles show dist/map.pmtiles | jq -e '.tileType == 3 and .tileCompression == 2'

The -e flag makes jq exit with a non-zero status if the condition fails, automatically failing the pipeline when tile type or compression doesn’t match expectations.

Python-Based Validation Hook

python
import subprocess
import sys

def validate_pmtiles_cli(path: str) -> bool:
    result = subprocess.run(
        ["pmtiles", "show", path],
        capture_output=True, text=True, check=True
    )
    header = json.loads(result.stdout)
    return header["min_zoom"] <= header["max_zoom"] and len(header["bounds"]) == 4

When building automated tile generation workflows, always validate bounds alignment and zoom continuity. Out-of-range tiles or missing directory entries will silently degrade map rendering or trigger excessive fallback requests. The official PMTiles repository maintains updated tooling and test fixtures to help standardize validation across teams.