Skip to content

Commit

Permalink
Add default style for rendering PMTiles (#617)
Browse files Browse the repository at this point in the history
  • Loading branch information
giswqs authored Nov 26, 2023
1 parent 38ef18b commit cf44121
Show file tree
Hide file tree
Showing 5 changed files with 155 additions and 4 deletions.
14 changes: 12 additions & 2 deletions docs/notebooks/82_pmtiles.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,8 @@
" ],\n",
"}\n",
"\n",
"# style = leafmap.pmtiles_style(url) # Use default style\n",
"\n",
"m.add_pmtiles(\n",
" url, name='PMTiles', style=style, overlay=True, show=True, zoom_to_layer=True\n",
")\n",
Expand Down Expand Up @@ -184,6 +186,8 @@
" ],\n",
"}\n",
"\n",
"# style = leafmap.pmtiles_style(url) # Use default style\n",
"\n",
"m.add_pmtiles(\n",
" url, name='PMTiles', style=style, overlay=True, show=True, zoom_to_layer=True\n",
")\n",
Expand Down Expand Up @@ -223,7 +227,9 @@
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"metadata": {
"tags": []
},
"outputs": [],
"source": [
"m = leafmap.Map(center=[20, 0], zoom=2, height='800px')\n",
Expand All @@ -250,6 +256,8 @@
" ],\n",
"}\n",
"\n",
"# style = leafmap.pmtiles_style(url) # Use default style\n",
"\n",
"m.add_pmtiles(\n",
" url, name='Buildings', style=style, overlay=True, show=True, zoom_to_layer=False\n",
")\n",
Expand Down Expand Up @@ -384,6 +392,8 @@
" ],\n",
"}\n",
"\n",
"# style = leafmap.pmtiles_style(url) # Use default style\n",
"\n",
"m.add_pmtiles(url, name='Buildings', show=True, zoom_to_layer=True, style=style)\n",
"m"
]
Expand All @@ -405,7 +415,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.10.12"
"version": "3.11.5"
}
},
"nbformat": 4,
Expand Down
12 changes: 11 additions & 1 deletion examples/notebooks/82_pmtiles.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,8 @@
" ],\n",
"}\n",
"\n",
"# style = leafmap.pmtiles_style(url) # Use default style\n",
"\n",
"m.add_pmtiles(\n",
" url, name='PMTiles', style=style, overlay=True, show=True, zoom_to_layer=True\n",
")\n",
Expand Down Expand Up @@ -184,6 +186,8 @@
" ],\n",
"}\n",
"\n",
"# style = leafmap.pmtiles_style(url) # Use default style\n",
"\n",
"m.add_pmtiles(\n",
" url, name='PMTiles', style=style, overlay=True, show=True, zoom_to_layer=True\n",
")\n",
Expand Down Expand Up @@ -223,7 +227,9 @@
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"metadata": {
"tags": []
},
"outputs": [],
"source": [
"m = leafmap.Map(center=[20, 0], zoom=2, height='800px')\n",
Expand All @@ -250,6 +256,8 @@
" ],\n",
"}\n",
"\n",
"# style = leafmap.pmtiles_style(url) # Use default style\n",
"\n",
"m.add_pmtiles(\n",
" url, name='Buildings', style=style, overlay=True, show=True, zoom_to_layer=False\n",
")\n",
Expand Down Expand Up @@ -384,6 +392,8 @@
" ],\n",
"}\n",
"\n",
"# style = leafmap.pmtiles_style(url) # Use default style\n",
"\n",
"m.add_pmtiles(url, name='Buildings', show=True, zoom_to_layer=True, style=style)\n",
"m"
]
Expand Down
126 changes: 126 additions & 0 deletions leafmap/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -11363,6 +11363,132 @@ def pmtiles_metadata(input_file: str) -> Dict[str, Union[str, int, List[str]]]:
return metadata


def pmtiles_style(
url: str,
layers: Optional[Union[str, List[str]]] = None,
cmap: str = "Set3",
n_class: Optional[int] = None,
opacity: float = 0.5,
circle_radius: int = 5,
line_width: int = 1,
attribution: str = "PMTiles",
**kwargs,
):
"""
Generates a Mapbox style JSON for rendering PMTiles data.
Args:
url (str): The URL of the PMTiles file.
layers (str or list[str], optional): The layers to include in the style. If None, all layers will be included.
Defaults to None.
cmap (str, optional): The color map to use for styling the layers. Defaults to "Set3".
n_class (int, optional): The number of classes to use for styling. If None, the number of classes will be
determined automatically based on the color map. Defaults to None.
opacity (float, optional): The fill opacity for polygon layers. Defaults to 0.5.
circle_radius (int, optional): The circle radius for point layers. Defaults to 5.
line_width (int, optional): The line width for line layers. Defaults to 1.
attribution (str, optional): The attribution text for the data source. Defaults to "PMTiles".
Returns:
dict: The Mapbox style JSON.
Raises:
ValueError: If the layers argument is not a string or a list.
ValueError: If a layer specified in the layers argument does not exist in the PMTiles file.
"""

if cmap == "Set3":
palette = [
"#8dd3c7",
"#ffffb3",
"#bebada",
"#fb8072",
"#80b1d3",
"#fdb462",
"#b3de69",
"#fccde5",
"#d9d9d9",
"#bc80bd",
"#ccebc5",
"#ffed6f",
]
elif isinstance(cmap, list):
palette = cmap
else:
from .colormaps import get_palette

palette = ["#" + c for c in get_palette(cmap, n_class)]

n_class = len(palette)

metadata = pmtiles_metadata(url)
layer_names = metadata["layer_names"]

style = {
"version": 8,
"sources": {
"source": {
"type": "vector",
"url": "pmtiles://" + url,
"attribution": attribution,
}
},
"layers": [],
}

if layers is None:
layers = layer_names
elif isinstance(layers, str):
layers = [layers]
elif isinstance(layers, list):
for layer in layers:
if layer not in layer_names:
raise ValueError(f"Layer {layer} does not exist in the PMTiles file.")
else:
raise ValueError("The layers argument must be a string or a list.")

for i, layer_name in enumerate(layers):
layer_point = {
"id": f"{layer_name}_point",
"source": "source",
"source-layer": layer_name,
"type": "circle",
"paint": {
"circle-color": palette[i % n_class],
"circle-radius": circle_radius,
},
"filter": ["==", ["geometry-type"], "Point"],
}

layer_stroke = {
"id": f"{layer_name}_stroke",
"source": "source",
"source-layer": layer_name,
"type": "line",
"paint": {
"line-color": palette[i % n_class],
"line-width": line_width,
},
"filter": ["==", ["geometry-type"], "LineString"],
}

layer_fill = {
"id": f"{layer_name}_fill",
"source": "source",
"source-layer": layer_name,
"type": "fill",
"paint": {
"fill-color": palette[i % n_class],
"fill-opacity": opacity,
},
"filter": ["==", ["geometry-type"], "Polygon"],
}

style["layers"].extend([layer_point, layer_stroke, layer_fill])

return style


def raster_to_vector(
source, output, simplify_tolerance=None, dst_crs=None, open_args={}, **kwargs
):
Expand Down
2 changes: 2 additions & 0 deletions leafmap/foliumap.py
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,8 @@ def add_pmtiles(
"""

try:
if style is None:
style = pmtiles_style(url)
layer = PMTilesLayer(
url,
style=style,
Expand Down
5 changes: 4 additions & 1 deletion leafmap/leafmap.py
Original file line number Diff line number Diff line change
Expand Up @@ -597,7 +597,7 @@ def add_vector_tile(
def add_pmtiles(
self,
url,
style={},
style=None,
name="PMTiles",
show=True,
zoom_to_layer=True,
Expand Down Expand Up @@ -628,6 +628,9 @@ def add_pmtiles(
if "version" in kwargs:
del kwargs["version"]

if style is None:
style = pmtiles_style(url)

layer = ipyleaflet.PMTilesLayer(
url=url,
style=style,
Expand Down

0 comments on commit cf44121

Please sign in to comment.