PMTiles Layer Support¶
This notebook demonstrates how to use PMTiles with anymap-ts MapLibre backend.
PMTiles is a single-file archive format for pyramids of map tiles that enables efficient web-native map serving without requiring a separate tile server.
Features¶
- Support for both vector and raster PMTiles
- Customizable styling for vector layers
- Layer opacity and visibility controls
- Integration with layer control panel
Installation¶
Make sure you have anymap-ts installed:
pip install anymap-ts
In [ ]:
Copied!
# %pip install anymap-ts
# %pip install anymap-ts
In [ ]:
Copied!
from anymap_ts import Map
from anymap_ts import Map
Example 1: Vector PMTiles - World Countries¶
Let's start with a vector PMTiles file showing world countries from Natural Earth data.
In [ ]:
Copied!
# Create a map centered on the world
m = Map(
center=[0, 20],
zoom=2,
)
# Add a vector PMTiles layer with custom styling
m.add_pmtiles_layer(
url="https://pmtiles.io/protomaps(vector)ODbL_firenze.pmtiles",
layer_id="firenze",
style={
"type": "fill",
"source-layer": "buildings",
"fill-color": "#ff6b6b",
"fill-opacity": 0.6,
"fill-outline-color": "#ffffff",
},
opacity=0.8,
)
# Add layer control
m.add_layer_control()
m
# Create a map centered on the world
m = Map(
center=[0, 20],
zoom=2,
)
# Add a vector PMTiles layer with custom styling
m.add_pmtiles_layer(
url="https://pmtiles.io/protomaps(vector)ODbL_firenze.pmtiles",
layer_id="firenze",
style={
"type": "fill",
"source-layer": "buildings",
"fill-color": "#ff6b6b",
"fill-opacity": 0.6,
"fill-outline-color": "#ffffff",
},
opacity=0.8,
)
# Add layer control
m.add_layer_control()
m
Example 2: Different Layer Types¶
PMTiles vector data can be styled using different layer types (fill, line, circle, symbol).
In [ ]:
Copied!
# Create a map focused on Firenze (Florence), Italy
m2 = Map(
center=[11.2558, 43.7696], # Florence coordinates
zoom=12,
style="https://demotiles.maplibre.org/style.json",
height="500px",
)
# Add roads as lines
m2.add_pmtiles_layer(
url="https://pmtiles.io/protomaps(vector)ODbL_firenze.pmtiles",
layer_id="roads",
style={
"type": "line",
"source-layer": "roads",
"line-color": "#2c3e50",
"line-width": 2,
"line-opacity": 0.8,
},
)
# Add buildings as fills
m2.add_pmtiles_layer(
url="https://pmtiles.io/protomaps(vector)ODbL_firenze.pmtiles",
layer_id="buildings",
style={
"type": "fill",
"source-layer": "buildings",
"fill-color": "#e74c3c",
"fill-opacity": 0.6,
"fill-outline-color": "#c0392b",
},
)
# Add points of interest as circles
m2.add_pmtiles_layer(
url="https://pmtiles.io/protomaps(vector)ODbL_firenze.pmtiles",
layer_id="pois",
style={
"type": "circle",
"source-layer": "pois",
"circle-color": "#f39c12",
"circle-radius": 4,
"circle-opacity": 0.9,
"circle-stroke-color": "#e67e22",
"circle-stroke-width": 1,
},
)
# Add layer control
m2.add_layer_control()
m2
# Create a map focused on Firenze (Florence), Italy
m2 = Map(
center=[11.2558, 43.7696], # Florence coordinates
zoom=12,
style="https://demotiles.maplibre.org/style.json",
height="500px",
)
# Add roads as lines
m2.add_pmtiles_layer(
url="https://pmtiles.io/protomaps(vector)ODbL_firenze.pmtiles",
layer_id="roads",
style={
"type": "line",
"source-layer": "roads",
"line-color": "#2c3e50",
"line-width": 2,
"line-opacity": 0.8,
},
)
# Add buildings as fills
m2.add_pmtiles_layer(
url="https://pmtiles.io/protomaps(vector)ODbL_firenze.pmtiles",
layer_id="buildings",
style={
"type": "fill",
"source-layer": "buildings",
"fill-color": "#e74c3c",
"fill-opacity": 0.6,
"fill-outline-color": "#c0392b",
},
)
# Add points of interest as circles
m2.add_pmtiles_layer(
url="https://pmtiles.io/protomaps(vector)ODbL_firenze.pmtiles",
layer_id="pois",
style={
"type": "circle",
"source-layer": "pois",
"circle-color": "#f39c12",
"circle-radius": 4,
"circle-opacity": 0.9,
"circle-stroke-color": "#e67e22",
"circle-stroke-width": 1,
},
)
# Add layer control
m2.add_layer_control()
m2
Example 3: Raster PMTiles¶
PMTiles also supports raster tiles for satellite imagery, digital elevation models, etc.
In [ ]:
Copied!
# Note: This is a placeholder example as we don't have a public raster PMTiles URL
# In practice, you would use your own raster PMTiles file
m3 = Map(
center=[-120.5, 35.5],
zoom=8,
style="https://demotiles.maplibre.org/style.json",
height="500px",
)
# Example of how to add a raster PMTiles layer
# m3.add_pmtiles_layer(
# url="https://example.com/satellite.pmtiles",
# layer_id="satellite",
# source_type="raster",
# opacity=0.8
# )
print("For raster PMTiles, use source_type='raster' and adjust opacity as needed.")
print("The layer will be added as a raster layer with the specified opacity.")
m3
# Note: This is a placeholder example as we don't have a public raster PMTiles URL
# In practice, you would use your own raster PMTiles file
m3 = Map(
center=[-120.5, 35.5],
zoom=8,
style="https://demotiles.maplibre.org/style.json",
height="500px",
)
# Example of how to add a raster PMTiles layer
# m3.add_pmtiles_layer(
# url="https://example.com/satellite.pmtiles",
# layer_id="satellite",
# source_type="raster",
# opacity=0.8
# )
print("For raster PMTiles, use source_type='raster' and adjust opacity as needed.")
print("The layer will be added as a raster layer with the specified opacity.")
m3
Layer Management¶
You can programmatically control PMTiles layers:
In [ ]:
Copied!
# Remove a layer
# m2.remove_pmtiles_layer("roads")
# Set layer visibility
# m2.set_visibility("buildings", False)
# Set layer opacity
# m2.set_opacity("buildings", 0.3)
print("Layer management methods:")
print("- remove_pmtiles_layer(layer_id)")
print("- set_visibility(layer_id, visible)")
print("- set_opacity(layer_id, opacity)")
# Remove a layer
# m2.remove_pmtiles_layer("roads")
# Set layer visibility
# m2.set_visibility("buildings", False)
# Set layer opacity
# m2.set_opacity("buildings", 0.3)
print("Layer management methods:")
print("- remove_pmtiles_layer(layer_id)")
print("- set_visibility(layer_id, visible)")
print("- set_opacity(layer_id, opacity)")
Style Configuration¶
The style parameter accepts MapLibre style properties:
For Fill Layers¶
style = {
"type": "fill",
"source-layer": "layer_name",
"fill-color": "#3388ff",
"fill-opacity": 0.6,
"fill-outline-color": "#ffffff"
}
For Line Layers¶
style = {
"type": "line",
"source-layer": "layer_name",
"line-color": "#ff0000",
"line-width": 2,
"line-opacity": 0.8
}
For Circle Layers¶
style = {
"type": "circle",
"source-layer": "layer_name",
"circle-color": "#00ff00",
"circle-radius": 5,
"circle-opacity": 0.9
}
Benefits of PMTiles¶
- Single File: No need for complex tile server infrastructure
- Efficient: HTTP range requests enable fast random access
- Scalable: Works with CDNs and cloud storage
- Portable: Easy to share and deploy
- Cost-effective: Reduces server costs and complexity
In [ ]:
Copied!
from anymap_ts import Map
# Use Example 2 which is centered on Florence at the right zoom
m = Map(
center=[11.2558, 43.7696],
zoom=14,
style="https://demotiles.maplibre.org/style.json",
height="500px",
)
m.add_pmtiles_layer(
url="https://pmtiles.io/protomaps(vector)ODbL_firenze.pmtiles",
layer_id="buildings",
style={
"type": "fill",
"source-layer": "buildings",
"fill-color": "#e74c3c",
"fill-opacity": 0.7,
"fill-outline-color": "#c0392b",
},
)
m
from anymap_ts import Map
# Use Example 2 which is centered on Florence at the right zoom
m = Map(
center=[11.2558, 43.7696],
zoom=14,
style="https://demotiles.maplibre.org/style.json",
height="500px",
)
m.add_pmtiles_layer(
url="https://pmtiles.io/protomaps(vector)ODbL_firenze.pmtiles",
layer_id="buildings",
style={
"type": "fill",
"source-layer": "buildings",
"fill-color": "#e74c3c",
"fill-opacity": 0.7,
"fill-outline-color": "#c0392b",
},
)
m
In [ ]:
Copied!
from anymap_ts import Map
m = Map(
center=[11.2558, 43.7696],
zoom=14,
style="https://demotiles.maplibre.org/style.json",
height="500px",
)
m.add_pmtiles_layer(
url="https://pmtiles.io/protomaps(vector)ODbL_firenze.pmtiles",
layer_id="buildings_test",
style={
"type": "fill",
"source-layer": "buildings",
"fill-color": "#e74c3c",
"fill-opacity": 0.7,
"fill-outline-color": "#c0392b",
},
)
m
from anymap_ts import Map
m = Map(
center=[11.2558, 43.7696],
zoom=14,
style="https://demotiles.maplibre.org/style.json",
height="500px",
)
m.add_pmtiles_layer(
url="https://pmtiles.io/protomaps(vector)ODbL_firenze.pmtiles",
layer_id="buildings_test",
style={
"type": "fill",
"source-layer": "buildings",
"fill-color": "#e74c3c",
"fill-opacity": 0.7,
"fill-outline-color": "#c0392b",
},
)
m
In [ ]:
Copied!
from anymap_ts import Map
m = Map(
center=[11.2558, 43.7696],
zoom=14,
style="https://demotiles.maplibre.org/style.json",
height="500px",
)
m.add_pmtiles_layer(
url="https://pmtiles.io/protomaps(vector)ODbL_firenze.pmtiles",
layer_id="buildings_v3",
style={
"type": "fill",
"source-layer": "buildings",
"fill-color": "#e74c3c",
"fill-opacity": 0.7,
"fill-outline-color": "#c0392b",
},
)
m
from anymap_ts import Map
m = Map(
center=[11.2558, 43.7696],
zoom=14,
style="https://demotiles.maplibre.org/style.json",
height="500px",
)
m.add_pmtiles_layer(
url="https://pmtiles.io/protomaps(vector)ODbL_firenze.pmtiles",
layer_id="buildings_v3",
style={
"type": "fill",
"source-layer": "buildings",
"fill-color": "#e74c3c",
"fill-opacity": 0.7,
"fill-outline-color": "#c0392b",
},
)
m
In [ ]:
Copied!
# Check if the ESM content includes the PMTiles handler
from anymap_ts import Map
m = Map()
esm_content = m._esm if isinstance(m._esm, str) else open(str(m._esm)).read()
print("Bundle size:", len(esm_content))
print("Has addPMTilesLayer:", "addPMTilesLayer" in esm_content)
print("Has pmtiles://:", "pmtiles://" in esm_content)
print(
"Has PMTilesProtocol:",
"PMTilesProtocol" in esm_content or "Protocol" in esm_content,
)
print("Has addProtocol:", "addProtocol" in esm_content)
# Check the specific call pattern
import re
matches = re.findall(r".{0,40}addProtocol.{0,60}", esm_content)
for match in matches[:5]:
print("Found:", match.strip())
# Check if the ESM content includes the PMTiles handler
from anymap_ts import Map
m = Map()
esm_content = m._esm if isinstance(m._esm, str) else open(str(m._esm)).read()
print("Bundle size:", len(esm_content))
print("Has addPMTilesLayer:", "addPMTilesLayer" in esm_content)
print("Has pmtiles://:", "pmtiles://" in esm_content)
print(
"Has PMTilesProtocol:",
"PMTilesProtocol" in esm_content or "Protocol" in esm_content,
)
print("Has addProtocol:", "addProtocol" in esm_content)
# Check the specific call pattern
import re
matches = re.findall(r".{0,40}addProtocol.{0,60}", esm_content)
for match in matches[:5]:
print("Found:", match.strip())