maplibre module¶
MapLibre GL JS implementation of the map widget.
This module provides the MapLibreMap class which implements an interactive map widget using the MapLibre GL JS library. MapLibre GL JS is an open-source fork of Mapbox GL JS, providing fast vector map rendering with WebGL.
Classes
MapLibreMap: Main map widget class for MapLibre GL JS.
Examples:
Basic usage of MapLibreMap:
>>> from anymap.maplibre import MapLibreMap
>>> m = MapLibreMap(center=[-74.0, 40.7], zoom=10)
>>> m.add_basemap("OpenStreetMap.Mapnik")
>>> m
MapLibreMap (MapWidget)
¶
MapLibre GL JS implementation of the map widget.
This class provides an interactive map widget using MapLibre GL JS, an open-source WebGL-based vector map renderer. It supports various data sources, custom styling, and interactive features.
Attributes:
Name | Type | Description |
---|---|---|
style |
Map style configuration (URL string or style object). |
|
bearing |
Map rotation in degrees (0-360). |
|
pitch |
Map tilt in degrees (0-60). |
|
antialias |
Whether to enable antialiasing for better rendering quality. |
Examples:
Creating a basic MapLibre map:
>>> m = MapLibreMap(
... center=[40.7749, -122.4194],
... zoom=12,
... style="3d-satellite"
... )
>>> m.add_basemap("OpenStreetMap.Mapnik")
Source code in anymap/maplibre.py
class MapLibreMap(MapWidget):
"""MapLibre GL JS implementation of the map widget.
This class provides an interactive map widget using MapLibre GL JS,
an open-source WebGL-based vector map renderer. It supports various
data sources, custom styling, and interactive features.
Attributes:
style: Map style configuration (URL string or style object).
bearing: Map rotation in degrees (0-360).
pitch: Map tilt in degrees (0-60).
antialias: Whether to enable antialiasing for better rendering quality.
Example:
Creating a basic MapLibre map:
>>> m = MapLibreMap(
... center=[40.7749, -122.4194],
... zoom=12,
... style="3d-satellite"
... )
>>> m.add_basemap("OpenStreetMap.Mapnik")
"""
# MapLibre-specific traits
style = traitlets.Union(
[traitlets.Unicode(), traitlets.Dict()],
default_value="dark-matter",
).tag(sync=True)
bearing = traitlets.Float(0.0).tag(sync=True)
pitch = traitlets.Float(0.0).tag(sync=True)
antialias = traitlets.Bool(True).tag(sync=True)
_draw_data = traitlets.Dict().tag(sync=True)
_terra_draw_data = traitlets.Dict().tag(sync=True)
_terra_draw_enabled = traitlets.Bool(False).tag(sync=True)
_layer_dict = traitlets.Dict().tag(sync=True)
# Define the JavaScript module path
_esm = _esm_maplibre
_css = _css_maplibre
def __init__(
self,
center: List[float] = [0, 20],
zoom: float = 1.0,
style: Union[str, Dict[str, Any]] = "dark-matter",
width: str = "100%",
height: str = "600px",
bearing: float = 0.0,
pitch: float = 0.0,
controls: Dict[str, str] = {
"navigation": "top-right",
"fullscreen": "top-right",
"scale": "bottom-left",
"globe": "top-right",
"layers": "top-right",
},
projection: str = "mercator",
add_sidebar: bool = False,
sidebar_visible: bool = False,
sidebar_width: int = 360,
sidebar_args: Optional[Dict] = None,
layer_manager_expanded: bool = True,
**kwargs: Any,
) -> None:
"""Initialize MapLibre map widget.
Args:
center: Map center coordinates as [longitude, latitude]. Default is [0, 20].
zoom: Initial zoom level (typically 0-20). Default is 1.0.
style: MapLibre style URL string or style object dictionary.
width: Widget width as CSS string (e.g., "100%", "800px").
height: Widget height as CSS string (e.g., "600px", "50vh").
bearing: Map bearing (rotation) in degrees (0-360).
pitch: Map pitch (tilt) in degrees (0-60).
controls: Dictionary of control names and their positions. Default is {
"navigation": "top-right",
"fullscreen": "top-right",
"scale": "bottom-left",
"globe": "top-right",
"layers": "top-right",
}.
projection: Map projection type. Can be "mercator" or "globe". Default is "mercator".
add_sidebar: Whether to add a sidebar to the map. Default is False.
sidebar_visible: Whether the sidebar is visible. Default is False.
sidebar_width: Width of the sidebar in pixels. Default is 360.
sidebar_args: Additional keyword arguments for the sidebar. Default is None.
layer_manager_expanded: Whether the layer manager is expanded. Default is True.
**kwargs: Additional keyword arguments passed to parent class.
"""
if isinstance(style, str):
style = construct_maplibre_style(style)
super().__init__(
center=center,
zoom=zoom,
width=width,
height=height,
style=style,
bearing=bearing,
pitch=pitch,
**kwargs,
)
self.layer_dict = {}
self.layer_dict["Background"] = {
"layer": {
"id": "Background",
"type": "background",
},
"opacity": 1.0,
"visible": True,
"type": "background",
"color": None,
}
# Initialize the _layer_dict trait with the layer_dict content
self._layer_dict = dict(self.layer_dict)
# Initialize current state attributes
self._current_center = center
self._current_zoom = zoom
self._current_bearing = bearing
self._current_pitch = pitch
self._current_bounds = None # Will be set after map loads
# Register event handler to update current state
self.on_map_event("moveend", self._update_current_state)
self._style = style
self.style_dict = {}
for layer in self.get_style_layers():
self.style_dict[layer["id"]] = layer
self.source_dict = {}
if projection.lower() == "globe":
self.set_projection(
{
"type": [
"interpolate",
["linear"],
["zoom"],
10,
"vertical-perspective",
12,
"mercator",
]
}
)
self.controls = {}
for control, position in controls.items():
if control == "layers":
self.add_layer_control(position)
else:
self.add_control(control, position)
self.controls[control] = position
if sidebar_args is None:
sidebar_args = {}
if "sidebar_visible" not in sidebar_args:
sidebar_args["sidebar_visible"] = sidebar_visible
if "sidebar_width" not in sidebar_args:
if isinstance(sidebar_width, str):
sidebar_width = int(sidebar_width.replace("px", ""))
sidebar_args["min_width"] = sidebar_width
sidebar_args["max_width"] = sidebar_width
if "expanded" not in sidebar_args:
sidebar_args["expanded"] = layer_manager_expanded
self.sidebar_args = sidebar_args
self.layer_manager = None
self.container = None
if add_sidebar:
self._ipython_display_ = self._patched_display
def show(
self,
sidebar_visible: bool = False,
min_width: int = 360,
max_width: int = 360,
sidebar_content: Optional[Any] = None,
**kwargs: Any,
) -> None:
"""
Displays the map with an optional sidebar.
Args:
sidebar_visible (bool): Whether the sidebar is visible. Defaults to False.
min_width (int): Minimum width of the sidebar in pixels. Defaults to 250.
max_width (int): Maximum width of the sidebar in pixels. Defaults to 300.
sidebar_content (Optional[Any]): Content to display in the sidebar. Defaults to None.
**kwargs (Any): Additional keyword arguments.
Returns:
None
"""
return Container(
self,
sidebar_visible=sidebar_visible,
min_width=min_width,
max_width=max_width,
sidebar_content=sidebar_content,
**kwargs,
)
def create_container(
self,
sidebar_visible: bool = None,
min_width: int = None,
max_width: int = None,
expanded: bool = None,
**kwargs: Any,
):
"""
Creates a container widget for the map with an optional sidebar.
This method initializes a `LayerManagerWidget` and a `Container` widget to display the map
alongside a sidebar. The sidebar can be customized with visibility, width, and additional content.
Args:
sidebar_visible (bool): Whether the sidebar is visible. Defaults to False.
min_width (int): Minimum width of the sidebar in pixels. Defaults to 360.
max_width (int): Maximum width of the sidebar in pixels. Defaults to 360.
expanded (bool): Whether the `LayerManagerWidget` is expanded by default. Defaults to True.
**kwargs (Any): Additional keyword arguments passed to the `Container` widget.
Returns:
Container: The created container widget with the map and sidebar.
"""
if sidebar_visible is None:
sidebar_visible = self.sidebar_args.get("sidebar_visible", False)
if min_width is None:
min_width = self.sidebar_args.get("min_width", 360)
if max_width is None:
max_width = self.sidebar_args.get("max_width", 360)
if expanded is None:
expanded = self.sidebar_args.get("expanded", True)
if self.layer_manager is None:
self.layer_manager = LayerManagerWidget(self, expanded=expanded)
container = Container(
host_map=self,
sidebar_visible=sidebar_visible,
min_width=min_width,
max_width=max_width,
sidebar_content=[self.layer_manager],
**kwargs,
)
self.container = container
self.container.sidebar_widgets["Layers"] = self.layer_manager
return container
def _repr_html_(self, **kwargs: Any) -> None:
"""
Displays the map in an IPython environment.
Args:
**kwargs (Any): Additional keyword arguments.
Returns:
None
"""
filename = os.environ.get("MAPLIBRE_OUTPUT", None)
replace_key = os.environ.get("MAPTILER_REPLACE_KEY", False)
if filename is not None:
self.to_html(filename, replace_key=replace_key)
def _patched_display(
self,
**kwargs: Any,
) -> None:
"""
Displays the map in an IPython environment with a patched display method.
Args:
**kwargs (Any): Additional keyword arguments.
Returns:
None
"""
if self.container is not None:
container = self.container
else:
sidebar_visible = self.sidebar_args.get("sidebar_visible", False)
min_width = self.sidebar_args.get("min_width", 360)
max_width = self.sidebar_args.get("max_width", 360)
expanded = self.sidebar_args.get("expanded", True)
if self.layer_manager is None:
self.layer_manager = LayerManagerWidget(self, expanded=expanded)
container = Container(
host_map=self,
sidebar_visible=sidebar_visible,
min_width=min_width,
max_width=max_width,
sidebar_content=[self.layer_manager],
**kwargs,
)
container.sidebar_widgets["Layers"] = self.layer_manager
self.container = container
if "google.colab" in sys.modules:
import ipyvue as vue
display(vue.Html(children=[]), container)
else:
display(container)
def add_layer_manager(
self,
expanded: bool = True,
height: str = "40px",
layer_icon: str = "mdi-layers",
close_icon: str = "mdi-close",
label="Layers",
background_color: str = "#f5f5f5",
*args: Any,
**kwargs: Any,
) -> None:
if self.layer_manager is None:
self.layer_manager = LayerManagerWidget(
self,
expanded=expanded,
height=height,
layer_icon=layer_icon,
close_icon=close_icon,
label=label,
background_color=background_color,
*args,
**kwargs,
)
def set_sidebar_content(
self, content: Union[widgets.VBox, List[widgets.Widget]]
) -> None:
"""
Replaces all content in the sidebar (except the toggle button).
Args:
content (Union[widgets.VBox, List[widgets.Widget]]): The new content for the sidebar.
"""
if self.container is not None:
self.container.set_sidebar_content(content)
def add_to_sidebar(
self,
widget: widgets.Widget,
add_header: bool = True,
widget_icon: str = "mdi-tools",
close_icon: str = "mdi-close",
label: str = "My Tools",
background_color: str = "#f5f5f5",
height: str = "40px",
expanded: bool = True,
**kwargs: Any,
) -> None:
"""
Appends a widget to the sidebar content.
Args:
widget (Optional[Union[widgets.Widget, List[widgets.Widget]]]): Initial widget(s) to display in the content box.
widget_icon (str): Icon for the header. See https://pictogrammers.github.io/@mdi/font/2.0.46/ for available icons.
close_icon (str): Icon for the close button. See https://pictogrammers.github.io/@mdi/font/2.0.46/ for available icons.
background_color (str): Background color of the header. Defaults to "#f5f5f5".
label (str): Text label for the header. Defaults to "My Tools".
height (str): Height of the header. Defaults to "40px".
expanded (bool): Whether the panel is expanded by default. Defaults to True.
**kwargs (Any): Additional keyword arguments for the parent class.
"""
if self.container is None:
self.create_container(**self.sidebar_args)
self.container.add_to_sidebar(
widget,
add_header=add_header,
widget_icon=widget_icon,
close_icon=close_icon,
label=label,
background_color=background_color,
height=height,
expanded=expanded,
host_map=self,
**kwargs,
)
def remove_from_sidebar(
self, widget: widgets.Widget = None, name: str = None
) -> None:
"""
Removes a widget from the sidebar content.
Args:
widget (widgets.Widget): The widget to remove from the sidebar.
name (str): The name of the widget to remove from the sidebar.
"""
if self.container is not None:
self.container.remove_from_sidebar(widget, name)
def set_sidebar_width(self, min_width: int = None, max_width: int = None) -> None:
"""
Dynamically updates the sidebar's minimum and maximum width.
Args:
min_width (int, optional): New minimum width in pixels. If None, keep current.
max_width (int, optional): New maximum width in pixels. If None, keep current.
"""
if self.container is None:
self.create_container()
self.container.set_sidebar_width(min_width, max_width)
@property
def sidebar_widgets(self) -> Dict[str, widgets.Widget]:
"""
Returns a dictionary of widgets currently in the sidebar.
Returns:
Dict[str, widgets.Widget]: A dictionary where keys are the labels of the widgets and values are the widgets themselves.
"""
return self.container.sidebar_widgets
def set_style(self, style: Union[str, Dict[str, Any]]) -> None:
"""Set the map style.
Args:
style: Map style as URL string or style object dictionary.
"""
if isinstance(style, str):
self.style = style
else:
self.call_js_method("setStyle", style)
def set_bearing(self, bearing: float) -> None:
"""Set the map bearing (rotation).
Args:
bearing: Map rotation in degrees (0-360).
"""
self.bearing = bearing
def set_pitch(self, pitch: float) -> None:
"""Set the map pitch (tilt).
Args:
pitch: Map tilt in degrees (0-60).
"""
self.pitch = pitch
def set_layout_property(self, layer_id: str, name: str, value: Any) -> None:
"""Set a layout property for a layer.
Args:
layer_id: Unique identifier of the layer.
name: Name of the layout property to set.
value: Value to set for the property.
"""
self.call_js_method("setLayoutProperty", layer_id, name, value)
def set_paint_property(self, layer_id: str, name: str, value: Any) -> None:
"""Set a paint property for a layer.
Args:
layer_id: Unique identifier of the layer.
name: Name of the paint property to set.
value: Value to set for the property.
"""
self.call_js_method("setPaintProperty", layer_id, name, value)
def set_visibility(self, layer_id: str, visible: bool) -> None:
"""Set the visibility of a layer.
Args:
layer_id: Unique identifier of the layer.
visible: Whether the layer should be visible.
"""
if visible:
visibility = "visible"
else:
visibility = "none"
if layer_id == "Background":
for layer in self.get_style_layers():
self.set_layout_property(layer["id"], "visibility", visibility)
else:
self.set_layout_property(layer_id, "visibility", visibility)
if layer_id in self.layer_dict:
self.layer_dict[layer_id]["visible"] = visible
self._update_layer_controls()
def set_opacity(self, layer_id: str, opacity: float) -> None:
"""Set the opacity of a layer.
Args:
layer_id: Unique identifier of the layer.
opacity: Opacity value between 0.0 (transparent) and 1.0 (opaque).
"""
layer_type = self.get_layer_type(layer_id)
if layer_id == "Background":
for layer in self.get_style_layers():
layer_type = layer.get("type")
if layer_type != "symbol":
self.set_paint_property(
layer["id"], f"{layer_type}-opacity", opacity
)
else:
self.set_paint_property(layer["id"], "icon-opacity", opacity)
self.set_paint_property(layer["id"], "text-opacity", opacity)
return
if layer_id in self.layer_dict:
layer_type = self.layer_dict[layer_id]["layer"]["type"]
prop_name = f"{layer_type}-opacity"
self.layer_dict[layer_id]["opacity"] = opacity
self._update_layer_controls()
elif layer_id in self.style_dict:
layer = self.style_dict[layer_id]
layer_type = layer.get("type")
prop_name = f"{layer_type}-opacity"
if "paint" in layer:
layer["paint"][prop_name] = opacity
if layer_type != "symbol":
self.set_paint_property(layer_id, f"{layer_type}-opacity", opacity)
else:
self.set_paint_property(layer_id, "icon-opacity", opacity)
self.set_paint_property(layer_id, "text-opacity", opacity)
def set_projection(self, projection: Dict[str, Any]) -> None:
"""Set the map projection.
Args:
projection: Projection configuration dictionary.
"""
# Store projection in persistent state
self._projection = projection
self.call_js_method("setProjection", projection)
def set_terrain(
self,
source: str = "https://elevation-tiles-prod.s3.amazonaws.com/terrarium/{z}/{x}/{y}.png",
exaggeration: float = 1.0,
tile_size: int = 256,
encoding: str = "terrarium",
source_id: str = "terrain-dem",
) -> None:
"""Add terrain visualization to the map.
Args:
source: URL template for terrain tiles. Defaults to AWS elevation tiles.
exaggeration: Terrain exaggeration factor. Defaults to 1.0.
tile_size: Tile size in pixels. Defaults to 256.
encoding: Encoding for the terrain tiles. Defaults to "terrarium".
source_id: Unique identifier for the terrain source. Defaults to "terrain-dem".
"""
# Add terrain source
self.add_source(
source_id,
{
"type": "raster-dem",
"tiles": [source],
"tileSize": tile_size,
"encoding": encoding,
},
)
# Set terrain on the map
terrain_config = {"source": source_id, "exaggeration": exaggeration}
# Store terrain configuration in persistent state
self._terrain = terrain_config
self.call_js_method("setTerrain", terrain_config)
def get_layer_type(self, layer_id: str) -> Optional[str]:
"""Get the type of a layer.
Args:
layer_id: Unique identifier of the layer.
Returns:
Layer type string, or None if layer doesn't exist.
"""
if layer_id in self._layers:
return self._layers[layer_id]["type"]
else:
return None
def get_style(self):
"""
Get the style of the map.
Returns:
Dict: The style of the map.
"""
if self._style is not None:
if isinstance(self._style, str):
response = requests.get(self._style, timeout=10)
style = response.json()
elif isinstance(self._style, dict):
style = self._style
else:
style = {}
return style
else:
return {}
def get_style_layers(self, return_ids=False, sorted=True) -> List[str]:
"""
Get the names of the basemap layers.
Returns:
List[str]: The names of the basemap layers.
"""
style = self.get_style()
if "layers" in style:
layers = style["layers"]
if return_ids:
ids = [layer["id"] for layer in layers]
if sorted:
ids.sort()
return ids
else:
return layers
else:
return []
def add_layer(
self,
layer_id: str,
layer: Dict[str, Any],
before_id: Optional[str] = None,
opacity: Optional[float] = 1.0,
visible: Optional[bool] = True,
) -> None:
"""Add a layer to the map.
Args:
layer_id: Unique identifier for the layer.
layer_config: Layer configuration dictionary containing
properties like type, source, paint, and layout.
before_id: Optional layer ID to insert this layer before.
If None, layer is added on top.
"""
# Store layer in local state for persistence
current_layers = dict(self._layers)
current_layers[layer_id] = layer
self._layers = current_layers
# Call JavaScript method with before_id if provided
if before_id:
self.call_js_method("addLayer", layer, before_id)
else:
self.call_js_method("addLayer", layer, layer_id)
self.set_visibility(layer_id, visible)
self.set_opacity(layer_id, opacity)
self.layer_dict[layer_id] = {
"layer": layer,
"opacity": opacity,
"visible": visible,
"type": layer["type"],
# "color": color,
}
# Update the _layer_dict trait to trigger JavaScript sync
self._layer_dict = dict(self.layer_dict)
if self.layer_manager is not None:
self.layer_manager.refresh()
# Update layer controls if they exist
self._update_layer_controls()
def add_geojson_layer(
self,
layer_id: str,
geojson_data: Dict[str, Any],
layer_type: str = "fill",
paint: Optional[Dict[str, Any]] = None,
before_id: Optional[str] = None,
) -> None:
"""Add a GeoJSON layer to the map.
Args:
layer_id: Unique identifier for the layer.
geojson_data: GeoJSON data as a dictionary.
layer_type: Type of layer (e.g., 'fill', 'line', 'circle', 'symbol').
paint: Optional paint properties for styling the layer.
before_id: Optional layer ID to insert this layer before.
"""
source_id = f"{layer_id}_source"
# Add source
self.add_source(source_id, {"type": "geojson", "data": geojson_data})
# Add layer
layer_config = {"id": layer_id, "type": layer_type, "source": source_id}
if paint:
layer_config["paint"] = paint
self.add_layer(layer_id, layer_config, before_id)
def add_marker(self, lng: float, lat: float, popup: Optional[str] = None) -> None:
"""Add a marker to the map.
Args:
lng: Longitude coordinate for the marker.
lat: Latitude coordinate for the marker.
popup: Optional popup text to display when marker is clicked.
"""
marker_data = {"coordinates": [lng, lat], "popup": popup}
self.call_js_method("addMarker", marker_data)
def fit_bounds(self, bounds: List[List[float]], padding: int = 50) -> None:
"""Fit the map to given bounds.
Args:
bounds: Bounding box as [[south, west], [north, east]].
padding: Padding around the bounds in pixels.
"""
self.call_js_method("fitBounds", bounds, {"padding": padding})
def add_tile_layer(
self,
layer_id: str,
source_url: str,
attribution: Optional[str] = None,
opacity: Optional[float] = 1.0,
visible: Optional[bool] = True,
minzoom: Optional[int] = None,
maxzoom: Optional[int] = None,
paint: Optional[Dict[str, Any]] = None,
layout: Optional[Dict[str, Any]] = None,
before_id: Optional[str] = None,
**kwargs: Any,
) -> None:
"""Add a raster tile layer to the map.
Args:
layer_id: Unique identifier for the layer.
source_url: URL template for the tile source (e.g., 'https://example.com/{z}/{x}/{y}.png').
attribution: Optional attribution text for the tile source.
opacity: Layer opacity between 0.0 and 1.0.
visible: Whether the layer should be visible initially.
minzoom: Minimum zoom level for the layer.
maxzoom: Maximum zoom level for the layer.
paint: Optional paint properties for the layer.
layout: Optional layout properties for the layer.
before_id: Optional layer ID to insert this layer before.
**kwargs: Additional source configuration options.
"""
source_id = f"{layer_id}_source"
# Add raster source
self.add_source(
source_id,
{"type": "raster", "tiles": [source_url], "tileSize": 256, **kwargs},
)
# Add raster layer
layer_config = {"id": layer_id, "type": "raster", "source": source_id}
if paint:
layer_config["paint"] = paint
if layout:
layer_config["layout"] = layout
self.add_layer(layer_id, layer_config, before_id)
def add_vector_layer(
self,
layer_id: str,
source_url: str,
source_layer: str,
layer_type: str = "fill",
paint: Optional[Dict[str, Any]] = None,
layout: Optional[Dict[str, Any]] = None,
before_id: Optional[str] = None,
) -> None:
"""Add a vector tile layer to the map.
Args:
layer_id: Unique identifier for the layer.
source_url: URL for the vector tile source.
source_layer: Name of the source layer within the vector tiles.
layer_type: Type of layer (e.g., 'fill', 'line', 'circle', 'symbol').
paint: Optional paint properties for styling the layer.
layout: Optional layout properties for the layer.
before_id: Optional layer ID to insert this layer before.
"""
source_id = f"{layer_id}_source"
# Add vector source
self.add_source(source_id, {"type": "vector", "url": source_url})
# Add vector layer
layer_config = {
"id": layer_id,
"type": layer_type,
"source": source_id,
"source-layer": source_layer,
}
if paint:
layer_config["paint"] = paint
if layout:
layer_config["layout"] = layout
self.add_layer(layer_id, layer_config, before_id)
def add_image_layer(
self,
layer_id: str,
image_url: str,
coordinates: List[List[float]],
paint: Optional[Dict[str, Any]] = None,
before_id: Optional[str] = None,
) -> None:
"""Add an image layer to the map.
Args:
layer_id: Unique identifier for the layer.
image_url: URL of the image to display.
coordinates: Corner coordinates of the image as [[top-left], [top-right], [bottom-right], [bottom-left]].
Each coordinate should be [longitude, latitude].
paint: Optional paint properties for the image layer.
before_id: Optional layer ID to insert this layer before.
"""
source_id = f"{layer_id}_source"
# Add image source
self.add_source(
source_id, {"type": "image", "url": image_url, "coordinates": coordinates}
)
# Add raster layer for the image
layer_config = {"id": layer_id, "type": "raster", "source": source_id}
if paint:
layer_config["paint"] = paint
self.add_layer(layer_id, layer_config, before_id)
def add_control(
self,
control_type: str,
position: str = "top-right",
options: Optional[Dict[str, Any]] = None,
) -> None:
"""Add a control to the map.
Args:
control_type: Type of control ('navigation', 'scale', 'fullscreen', 'geolocate', 'attribution', 'globe')
position: Position on map ('top-left', 'top-right', 'bottom-left', 'bottom-right')
options: Additional options for the control
"""
control_options = options or {}
control_options["position"] = position
# Store control in persistent state
control_key = f"{control_type}_{position}"
current_controls = dict(self._controls)
current_controls[control_key] = {
"type": control_type,
"position": position,
"options": control_options,
}
self._controls = current_controls
self.call_js_method("addControl", control_type, control_options)
def remove_control(
self,
control_type: str,
position: str = "top-right",
) -> None:
"""Remove a control from the map.
Args:
control_type: Type of control to remove ('navigation', 'scale', 'fullscreen', 'geolocate', 'attribution', 'globe')
position: Position where the control was added ('top-left', 'top-right', 'bottom-left', 'bottom-right')
"""
# Remove control from persistent state
control_key = f"{control_type}_{position}"
current_controls = dict(self._controls)
if control_key in current_controls:
del current_controls[control_key]
self._controls = current_controls
self.call_js_method("removeControl", control_type, position)
def add_layer_control(
self,
position: str = "top-right",
collapsed: bool = True,
layers: Optional[List[str]] = None,
options: Optional[Dict[str, Any]] = None,
) -> None:
"""Add a collapsible layer control panel to the map.
The layer control is a collapsible panel that allows users to toggle
visibility and adjust opacity of map layers. It displays as an icon
similar to other controls, and expands when clicked.
Args:
position: Position on map ('top-left', 'top-right', 'bottom-left', 'bottom-right')
collapsed: Whether the control starts collapsed
layers: List of layer IDs to include. If None, includes all layers
options: Additional options for the control
"""
control_options = options or {}
control_options.update(
{
"position": position,
"collapsed": collapsed,
"layers": layers,
}
)
# Get current layer states for initialization
layer_states = {}
target_layers = layers if layers is not None else list(self.layer_dict.keys())
# Always include Background layer for controlling map style layers
if layers is None or "Background" in layers:
layer_states["Background"] = {
"visible": True,
"opacity": 1.0,
"name": "Background",
}
for layer_id in target_layers:
if layer_id in self.layer_dict and layer_id != "Background":
layer_info = self.layer_dict[layer_id]
layer_states[layer_id] = {
"visible": layer_info.get("visible", True),
"opacity": layer_info.get("opacity", 1.0),
"name": layer_id, # Use layer_id as display name by default
}
control_options["layerStates"] = layer_states
# Store control in persistent state
control_key = f"layer_control_{position}"
current_controls = dict(self._controls)
current_controls[control_key] = {
"type": "layer_control",
"position": position,
"options": control_options,
}
self._controls = current_controls
self.call_js_method("addControl", "layer_control", control_options)
def add_geocoder_control(
self,
position: str = "top-left",
api_config: Optional[Dict[str, Any]] = None,
options: Optional[Dict[str, Any]] = None,
collapsed: bool = True,
) -> None:
"""Add a geocoder control to the map for searching locations.
The geocoder control allows users to search for locations using a geocoding service.
By default, it uses the Nominatim (OpenStreetMap) geocoding API.
Args:
position: Position on map ('top-left', 'top-right', 'bottom-left', 'bottom-right')
api_config: Configuration for the geocoding API. If None, uses default Nominatim config
options: Additional options for the geocoder control
collapsed: If True, shows only search icon initially. Click to expand input box.
"""
if api_config is None:
# Default configuration using Nominatim API
api_config = {
"forwardGeocode": True,
"reverseGeocode": False,
"placeholder": "Search for places...",
"limit": 5,
"api_url": "https://nominatim.openstreetmap.org/search",
}
control_options = options or {}
control_options.update(
{
"position": position,
"api_config": api_config,
"collapsed": collapsed,
}
)
# Store control in persistent state
control_key = f"geocoder_{position}"
current_controls = dict(self._controls)
current_controls[control_key] = {
"type": "geocoder",
"position": position,
"options": control_options,
}
self._controls = current_controls
self.call_js_method("addControl", "geocoder", control_options)
def add_google_streetview(
self,
position: str = "top-left",
api_key: Optional[str] = None,
options: Optional[Dict[str, Any]] = None,
) -> None:
"""Add a Google Street View control to the map.
This method adds a Google Street View control that allows users to view
street-level imagery at clicked locations on the map.
Args:
position: Position on map ('top-left', 'top-right', 'bottom-left', 'bottom-right')
api_key: Google Maps API key. If None, retrieves from GOOGLE_MAPS_API_KEY environment variable
options: Additional options for the Street View control
Raises:
ValueError: If no API key is provided and none can be found in environment variables
"""
if api_key is None:
api_key = get_env_var("GOOGLE_MAPS_API_KEY")
if api_key is None:
raise ValueError(
"Google Maps API key is required. Please provide it as a parameter "
"or set the GOOGLE_MAPS_API_KEY environment variable."
)
control_options = options or {}
control_options.update(
{
"position": position,
"api_key": api_key,
}
)
# Store control in persistent state
control_key = f"google_streetview_{position}"
current_controls = dict(self._controls)
current_controls[control_key] = {
"type": "google_streetview",
"position": position,
"options": control_options,
}
self._controls = current_controls
self.call_js_method("addControl", "google_streetview", control_options)
def _update_layer_controls(self) -> None:
"""Update all existing layer controls with the current layer state."""
# Find all layer controls in the _controls dictionary
for control_key, control_config in self._controls.items():
if control_config.get("type") == "layer_control":
# Update the layerStates in the control options
control_options = control_config.get("options", {})
layers_filter = control_options.get("layers")
# Get current layer states for this control
layer_states = {}
target_layers = (
layers_filter
if layers_filter is not None
else list(self.layer_dict.keys())
)
# Always include Background layer for controlling map style layers
if layers_filter is None or "Background" in layers_filter:
layer_states["Background"] = {
"visible": True,
"opacity": 1.0,
"name": "Background",
}
for layer_id in target_layers:
if layer_id in self.layer_dict and layer_id != "Background":
layer_info = self.layer_dict[layer_id]
layer_states[layer_id] = {
"visible": layer_info.get("visible", True),
"opacity": layer_info.get("opacity", 1.0),
"name": layer_id,
}
# Update the control options with new layer states
control_options["layerStates"] = layer_states
# Update the control configuration
control_config["options"] = control_options
# Trigger the JavaScript layer control to check for new layers
# by updating the _layer_dict trait that the JS listens to
self._layer_dict = dict(self.layer_dict)
def remove_layer(self, layer_id: str) -> None:
"""Remove a layer from the map.
Args:
layer_id: Unique identifier for the layer to remove.
"""
# Remove from JavaScript map
self.call_js_method("removeLayer", layer_id)
# Remove from local state
if layer_id in self._layers:
current_layers = dict(self._layers)
del current_layers[layer_id]
self._layers = current_layers
# Remove from layer_dict
if layer_id in self.layer_dict:
del self.layer_dict[layer_id]
# Update layer controls if they exist
self._update_layer_controls()
def add_cog_layer(
self,
layer_id: str,
cog_url: str,
opacity: Optional[float] = 1.0,
visible: Optional[bool] = True,
paint: Optional[Dict[str, Any]] = None,
before_id: Optional[str] = None,
) -> None:
"""Add a Cloud Optimized GeoTIFF (COG) layer to the map.
Args:
layer_id: Unique identifier for the COG layer.
cog_url: URL to the COG file.
opacity: Layer opacity between 0.0 and 1.0.
visible: Whether the layer should be visible initially.
paint: Optional paint properties for the layer.
before_id: Optional layer ID to insert this layer before.
"""
source_id = f"{layer_id}_source"
# Add COG source using cog:// protocol
cog_source_url = f"cog://{cog_url}"
self.add_source(
source_id,
{
"type": "raster",
"url": cog_source_url,
"tileSize": 256,
},
)
# Add raster layer
layer_config = {"id": layer_id, "type": "raster", "source": source_id}
if paint:
layer_config["paint"] = paint
self.add_layer(
layer_id, layer_config, before_id, opacity=opacity, visible=visible
)
def add_pmtiles(
self,
pmtiles_url: str,
layer_id: Optional[str] = None,
layers: Optional[List[Dict[str, Any]]] = None,
opacity: Optional[float] = 1.0,
visible: Optional[bool] = True,
before_id: Optional[str] = None,
) -> None:
"""Add PMTiles vector tiles to the map.
Args:
pmtiles_url: URL to the PMTiles file.
layer_id: Optional unique identifier for the layer. If None, uses filename.
layers: Optional list of layer configurations for rendering. If None, creates default layers.
opacity: Layer opacity between 0.0 and 1.0.
visible: Whether the layer should be visible initially.
before_id: Optional layer ID to insert this layer before.
"""
if layer_id is None:
layer_id = pmtiles_url.split("/")[-1].replace(".pmtiles", "")
source_id = f"{layer_id}_source"
# Add PMTiles source using pmtiles:// protocol
pmtiles_source_url = f"pmtiles://{pmtiles_url}"
self.add_source(
source_id,
{
"type": "vector",
"url": pmtiles_source_url,
"attribution": "PMTiles",
},
)
# Add default layers if none provided
if layers is None:
layers = [
{
"id": f"{layer_id}_landuse",
"source": source_id,
"source-layer": "landuse",
"type": "fill",
"paint": {"fill-color": "steelblue", "fill-opacity": 0.5},
},
{
"id": f"{layer_id}_roads",
"source": source_id,
"source-layer": "roads",
"type": "line",
"paint": {"line-color": "black", "line-width": 1},
},
{
"id": f"{layer_id}_buildings",
"source": source_id,
"source-layer": "buildings",
"type": "fill",
"paint": {"fill-color": "gray", "fill-opacity": 0.7},
},
{
"id": f"{layer_id}_water",
"source": source_id,
"source-layer": "water",
"type": "fill",
"paint": {"fill-color": "lightblue", "fill-opacity": 0.8},
},
]
# Add all layers
for layer_config in layers:
self.add_layer(
layer_config["id"],
layer_config,
before_id,
opacity=opacity,
visible=visible,
)
def add_basemap(
self,
basemap: str,
layer_id: Optional[str] = None,
before_id: Optional[str] = None,
) -> None:
"""Add a basemap to the map using xyzservices providers.
Args:
basemap: Name of the basemap from xyzservices (e.g., "Esri.WorldImagery").
Use available_basemaps to see all available options.
layer_id: Optional ID for the basemap layer. If None, uses basemap name.
before_id: Optional layer ID to insert this layer before.
If None, layer is added on top.
Raises:
ValueError: If the specified basemap is not available.
"""
from .basemaps import available_basemaps
if basemap not in available_basemaps:
available_names = list(available_basemaps.keys())
raise ValueError(
f"Basemap '{basemap}' not found. Available basemaps: {available_names}"
)
basemap_config = available_basemaps[basemap]
# Convert xyzservices URL template to tile URL
tile_url = basemap_config.build_url()
# Get attribution if available
attribution = basemap_config.get("attribution", "")
if layer_id is None:
layer_id = basemap
# Add as raster layer
self.add_tile_layer(
layer_id=layer_id,
source_url=tile_url,
paint={"raster-opacity": 1.0},
before_id=before_id,
)
def add_draw_control(
self,
position: str = "top-left",
controls: Optional[Dict[str, bool]] = None,
default_mode: str = "simple_select",
keybindings: bool = True,
touch_enabled: bool = True,
**kwargs: Any,
) -> None:
"""Add a draw control to the map for drawing and editing geometries.
Args:
position: Position on map ('top-left', 'top-right', 'bottom-left', 'bottom-right')
controls: Dictionary specifying which drawing tools to show.
Defaults to {'point': True, 'line_string': True, 'polygon': True, 'trash': True}
default_mode: Initial interaction mode ('simple_select', 'direct_select', 'draw_point', etc.)
keybindings: Whether to enable keyboard shortcuts
touch_enabled: Whether to enable touch interactions
**kwargs: Additional options to pass to MapboxDraw constructor
"""
if controls is None:
controls = {
"point": True,
"line_string": True,
"polygon": True,
"trash": True,
}
draw_options = {
"displayControlsDefault": False,
"controls": controls,
"defaultMode": default_mode,
"keybindings": keybindings,
"touchEnabled": touch_enabled,
"position": position,
**kwargs,
}
# Store draw control configuration
current_controls = dict(self._controls)
draw_key = f"draw_{position}"
current_controls[draw_key] = {
"type": "draw",
"position": position,
"options": draw_options,
}
self._controls = current_controls
self.call_js_method("addDrawControl", draw_options)
def load_draw_data(self, geojson_data: Union[Dict[str, Any], str]) -> None:
"""Load GeoJSON data into the draw control.
Args:
geojson_data: GeoJSON data as dictionary or JSON string
"""
if isinstance(geojson_data, str):
geojson_data = json.loads(geojson_data)
# Update the trait immediately to ensure consistency
self._draw_data = geojson_data
# Send to JavaScript
self.call_js_method("loadDrawData", geojson_data)
def get_draw_data(self) -> Dict[str, Any]:
"""Get all drawn features as GeoJSON.
Returns:
Dict containing GeoJSON FeatureCollection with drawn features
"""
# Try to get current data first
if self._draw_data:
return self._draw_data
# If no data in trait, call JavaScript to get fresh data
self.call_js_method("getDrawData")
# Give JavaScript time to execute and sync data
import time
time.sleep(0.2)
# Return the synced data or empty FeatureCollection if nothing
return (
self._draw_data
if self._draw_data
else {"type": "FeatureCollection", "features": []}
)
def clear_draw_data(self) -> None:
"""Clear all drawn features from the draw control."""
# Clear the trait data immediately
self._draw_data = {"type": "FeatureCollection", "features": []}
# Clear in JavaScript
self.call_js_method("clearDrawData")
def delete_draw_features(self, feature_ids: List[str]) -> None:
"""Delete specific features from the draw control.
Args:
feature_ids: List of feature IDs to delete
"""
self.call_js_method("deleteDrawFeatures", feature_ids)
def set_draw_mode(self, mode: str) -> None:
"""Set the draw control mode.
Args:
mode: Draw mode ('simple_select', 'direct_select', 'draw_point',
'draw_line_string', 'draw_polygon', 'static')
"""
self.call_js_method("setDrawMode", mode)
def add_terra_draw(
self,
position: str = "top-left",
modes: Optional[List[str]] = None,
open: bool = True,
**kwargs: Any,
) -> None:
"""Add a Terra Draw control to the map for drawing and editing geometries.
Args:
position: Position on map ('top-left', 'top-right', 'bottom-left', 'bottom-right')
modes: List of drawing modes to enable. Available modes:
['render', 'point', 'linestring', 'polygon', 'rectangle', 'circle',
'freehand', 'angled-rectangle', 'sensor', 'sector', 'select',
'delete-selection', 'delete', 'download']
Defaults to all modes except 'render'
open: Whether the draw control panel should be open by default
**kwargs: Additional options to pass to Terra Draw constructor
"""
if modes is None:
modes = [
# 'render', # Commented out to always show drawing tool
"point",
"linestring",
"polygon",
"rectangle",
"circle",
"freehand",
"angled-rectangle",
"sensor",
"sector",
"select",
"delete-selection",
"delete",
"download",
]
terra_draw_options = {
"modes": modes,
"open": open,
"position": position,
**kwargs,
}
# Mark that Terra Draw is enabled
self._terra_draw_enabled = True
# Store Terra Draw control configuration
current_controls = dict(self._controls)
terra_draw_key = f"terra_draw_{position}"
current_controls[terra_draw_key] = {
"type": "terra_draw",
"position": position,
"options": terra_draw_options,
}
self._controls = current_controls
self.call_js_method("addTerraDrawControl", terra_draw_options)
def get_terra_draw_data(self) -> Dict[str, Any]:
"""Get all Terra Draw features as GeoJSON.
Returns:
Dict containing GeoJSON FeatureCollection with drawn features
"""
# Try to get current data first
if self._terra_draw_data:
return self._terra_draw_data
# If no data in trait, call JavaScript to get fresh data
self.call_js_method("getTerraDrawData")
# Give JavaScript time to execute and sync data
import time
time.sleep(0.2)
# Return the synced data or empty FeatureCollection if nothing
return (
self._terra_draw_data
if self._terra_draw_data
else {"type": "FeatureCollection", "features": []}
)
def clear_terra_draw_data(self) -> None:
"""Clear all Terra Draw features from the draw control."""
# Clear the trait data immediately
self._terra_draw_data = {"type": "FeatureCollection", "features": []}
# Clear in JavaScript
self.call_js_method("clearTerraDrawData")
def load_terra_draw_data(self, geojson_data: Union[Dict[str, Any], str]) -> None:
"""Load GeoJSON data into the Terra Draw control.
Args:
geojson_data: GeoJSON data as dictionary or JSON string
"""
if isinstance(geojson_data, str):
geojson_data = json.loads(geojson_data)
# Update the trait immediately to ensure consistency
self._terra_draw_data = geojson_data
# Send to JavaScript
self.call_js_method("loadTerraDrawData", geojson_data)
def _generate_html_template(
self, map_state: Dict[str, Any], title: str, **kwargs: Any
) -> str:
"""Generate HTML template for MapLibre GL JS.
Args:
map_state: Dictionary containing the current map state including
center, zoom, style, layers, and sources.
title: Title for the HTML page.
**kwargs: Additional arguments for template customization.
Returns:
Complete HTML string for a standalone MapLibre GL JS map.
"""
import os
# Get the directory of the current file
current_dir = os.path.dirname(os.path.abspath(__file__))
template_path = os.path.join(current_dir, "templates", "maplibre_template.html")
# Read the template file
with open(template_path, "r", encoding="utf-8") as f:
template_content = f.read()
# Serialize map state for JavaScript
map_state_json = json.dumps(map_state, indent=2)
# Replace placeholders with actual values
html_template = template_content.format(
title=title,
width=map_state["width"],
height=map_state["height"],
map_state_json=map_state_json,
)
return html_template
def _update_current_state(self, event: Dict[str, Any]) -> None:
"""Update current state attributes from moveend event."""
if "center" in event:
self._current_center = event["center"]
if "zoom" in event:
self._current_zoom = event["zoom"]
if "bearing" in event:
self._current_bearing = event["bearing"]
if "pitch" in event:
self._current_pitch = event["pitch"]
if "bounds" in event:
self._current_bounds = event["bounds"]
def set_center(self, lng: float, lat: float) -> None:
"""Set the map center coordinates.
Args:
lng: Longitude coordinate.
lat: Latitude coordinate.
"""
self.center = [lng, lat]
self._current_center = [lng, lat]
def set_zoom(self, zoom: float) -> None:
"""Set the map zoom level.
Args:
zoom: Zoom level (typically 0-20).
"""
self.zoom = zoom
self._current_zoom = zoom
@property
def current_center(self) -> List[float]:
"""Get the current map center coordinates as [longitude, latitude]."""
return self._current_center
@property
def current_zoom(self) -> float:
"""Get the current map zoom level."""
return self._current_zoom
@property
def current_bounds(self) -> Optional[List[List[float]]]:
"""Get the current map bounds as [[lng, lat], [lng, lat]] (southwest, northeast)."""
return self._current_bounds
@property
def viewstate(self) -> Dict[str, Any]:
"""Get the current map viewstate including center, zoom, bearing, pitch, and bounds."""
return {
"center": self._current_center,
"zoom": self._current_zoom,
"bearing": self._current_bearing,
"pitch": self._current_pitch,
"bounds": self._current_bounds,
}
def add_basemap_control(
self,
position: str = "top-right",
basemaps: Optional[List[str]] = None,
labels: Optional[Dict[str, str]] = None,
initial_basemap: Optional[str] = None,
expand_direction: str = "down",
options: Optional[Dict[str, Any]] = None,
) -> None:
"""Add a basemap control to the map for switching between different basemaps.
The basemap control allows users to switch between different basemap providers
using a dropdown or expandable control. It uses the maplibre-gl-basemaps library.
Args:
position: Position on map ('top-left', 'top-right', 'bottom-left', 'bottom-right')
basemaps: List of basemap names to include. If None, uses a default set.
Available basemaps can be found in anymap.basemaps.available_basemaps
labels: Dictionary mapping basemap names to display labels. If None, uses basemap names.
initial_basemap: Name of the initial basemap to show. If None, uses the first basemap.
expand_direction: Direction to expand the control ('up', 'down', 'left', 'right')
options: Additional options for the basemap control
Example:
>>> m = MapLibreMap()
>>> m.add_basemap_control(
... position="top-right",
... basemaps=["OpenStreetMap.Mapnik", "Esri.WorldImagery", "CartoDB.DarkMatter"],
... labels={"OpenStreetMap.Mapnik": "OpenStreetMap", "Esri.WorldImagery": "Satellite"},
... initial_basemap="OpenStreetMap.Mapnik"
... )
"""
from .basemaps import available_basemaps
# Default basemaps if none provided
if basemaps is None:
basemaps = [
"OpenStreetMap.Mapnik",
"Esri.WorldImagery",
"CartoDB.DarkMatter",
"CartoDB.Positron",
]
# Filter available basemaps to only include those that exist
valid_basemaps = [name for name in basemaps if name in available_basemaps]
if not valid_basemaps:
raise ValueError(
f"No valid basemaps found. Available basemaps: {list(available_basemaps.keys())}"
)
# Set initial basemap if not provided
if initial_basemap is None:
initial_basemap = valid_basemaps[0]
elif initial_basemap not in valid_basemaps:
raise ValueError(
f"Initial basemap '{initial_basemap}' not found in provided basemaps"
)
# Create basemap configurations for the control
basemap_configs = []
for basemap_name in valid_basemaps:
basemap_provider = available_basemaps[basemap_name]
tile_url = basemap_provider.build_url()
attribution = basemap_provider.get("attribution", "")
# Use custom label if provided, otherwise use basemap name
display_label = (
labels.get(basemap_name, basemap_name) if labels else basemap_name
)
basemap_config = {
"id": basemap_name,
"tiles": [tile_url],
"sourceExtraParams": {
"tileSize": 256,
"attribution": attribution,
"minzoom": basemap_provider.get("min_zoom", 0),
"maxzoom": basemap_provider.get("max_zoom", 22),
},
"label": display_label,
}
basemap_configs.append(basemap_config)
control_options = options or {}
control_options.update(
{
"position": position,
"basemaps": basemap_configs,
"initialBasemap": initial_basemap,
"expandDirection": expand_direction,
}
)
# Store control in persistent state
control_key = f"basemap_control_{position}"
current_controls = dict(self._controls)
current_controls[control_key] = {
"type": "basemap_control",
"position": position,
"options": control_options,
}
self._controls = current_controls
self.call_js_method("addControl", "basemap_control", control_options)
current_bounds: Optional[List[List[float]]]
property
readonly
¶
Get the current map bounds as [[lng, lat], [lng, lat]] (southwest, northeast).
current_center: List[float]
property
readonly
¶
Get the current map center coordinates as [longitude, latitude].
current_zoom: float
property
readonly
¶
Get the current map zoom level.
sidebar_widgets: Dict[str, ipywidgets.widgets.widget.Widget]
property
readonly
¶
Returns a dictionary of widgets currently in the sidebar.
Returns:
Type | Description |
---|---|
Dict[str, widgets.Widget] |
A dictionary where keys are the labels of the widgets and values are the widgets themselves. |
viewstate: Dict[str, Any]
property
readonly
¶
Get the current map viewstate including center, zoom, bearing, pitch, and bounds.
__init__(self, center=[0, 20], zoom=1.0, style='dark-matter', width='100%', height='600px', bearing=0.0, pitch=0.0, controls={'navigation': 'top-right', 'fullscreen': 'top-right', 'scale': 'bottom-left', 'globe': 'top-right', 'layers': 'top-right'}, projection='mercator', add_sidebar=False, sidebar_visible=False, sidebar_width=360, sidebar_args=None, layer_manager_expanded=True, **kwargs)
special
¶
Initialize MapLibre map widget.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
center |
List[float] |
Map center coordinates as [longitude, latitude]. Default is [0, 20]. |
[0, 20] |
zoom |
float |
Initial zoom level (typically 0-20). Default is 1.0. |
1.0 |
style |
Union[str, Dict[str, Any]] |
MapLibre style URL string or style object dictionary. |
'dark-matter' |
width |
str |
Widget width as CSS string (e.g., "100%", "800px"). |
'100%' |
height |
str |
Widget height as CSS string (e.g., "600px", "50vh"). |
'600px' |
bearing |
float |
Map bearing (rotation) in degrees (0-360). |
0.0 |
pitch |
float |
Map pitch (tilt) in degrees (0-60). |
0.0 |
controls |
Dict[str, str] |
Dictionary of control names and their positions. Default is { "navigation": "top-right", "fullscreen": "top-right", "scale": "bottom-left", "globe": "top-right", "layers": "top-right", |
{'navigation': 'top-right', 'fullscreen': 'top-right', 'scale': 'bottom-left', 'globe': 'top-right', 'layers': 'top-right'} |
projection |
str |
Map projection type. Can be "mercator" or "globe". Default is "mercator". |
'mercator' |
add_sidebar |
bool |
Whether to add a sidebar to the map. Default is False. |
False |
sidebar_visible |
bool |
Whether the sidebar is visible. Default is False. |
False |
sidebar_width |
int |
Width of the sidebar in pixels. Default is 360. |
360 |
sidebar_args |
Optional[Dict] |
Additional keyword arguments for the sidebar. Default is None. |
None |
layer_manager_expanded |
bool |
Whether the layer manager is expanded. Default is True. |
True |
**kwargs |
Any |
Additional keyword arguments passed to parent class. |
{} |
Source code in anymap/maplibre.py
def __init__(
self,
center: List[float] = [0, 20],
zoom: float = 1.0,
style: Union[str, Dict[str, Any]] = "dark-matter",
width: str = "100%",
height: str = "600px",
bearing: float = 0.0,
pitch: float = 0.0,
controls: Dict[str, str] = {
"navigation": "top-right",
"fullscreen": "top-right",
"scale": "bottom-left",
"globe": "top-right",
"layers": "top-right",
},
projection: str = "mercator",
add_sidebar: bool = False,
sidebar_visible: bool = False,
sidebar_width: int = 360,
sidebar_args: Optional[Dict] = None,
layer_manager_expanded: bool = True,
**kwargs: Any,
) -> None:
"""Initialize MapLibre map widget.
Args:
center: Map center coordinates as [longitude, latitude]. Default is [0, 20].
zoom: Initial zoom level (typically 0-20). Default is 1.0.
style: MapLibre style URL string or style object dictionary.
width: Widget width as CSS string (e.g., "100%", "800px").
height: Widget height as CSS string (e.g., "600px", "50vh").
bearing: Map bearing (rotation) in degrees (0-360).
pitch: Map pitch (tilt) in degrees (0-60).
controls: Dictionary of control names and their positions. Default is {
"navigation": "top-right",
"fullscreen": "top-right",
"scale": "bottom-left",
"globe": "top-right",
"layers": "top-right",
}.
projection: Map projection type. Can be "mercator" or "globe". Default is "mercator".
add_sidebar: Whether to add a sidebar to the map. Default is False.
sidebar_visible: Whether the sidebar is visible. Default is False.
sidebar_width: Width of the sidebar in pixels. Default is 360.
sidebar_args: Additional keyword arguments for the sidebar. Default is None.
layer_manager_expanded: Whether the layer manager is expanded. Default is True.
**kwargs: Additional keyword arguments passed to parent class.
"""
if isinstance(style, str):
style = construct_maplibre_style(style)
super().__init__(
center=center,
zoom=zoom,
width=width,
height=height,
style=style,
bearing=bearing,
pitch=pitch,
**kwargs,
)
self.layer_dict = {}
self.layer_dict["Background"] = {
"layer": {
"id": "Background",
"type": "background",
},
"opacity": 1.0,
"visible": True,
"type": "background",
"color": None,
}
# Initialize the _layer_dict trait with the layer_dict content
self._layer_dict = dict(self.layer_dict)
# Initialize current state attributes
self._current_center = center
self._current_zoom = zoom
self._current_bearing = bearing
self._current_pitch = pitch
self._current_bounds = None # Will be set after map loads
# Register event handler to update current state
self.on_map_event("moveend", self._update_current_state)
self._style = style
self.style_dict = {}
for layer in self.get_style_layers():
self.style_dict[layer["id"]] = layer
self.source_dict = {}
if projection.lower() == "globe":
self.set_projection(
{
"type": [
"interpolate",
["linear"],
["zoom"],
10,
"vertical-perspective",
12,
"mercator",
]
}
)
self.controls = {}
for control, position in controls.items():
if control == "layers":
self.add_layer_control(position)
else:
self.add_control(control, position)
self.controls[control] = position
if sidebar_args is None:
sidebar_args = {}
if "sidebar_visible" not in sidebar_args:
sidebar_args["sidebar_visible"] = sidebar_visible
if "sidebar_width" not in sidebar_args:
if isinstance(sidebar_width, str):
sidebar_width = int(sidebar_width.replace("px", ""))
sidebar_args["min_width"] = sidebar_width
sidebar_args["max_width"] = sidebar_width
if "expanded" not in sidebar_args:
sidebar_args["expanded"] = layer_manager_expanded
self.sidebar_args = sidebar_args
self.layer_manager = None
self.container = None
if add_sidebar:
self._ipython_display_ = self._patched_display
add_basemap(self, basemap, layer_id=None, before_id=None)
¶
Add a basemap to the map using xyzservices providers.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
basemap |
str |
Name of the basemap from xyzservices (e.g., "Esri.WorldImagery"). Use available_basemaps to see all available options. |
required |
layer_id |
Optional[str] |
Optional ID for the basemap layer. If None, uses basemap name. |
None |
before_id |
Optional[str] |
Optional layer ID to insert this layer before. If None, layer is added on top. |
None |
Exceptions:
Type | Description |
---|---|
ValueError |
If the specified basemap is not available. |
Source code in anymap/maplibre.py
def add_basemap(
self,
basemap: str,
layer_id: Optional[str] = None,
before_id: Optional[str] = None,
) -> None:
"""Add a basemap to the map using xyzservices providers.
Args:
basemap: Name of the basemap from xyzservices (e.g., "Esri.WorldImagery").
Use available_basemaps to see all available options.
layer_id: Optional ID for the basemap layer. If None, uses basemap name.
before_id: Optional layer ID to insert this layer before.
If None, layer is added on top.
Raises:
ValueError: If the specified basemap is not available.
"""
from .basemaps import available_basemaps
if basemap not in available_basemaps:
available_names = list(available_basemaps.keys())
raise ValueError(
f"Basemap '{basemap}' not found. Available basemaps: {available_names}"
)
basemap_config = available_basemaps[basemap]
# Convert xyzservices URL template to tile URL
tile_url = basemap_config.build_url()
# Get attribution if available
attribution = basemap_config.get("attribution", "")
if layer_id is None:
layer_id = basemap
# Add as raster layer
self.add_tile_layer(
layer_id=layer_id,
source_url=tile_url,
paint={"raster-opacity": 1.0},
before_id=before_id,
)
add_basemap_control(self, position='top-right', basemaps=None, labels=None, initial_basemap=None, expand_direction='down', options=None)
¶
Add a basemap control to the map for switching between different basemaps.
The basemap control allows users to switch between different basemap providers using a dropdown or expandable control. It uses the maplibre-gl-basemaps library.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
position |
str |
Position on map ('top-left', 'top-right', 'bottom-left', 'bottom-right') |
'top-right' |
basemaps |
Optional[List[str]] |
List of basemap names to include. If None, uses a default set. Available basemaps can be found in anymap.basemaps.available_basemaps |
None |
labels |
Optional[Dict[str, str]] |
Dictionary mapping basemap names to display labels. If None, uses basemap names. |
None |
initial_basemap |
Optional[str] |
Name of the initial basemap to show. If None, uses the first basemap. |
None |
expand_direction |
str |
Direction to expand the control ('up', 'down', 'left', 'right') |
'down' |
options |
Optional[Dict[str, Any]] |
Additional options for the basemap control |
None |
Examples:
>>> m = MapLibreMap()
>>> m.add_basemap_control(
... position="top-right",
... basemaps=["OpenStreetMap.Mapnik", "Esri.WorldImagery", "CartoDB.DarkMatter"],
... labels={"OpenStreetMap.Mapnik": "OpenStreetMap", "Esri.WorldImagery": "Satellite"},
... initial_basemap="OpenStreetMap.Mapnik"
... )
Source code in anymap/maplibre.py
def add_basemap_control(
self,
position: str = "top-right",
basemaps: Optional[List[str]] = None,
labels: Optional[Dict[str, str]] = None,
initial_basemap: Optional[str] = None,
expand_direction: str = "down",
options: Optional[Dict[str, Any]] = None,
) -> None:
"""Add a basemap control to the map for switching between different basemaps.
The basemap control allows users to switch between different basemap providers
using a dropdown or expandable control. It uses the maplibre-gl-basemaps library.
Args:
position: Position on map ('top-left', 'top-right', 'bottom-left', 'bottom-right')
basemaps: List of basemap names to include. If None, uses a default set.
Available basemaps can be found in anymap.basemaps.available_basemaps
labels: Dictionary mapping basemap names to display labels. If None, uses basemap names.
initial_basemap: Name of the initial basemap to show. If None, uses the first basemap.
expand_direction: Direction to expand the control ('up', 'down', 'left', 'right')
options: Additional options for the basemap control
Example:
>>> m = MapLibreMap()
>>> m.add_basemap_control(
... position="top-right",
... basemaps=["OpenStreetMap.Mapnik", "Esri.WorldImagery", "CartoDB.DarkMatter"],
... labels={"OpenStreetMap.Mapnik": "OpenStreetMap", "Esri.WorldImagery": "Satellite"},
... initial_basemap="OpenStreetMap.Mapnik"
... )
"""
from .basemaps import available_basemaps
# Default basemaps if none provided
if basemaps is None:
basemaps = [
"OpenStreetMap.Mapnik",
"Esri.WorldImagery",
"CartoDB.DarkMatter",
"CartoDB.Positron",
]
# Filter available basemaps to only include those that exist
valid_basemaps = [name for name in basemaps if name in available_basemaps]
if not valid_basemaps:
raise ValueError(
f"No valid basemaps found. Available basemaps: {list(available_basemaps.keys())}"
)
# Set initial basemap if not provided
if initial_basemap is None:
initial_basemap = valid_basemaps[0]
elif initial_basemap not in valid_basemaps:
raise ValueError(
f"Initial basemap '{initial_basemap}' not found in provided basemaps"
)
# Create basemap configurations for the control
basemap_configs = []
for basemap_name in valid_basemaps:
basemap_provider = available_basemaps[basemap_name]
tile_url = basemap_provider.build_url()
attribution = basemap_provider.get("attribution", "")
# Use custom label if provided, otherwise use basemap name
display_label = (
labels.get(basemap_name, basemap_name) if labels else basemap_name
)
basemap_config = {
"id": basemap_name,
"tiles": [tile_url],
"sourceExtraParams": {
"tileSize": 256,
"attribution": attribution,
"minzoom": basemap_provider.get("min_zoom", 0),
"maxzoom": basemap_provider.get("max_zoom", 22),
},
"label": display_label,
}
basemap_configs.append(basemap_config)
control_options = options or {}
control_options.update(
{
"position": position,
"basemaps": basemap_configs,
"initialBasemap": initial_basemap,
"expandDirection": expand_direction,
}
)
# Store control in persistent state
control_key = f"basemap_control_{position}"
current_controls = dict(self._controls)
current_controls[control_key] = {
"type": "basemap_control",
"position": position,
"options": control_options,
}
self._controls = current_controls
self.call_js_method("addControl", "basemap_control", control_options)
add_cog_layer(self, layer_id, cog_url, opacity=1.0, visible=True, paint=None, before_id=None)
¶
Add a Cloud Optimized GeoTIFF (COG) layer to the map.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
layer_id |
str |
Unique identifier for the COG layer. |
required |
cog_url |
str |
URL to the COG file. |
required |
opacity |
Optional[float] |
Layer opacity between 0.0 and 1.0. |
1.0 |
visible |
Optional[bool] |
Whether the layer should be visible initially. |
True |
paint |
Optional[Dict[str, Any]] |
Optional paint properties for the layer. |
None |
before_id |
Optional[str] |
Optional layer ID to insert this layer before. |
None |
Source code in anymap/maplibre.py
def add_cog_layer(
self,
layer_id: str,
cog_url: str,
opacity: Optional[float] = 1.0,
visible: Optional[bool] = True,
paint: Optional[Dict[str, Any]] = None,
before_id: Optional[str] = None,
) -> None:
"""Add a Cloud Optimized GeoTIFF (COG) layer to the map.
Args:
layer_id: Unique identifier for the COG layer.
cog_url: URL to the COG file.
opacity: Layer opacity between 0.0 and 1.0.
visible: Whether the layer should be visible initially.
paint: Optional paint properties for the layer.
before_id: Optional layer ID to insert this layer before.
"""
source_id = f"{layer_id}_source"
# Add COG source using cog:// protocol
cog_source_url = f"cog://{cog_url}"
self.add_source(
source_id,
{
"type": "raster",
"url": cog_source_url,
"tileSize": 256,
},
)
# Add raster layer
layer_config = {"id": layer_id, "type": "raster", "source": source_id}
if paint:
layer_config["paint"] = paint
self.add_layer(
layer_id, layer_config, before_id, opacity=opacity, visible=visible
)
add_control(self, control_type, position='top-right', options=None)
¶
Add a control to the map.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
control_type |
str |
Type of control ('navigation', 'scale', 'fullscreen', 'geolocate', 'attribution', 'globe') |
required |
position |
str |
Position on map ('top-left', 'top-right', 'bottom-left', 'bottom-right') |
'top-right' |
options |
Optional[Dict[str, Any]] |
Additional options for the control |
None |
Source code in anymap/maplibre.py
def add_control(
self,
control_type: str,
position: str = "top-right",
options: Optional[Dict[str, Any]] = None,
) -> None:
"""Add a control to the map.
Args:
control_type: Type of control ('navigation', 'scale', 'fullscreen', 'geolocate', 'attribution', 'globe')
position: Position on map ('top-left', 'top-right', 'bottom-left', 'bottom-right')
options: Additional options for the control
"""
control_options = options or {}
control_options["position"] = position
# Store control in persistent state
control_key = f"{control_type}_{position}"
current_controls = dict(self._controls)
current_controls[control_key] = {
"type": control_type,
"position": position,
"options": control_options,
}
self._controls = current_controls
self.call_js_method("addControl", control_type, control_options)
add_draw_control(self, position='top-left', controls=None, default_mode='simple_select', keybindings=True, touch_enabled=True, **kwargs)
¶
Add a draw control to the map for drawing and editing geometries.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
position |
str |
Position on map ('top-left', 'top-right', 'bottom-left', 'bottom-right') |
'top-left' |
controls |
Optional[Dict[str, bool]] |
Dictionary specifying which drawing tools to show. Defaults to {'point': True, 'line_string': True, 'polygon': True, 'trash': True} |
None |
default_mode |
str |
Initial interaction mode ('simple_select', 'direct_select', 'draw_point', etc.) |
'simple_select' |
keybindings |
bool |
Whether to enable keyboard shortcuts |
True |
touch_enabled |
bool |
Whether to enable touch interactions |
True |
**kwargs |
Any |
Additional options to pass to MapboxDraw constructor |
{} |
Source code in anymap/maplibre.py
def add_draw_control(
self,
position: str = "top-left",
controls: Optional[Dict[str, bool]] = None,
default_mode: str = "simple_select",
keybindings: bool = True,
touch_enabled: bool = True,
**kwargs: Any,
) -> None:
"""Add a draw control to the map for drawing and editing geometries.
Args:
position: Position on map ('top-left', 'top-right', 'bottom-left', 'bottom-right')
controls: Dictionary specifying which drawing tools to show.
Defaults to {'point': True, 'line_string': True, 'polygon': True, 'trash': True}
default_mode: Initial interaction mode ('simple_select', 'direct_select', 'draw_point', etc.)
keybindings: Whether to enable keyboard shortcuts
touch_enabled: Whether to enable touch interactions
**kwargs: Additional options to pass to MapboxDraw constructor
"""
if controls is None:
controls = {
"point": True,
"line_string": True,
"polygon": True,
"trash": True,
}
draw_options = {
"displayControlsDefault": False,
"controls": controls,
"defaultMode": default_mode,
"keybindings": keybindings,
"touchEnabled": touch_enabled,
"position": position,
**kwargs,
}
# Store draw control configuration
current_controls = dict(self._controls)
draw_key = f"draw_{position}"
current_controls[draw_key] = {
"type": "draw",
"position": position,
"options": draw_options,
}
self._controls = current_controls
self.call_js_method("addDrawControl", draw_options)
add_geocoder_control(self, position='top-left', api_config=None, options=None, collapsed=True)
¶
Add a geocoder control to the map for searching locations.
The geocoder control allows users to search for locations using a geocoding service. By default, it uses the Nominatim (OpenStreetMap) geocoding API.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
position |
str |
Position on map ('top-left', 'top-right', 'bottom-left', 'bottom-right') |
'top-left' |
api_config |
Optional[Dict[str, Any]] |
Configuration for the geocoding API. If None, uses default Nominatim config |
None |
options |
Optional[Dict[str, Any]] |
Additional options for the geocoder control |
None |
collapsed |
bool |
If True, shows only search icon initially. Click to expand input box. |
True |
Source code in anymap/maplibre.py
def add_geocoder_control(
self,
position: str = "top-left",
api_config: Optional[Dict[str, Any]] = None,
options: Optional[Dict[str, Any]] = None,
collapsed: bool = True,
) -> None:
"""Add a geocoder control to the map for searching locations.
The geocoder control allows users to search for locations using a geocoding service.
By default, it uses the Nominatim (OpenStreetMap) geocoding API.
Args:
position: Position on map ('top-left', 'top-right', 'bottom-left', 'bottom-right')
api_config: Configuration for the geocoding API. If None, uses default Nominatim config
options: Additional options for the geocoder control
collapsed: If True, shows only search icon initially. Click to expand input box.
"""
if api_config is None:
# Default configuration using Nominatim API
api_config = {
"forwardGeocode": True,
"reverseGeocode": False,
"placeholder": "Search for places...",
"limit": 5,
"api_url": "https://nominatim.openstreetmap.org/search",
}
control_options = options or {}
control_options.update(
{
"position": position,
"api_config": api_config,
"collapsed": collapsed,
}
)
# Store control in persistent state
control_key = f"geocoder_{position}"
current_controls = dict(self._controls)
current_controls[control_key] = {
"type": "geocoder",
"position": position,
"options": control_options,
}
self._controls = current_controls
self.call_js_method("addControl", "geocoder", control_options)
add_geojson_layer(self, layer_id, geojson_data, layer_type='fill', paint=None, before_id=None)
¶
Add a GeoJSON layer to the map.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
layer_id |
str |
Unique identifier for the layer. |
required |
geojson_data |
Dict[str, Any] |
GeoJSON data as a dictionary. |
required |
layer_type |
str |
Type of layer (e.g., 'fill', 'line', 'circle', 'symbol'). |
'fill' |
paint |
Optional[Dict[str, Any]] |
Optional paint properties for styling the layer. |
None |
before_id |
Optional[str] |
Optional layer ID to insert this layer before. |
None |
Source code in anymap/maplibre.py
def add_geojson_layer(
self,
layer_id: str,
geojson_data: Dict[str, Any],
layer_type: str = "fill",
paint: Optional[Dict[str, Any]] = None,
before_id: Optional[str] = None,
) -> None:
"""Add a GeoJSON layer to the map.
Args:
layer_id: Unique identifier for the layer.
geojson_data: GeoJSON data as a dictionary.
layer_type: Type of layer (e.g., 'fill', 'line', 'circle', 'symbol').
paint: Optional paint properties for styling the layer.
before_id: Optional layer ID to insert this layer before.
"""
source_id = f"{layer_id}_source"
# Add source
self.add_source(source_id, {"type": "geojson", "data": geojson_data})
# Add layer
layer_config = {"id": layer_id, "type": layer_type, "source": source_id}
if paint:
layer_config["paint"] = paint
self.add_layer(layer_id, layer_config, before_id)
add_google_streetview(self, position='top-left', api_key=None, options=None)
¶
Add a Google Street View control to the map.
This method adds a Google Street View control that allows users to view street-level imagery at clicked locations on the map.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
position |
str |
Position on map ('top-left', 'top-right', 'bottom-left', 'bottom-right') |
'top-left' |
api_key |
Optional[str] |
Google Maps API key. If None, retrieves from GOOGLE_MAPS_API_KEY environment variable |
None |
options |
Optional[Dict[str, Any]] |
Additional options for the Street View control |
None |
Exceptions:
Type | Description |
---|---|
ValueError |
If no API key is provided and none can be found in environment variables |
Source code in anymap/maplibre.py
def add_google_streetview(
self,
position: str = "top-left",
api_key: Optional[str] = None,
options: Optional[Dict[str, Any]] = None,
) -> None:
"""Add a Google Street View control to the map.
This method adds a Google Street View control that allows users to view
street-level imagery at clicked locations on the map.
Args:
position: Position on map ('top-left', 'top-right', 'bottom-left', 'bottom-right')
api_key: Google Maps API key. If None, retrieves from GOOGLE_MAPS_API_KEY environment variable
options: Additional options for the Street View control
Raises:
ValueError: If no API key is provided and none can be found in environment variables
"""
if api_key is None:
api_key = get_env_var("GOOGLE_MAPS_API_KEY")
if api_key is None:
raise ValueError(
"Google Maps API key is required. Please provide it as a parameter "
"or set the GOOGLE_MAPS_API_KEY environment variable."
)
control_options = options or {}
control_options.update(
{
"position": position,
"api_key": api_key,
}
)
# Store control in persistent state
control_key = f"google_streetview_{position}"
current_controls = dict(self._controls)
current_controls[control_key] = {
"type": "google_streetview",
"position": position,
"options": control_options,
}
self._controls = current_controls
self.call_js_method("addControl", "google_streetview", control_options)
add_image_layer(self, layer_id, image_url, coordinates, paint=None, before_id=None)
¶
Add an image layer to the map.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
layer_id |
str |
Unique identifier for the layer. |
required |
image_url |
str |
URL of the image to display. |
required |
coordinates |
List[List[float]] |
Corner coordinates of the image as [[top-left], [top-right], [bottom-right], [bottom-left]]. Each coordinate should be [longitude, latitude]. |
required |
paint |
Optional[Dict[str, Any]] |
Optional paint properties for the image layer. |
None |
before_id |
Optional[str] |
Optional layer ID to insert this layer before. |
None |
Source code in anymap/maplibre.py
def add_image_layer(
self,
layer_id: str,
image_url: str,
coordinates: List[List[float]],
paint: Optional[Dict[str, Any]] = None,
before_id: Optional[str] = None,
) -> None:
"""Add an image layer to the map.
Args:
layer_id: Unique identifier for the layer.
image_url: URL of the image to display.
coordinates: Corner coordinates of the image as [[top-left], [top-right], [bottom-right], [bottom-left]].
Each coordinate should be [longitude, latitude].
paint: Optional paint properties for the image layer.
before_id: Optional layer ID to insert this layer before.
"""
source_id = f"{layer_id}_source"
# Add image source
self.add_source(
source_id, {"type": "image", "url": image_url, "coordinates": coordinates}
)
# Add raster layer for the image
layer_config = {"id": layer_id, "type": "raster", "source": source_id}
if paint:
layer_config["paint"] = paint
self.add_layer(layer_id, layer_config, before_id)
add_layer(self, layer_id, layer, before_id=None, opacity=1.0, visible=True)
¶
Add a layer to the map.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
layer_id |
str |
Unique identifier for the layer. |
required |
layer_config |
Layer configuration dictionary containing properties like type, source, paint, and layout. |
required | |
before_id |
Optional[str] |
Optional layer ID to insert this layer before. If None, layer is added on top. |
None |
Source code in anymap/maplibre.py
def add_layer(
self,
layer_id: str,
layer: Dict[str, Any],
before_id: Optional[str] = None,
opacity: Optional[float] = 1.0,
visible: Optional[bool] = True,
) -> None:
"""Add a layer to the map.
Args:
layer_id: Unique identifier for the layer.
layer_config: Layer configuration dictionary containing
properties like type, source, paint, and layout.
before_id: Optional layer ID to insert this layer before.
If None, layer is added on top.
"""
# Store layer in local state for persistence
current_layers = dict(self._layers)
current_layers[layer_id] = layer
self._layers = current_layers
# Call JavaScript method with before_id if provided
if before_id:
self.call_js_method("addLayer", layer, before_id)
else:
self.call_js_method("addLayer", layer, layer_id)
self.set_visibility(layer_id, visible)
self.set_opacity(layer_id, opacity)
self.layer_dict[layer_id] = {
"layer": layer,
"opacity": opacity,
"visible": visible,
"type": layer["type"],
# "color": color,
}
# Update the _layer_dict trait to trigger JavaScript sync
self._layer_dict = dict(self.layer_dict)
if self.layer_manager is not None:
self.layer_manager.refresh()
# Update layer controls if they exist
self._update_layer_controls()
add_layer_control(self, position='top-right', collapsed=True, layers=None, options=None)
¶
Add a collapsible layer control panel to the map.
The layer control is a collapsible panel that allows users to toggle visibility and adjust opacity of map layers. It displays as an icon similar to other controls, and expands when clicked.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
position |
str |
Position on map ('top-left', 'top-right', 'bottom-left', 'bottom-right') |
'top-right' |
collapsed |
bool |
Whether the control starts collapsed |
True |
layers |
Optional[List[str]] |
List of layer IDs to include. If None, includes all layers |
None |
options |
Optional[Dict[str, Any]] |
Additional options for the control |
None |
Source code in anymap/maplibre.py
def add_layer_control(
self,
position: str = "top-right",
collapsed: bool = True,
layers: Optional[List[str]] = None,
options: Optional[Dict[str, Any]] = None,
) -> None:
"""Add a collapsible layer control panel to the map.
The layer control is a collapsible panel that allows users to toggle
visibility and adjust opacity of map layers. It displays as an icon
similar to other controls, and expands when clicked.
Args:
position: Position on map ('top-left', 'top-right', 'bottom-left', 'bottom-right')
collapsed: Whether the control starts collapsed
layers: List of layer IDs to include. If None, includes all layers
options: Additional options for the control
"""
control_options = options or {}
control_options.update(
{
"position": position,
"collapsed": collapsed,
"layers": layers,
}
)
# Get current layer states for initialization
layer_states = {}
target_layers = layers if layers is not None else list(self.layer_dict.keys())
# Always include Background layer for controlling map style layers
if layers is None or "Background" in layers:
layer_states["Background"] = {
"visible": True,
"opacity": 1.0,
"name": "Background",
}
for layer_id in target_layers:
if layer_id in self.layer_dict and layer_id != "Background":
layer_info = self.layer_dict[layer_id]
layer_states[layer_id] = {
"visible": layer_info.get("visible", True),
"opacity": layer_info.get("opacity", 1.0),
"name": layer_id, # Use layer_id as display name by default
}
control_options["layerStates"] = layer_states
# Store control in persistent state
control_key = f"layer_control_{position}"
current_controls = dict(self._controls)
current_controls[control_key] = {
"type": "layer_control",
"position": position,
"options": control_options,
}
self._controls = current_controls
self.call_js_method("addControl", "layer_control", control_options)
add_marker(self, lng, lat, popup=None)
¶
Add a marker to the map.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
lng |
float |
Longitude coordinate for the marker. |
required |
lat |
float |
Latitude coordinate for the marker. |
required |
popup |
Optional[str] |
Optional popup text to display when marker is clicked. |
None |
Source code in anymap/maplibre.py
def add_marker(self, lng: float, lat: float, popup: Optional[str] = None) -> None:
"""Add a marker to the map.
Args:
lng: Longitude coordinate for the marker.
lat: Latitude coordinate for the marker.
popup: Optional popup text to display when marker is clicked.
"""
marker_data = {"coordinates": [lng, lat], "popup": popup}
self.call_js_method("addMarker", marker_data)
add_pmtiles(self, pmtiles_url, layer_id=None, layers=None, opacity=1.0, visible=True, before_id=None)
¶
Add PMTiles vector tiles to the map.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
pmtiles_url |
str |
URL to the PMTiles file. |
required |
layer_id |
Optional[str] |
Optional unique identifier for the layer. If None, uses filename. |
None |
layers |
Optional[List[Dict[str, Any]]] |
Optional list of layer configurations for rendering. If None, creates default layers. |
None |
opacity |
Optional[float] |
Layer opacity between 0.0 and 1.0. |
1.0 |
visible |
Optional[bool] |
Whether the layer should be visible initially. |
True |
before_id |
Optional[str] |
Optional layer ID to insert this layer before. |
None |
Source code in anymap/maplibre.py
def add_pmtiles(
self,
pmtiles_url: str,
layer_id: Optional[str] = None,
layers: Optional[List[Dict[str, Any]]] = None,
opacity: Optional[float] = 1.0,
visible: Optional[bool] = True,
before_id: Optional[str] = None,
) -> None:
"""Add PMTiles vector tiles to the map.
Args:
pmtiles_url: URL to the PMTiles file.
layer_id: Optional unique identifier for the layer. If None, uses filename.
layers: Optional list of layer configurations for rendering. If None, creates default layers.
opacity: Layer opacity between 0.0 and 1.0.
visible: Whether the layer should be visible initially.
before_id: Optional layer ID to insert this layer before.
"""
if layer_id is None:
layer_id = pmtiles_url.split("/")[-1].replace(".pmtiles", "")
source_id = f"{layer_id}_source"
# Add PMTiles source using pmtiles:// protocol
pmtiles_source_url = f"pmtiles://{pmtiles_url}"
self.add_source(
source_id,
{
"type": "vector",
"url": pmtiles_source_url,
"attribution": "PMTiles",
},
)
# Add default layers if none provided
if layers is None:
layers = [
{
"id": f"{layer_id}_landuse",
"source": source_id,
"source-layer": "landuse",
"type": "fill",
"paint": {"fill-color": "steelblue", "fill-opacity": 0.5},
},
{
"id": f"{layer_id}_roads",
"source": source_id,
"source-layer": "roads",
"type": "line",
"paint": {"line-color": "black", "line-width": 1},
},
{
"id": f"{layer_id}_buildings",
"source": source_id,
"source-layer": "buildings",
"type": "fill",
"paint": {"fill-color": "gray", "fill-opacity": 0.7},
},
{
"id": f"{layer_id}_water",
"source": source_id,
"source-layer": "water",
"type": "fill",
"paint": {"fill-color": "lightblue", "fill-opacity": 0.8},
},
]
# Add all layers
for layer_config in layers:
self.add_layer(
layer_config["id"],
layer_config,
before_id,
opacity=opacity,
visible=visible,
)
add_terra_draw(self, position='top-left', modes=None, open=True, **kwargs)
¶
Add a Terra Draw control to the map for drawing and editing geometries.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
position |
str |
Position on map ('top-left', 'top-right', 'bottom-left', 'bottom-right') |
'top-left' |
modes |
Optional[List[str]] |
List of drawing modes to enable. Available modes: ['render', 'point', 'linestring', 'polygon', 'rectangle', 'circle', 'freehand', 'angled-rectangle', 'sensor', 'sector', 'select', 'delete-selection', 'delete', 'download'] Defaults to all modes except 'render' |
None |
open |
bool |
Whether the draw control panel should be open by default |
True |
**kwargs |
Any |
Additional options to pass to Terra Draw constructor |
{} |
Source code in anymap/maplibre.py
def add_terra_draw(
self,
position: str = "top-left",
modes: Optional[List[str]] = None,
open: bool = True,
**kwargs: Any,
) -> None:
"""Add a Terra Draw control to the map for drawing and editing geometries.
Args:
position: Position on map ('top-left', 'top-right', 'bottom-left', 'bottom-right')
modes: List of drawing modes to enable. Available modes:
['render', 'point', 'linestring', 'polygon', 'rectangle', 'circle',
'freehand', 'angled-rectangle', 'sensor', 'sector', 'select',
'delete-selection', 'delete', 'download']
Defaults to all modes except 'render'
open: Whether the draw control panel should be open by default
**kwargs: Additional options to pass to Terra Draw constructor
"""
if modes is None:
modes = [
# 'render', # Commented out to always show drawing tool
"point",
"linestring",
"polygon",
"rectangle",
"circle",
"freehand",
"angled-rectangle",
"sensor",
"sector",
"select",
"delete-selection",
"delete",
"download",
]
terra_draw_options = {
"modes": modes,
"open": open,
"position": position,
**kwargs,
}
# Mark that Terra Draw is enabled
self._terra_draw_enabled = True
# Store Terra Draw control configuration
current_controls = dict(self._controls)
terra_draw_key = f"terra_draw_{position}"
current_controls[terra_draw_key] = {
"type": "terra_draw",
"position": position,
"options": terra_draw_options,
}
self._controls = current_controls
self.call_js_method("addTerraDrawControl", terra_draw_options)
add_tile_layer(self, layer_id, source_url, attribution=None, opacity=1.0, visible=True, minzoom=None, maxzoom=None, paint=None, layout=None, before_id=None, **kwargs)
¶
Add a raster tile layer to the map.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
layer_id |
str |
Unique identifier for the layer. |
required |
source_url |
str |
URL template for the tile source (e.g., 'https://example.com/{z}/{x}/{y}.png'). |
required |
attribution |
Optional[str] |
Optional attribution text for the tile source. |
None |
opacity |
Optional[float] |
Layer opacity between 0.0 and 1.0. |
1.0 |
visible |
Optional[bool] |
Whether the layer should be visible initially. |
True |
minzoom |
Optional[int] |
Minimum zoom level for the layer. |
None |
maxzoom |
Optional[int] |
Maximum zoom level for the layer. |
None |
paint |
Optional[Dict[str, Any]] |
Optional paint properties for the layer. |
None |
layout |
Optional[Dict[str, Any]] |
Optional layout properties for the layer. |
None |
before_id |
Optional[str] |
Optional layer ID to insert this layer before. |
None |
**kwargs |
Any |
Additional source configuration options. |
{} |
Source code in anymap/maplibre.py
def add_tile_layer(
self,
layer_id: str,
source_url: str,
attribution: Optional[str] = None,
opacity: Optional[float] = 1.0,
visible: Optional[bool] = True,
minzoom: Optional[int] = None,
maxzoom: Optional[int] = None,
paint: Optional[Dict[str, Any]] = None,
layout: Optional[Dict[str, Any]] = None,
before_id: Optional[str] = None,
**kwargs: Any,
) -> None:
"""Add a raster tile layer to the map.
Args:
layer_id: Unique identifier for the layer.
source_url: URL template for the tile source (e.g., 'https://example.com/{z}/{x}/{y}.png').
attribution: Optional attribution text for the tile source.
opacity: Layer opacity between 0.0 and 1.0.
visible: Whether the layer should be visible initially.
minzoom: Minimum zoom level for the layer.
maxzoom: Maximum zoom level for the layer.
paint: Optional paint properties for the layer.
layout: Optional layout properties for the layer.
before_id: Optional layer ID to insert this layer before.
**kwargs: Additional source configuration options.
"""
source_id = f"{layer_id}_source"
# Add raster source
self.add_source(
source_id,
{"type": "raster", "tiles": [source_url], "tileSize": 256, **kwargs},
)
# Add raster layer
layer_config = {"id": layer_id, "type": "raster", "source": source_id}
if paint:
layer_config["paint"] = paint
if layout:
layer_config["layout"] = layout
self.add_layer(layer_id, layer_config, before_id)
add_to_sidebar(self, widget, add_header=True, widget_icon='mdi-tools', close_icon='mdi-close', label='My Tools', background_color='#f5f5f5', height='40px', expanded=True, **kwargs)
¶
Appends a widget to the sidebar content.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
widget |
Optional[Union[widgets.Widget, List[widgets.Widget]]] |
Initial widget(s) to display in the content box. |
required |
widget_icon |
str |
Icon for the header. See https://pictogrammers.github.io/@mdi/font/2.0.46/ for available icons. |
'mdi-tools' |
close_icon |
str |
Icon for the close button. See https://pictogrammers.github.io/@mdi/font/2.0.46/ for available icons. |
'mdi-close' |
background_color |
str |
Background color of the header. Defaults to "#f5f5f5". |
'#f5f5f5' |
label |
str |
Text label for the header. Defaults to "My Tools". |
'My Tools' |
height |
str |
Height of the header. Defaults to "40px". |
'40px' |
expanded |
bool |
Whether the panel is expanded by default. Defaults to True. |
True |
**kwargs |
Any |
Additional keyword arguments for the parent class. |
{} |
Source code in anymap/maplibre.py
def add_to_sidebar(
self,
widget: widgets.Widget,
add_header: bool = True,
widget_icon: str = "mdi-tools",
close_icon: str = "mdi-close",
label: str = "My Tools",
background_color: str = "#f5f5f5",
height: str = "40px",
expanded: bool = True,
**kwargs: Any,
) -> None:
"""
Appends a widget to the sidebar content.
Args:
widget (Optional[Union[widgets.Widget, List[widgets.Widget]]]): Initial widget(s) to display in the content box.
widget_icon (str): Icon for the header. See https://pictogrammers.github.io/@mdi/font/2.0.46/ for available icons.
close_icon (str): Icon for the close button. See https://pictogrammers.github.io/@mdi/font/2.0.46/ for available icons.
background_color (str): Background color of the header. Defaults to "#f5f5f5".
label (str): Text label for the header. Defaults to "My Tools".
height (str): Height of the header. Defaults to "40px".
expanded (bool): Whether the panel is expanded by default. Defaults to True.
**kwargs (Any): Additional keyword arguments for the parent class.
"""
if self.container is None:
self.create_container(**self.sidebar_args)
self.container.add_to_sidebar(
widget,
add_header=add_header,
widget_icon=widget_icon,
close_icon=close_icon,
label=label,
background_color=background_color,
height=height,
expanded=expanded,
host_map=self,
**kwargs,
)
add_vector_layer(self, layer_id, source_url, source_layer, layer_type='fill', paint=None, layout=None, before_id=None)
¶
Add a vector tile layer to the map.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
layer_id |
str |
Unique identifier for the layer. |
required |
source_url |
str |
URL for the vector tile source. |
required |
source_layer |
str |
Name of the source layer within the vector tiles. |
required |
layer_type |
str |
Type of layer (e.g., 'fill', 'line', 'circle', 'symbol'). |
'fill' |
paint |
Optional[Dict[str, Any]] |
Optional paint properties for styling the layer. |
None |
layout |
Optional[Dict[str, Any]] |
Optional layout properties for the layer. |
None |
before_id |
Optional[str] |
Optional layer ID to insert this layer before. |
None |
Source code in anymap/maplibre.py
def add_vector_layer(
self,
layer_id: str,
source_url: str,
source_layer: str,
layer_type: str = "fill",
paint: Optional[Dict[str, Any]] = None,
layout: Optional[Dict[str, Any]] = None,
before_id: Optional[str] = None,
) -> None:
"""Add a vector tile layer to the map.
Args:
layer_id: Unique identifier for the layer.
source_url: URL for the vector tile source.
source_layer: Name of the source layer within the vector tiles.
layer_type: Type of layer (e.g., 'fill', 'line', 'circle', 'symbol').
paint: Optional paint properties for styling the layer.
layout: Optional layout properties for the layer.
before_id: Optional layer ID to insert this layer before.
"""
source_id = f"{layer_id}_source"
# Add vector source
self.add_source(source_id, {"type": "vector", "url": source_url})
# Add vector layer
layer_config = {
"id": layer_id,
"type": layer_type,
"source": source_id,
"source-layer": source_layer,
}
if paint:
layer_config["paint"] = paint
if layout:
layer_config["layout"] = layout
self.add_layer(layer_id, layer_config, before_id)
clear_draw_data(self)
¶
Clear all drawn features from the draw control.
Source code in anymap/maplibre.py
def clear_draw_data(self) -> None:
"""Clear all drawn features from the draw control."""
# Clear the trait data immediately
self._draw_data = {"type": "FeatureCollection", "features": []}
# Clear in JavaScript
self.call_js_method("clearDrawData")
clear_terra_draw_data(self)
¶
Clear all Terra Draw features from the draw control.
Source code in anymap/maplibre.py
def clear_terra_draw_data(self) -> None:
"""Clear all Terra Draw features from the draw control."""
# Clear the trait data immediately
self._terra_draw_data = {"type": "FeatureCollection", "features": []}
# Clear in JavaScript
self.call_js_method("clearTerraDrawData")
create_container(self, sidebar_visible=None, min_width=None, max_width=None, expanded=None, **kwargs)
¶
Creates a container widget for the map with an optional sidebar.
This method initializes a LayerManagerWidget
and a Container
widget to display the map
alongside a sidebar. The sidebar can be customized with visibility, width, and additional content.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
sidebar_visible |
bool |
Whether the sidebar is visible. Defaults to False. |
None |
min_width |
int |
Minimum width of the sidebar in pixels. Defaults to 360. |
None |
max_width |
int |
Maximum width of the sidebar in pixels. Defaults to 360. |
None |
expanded |
bool |
Whether the |
None |
**kwargs |
Any |
Additional keyword arguments passed to the |
{} |
Returns:
Type | Description |
---|---|
Container |
The created container widget with the map and sidebar. |
Source code in anymap/maplibre.py
def create_container(
self,
sidebar_visible: bool = None,
min_width: int = None,
max_width: int = None,
expanded: bool = None,
**kwargs: Any,
):
"""
Creates a container widget for the map with an optional sidebar.
This method initializes a `LayerManagerWidget` and a `Container` widget to display the map
alongside a sidebar. The sidebar can be customized with visibility, width, and additional content.
Args:
sidebar_visible (bool): Whether the sidebar is visible. Defaults to False.
min_width (int): Minimum width of the sidebar in pixels. Defaults to 360.
max_width (int): Maximum width of the sidebar in pixels. Defaults to 360.
expanded (bool): Whether the `LayerManagerWidget` is expanded by default. Defaults to True.
**kwargs (Any): Additional keyword arguments passed to the `Container` widget.
Returns:
Container: The created container widget with the map and sidebar.
"""
if sidebar_visible is None:
sidebar_visible = self.sidebar_args.get("sidebar_visible", False)
if min_width is None:
min_width = self.sidebar_args.get("min_width", 360)
if max_width is None:
max_width = self.sidebar_args.get("max_width", 360)
if expanded is None:
expanded = self.sidebar_args.get("expanded", True)
if self.layer_manager is None:
self.layer_manager = LayerManagerWidget(self, expanded=expanded)
container = Container(
host_map=self,
sidebar_visible=sidebar_visible,
min_width=min_width,
max_width=max_width,
sidebar_content=[self.layer_manager],
**kwargs,
)
self.container = container
self.container.sidebar_widgets["Layers"] = self.layer_manager
return container
delete_draw_features(self, feature_ids)
¶
Delete specific features from the draw control.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
feature_ids |
List[str] |
List of feature IDs to delete |
required |
Source code in anymap/maplibre.py
def delete_draw_features(self, feature_ids: List[str]) -> None:
"""Delete specific features from the draw control.
Args:
feature_ids: List of feature IDs to delete
"""
self.call_js_method("deleteDrawFeatures", feature_ids)
fit_bounds(self, bounds, padding=50)
¶
Fit the map to given bounds.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
bounds |
List[List[float]] |
Bounding box as [[south, west], [north, east]]. |
required |
padding |
int |
Padding around the bounds in pixels. |
50 |
Source code in anymap/maplibre.py
def fit_bounds(self, bounds: List[List[float]], padding: int = 50) -> None:
"""Fit the map to given bounds.
Args:
bounds: Bounding box as [[south, west], [north, east]].
padding: Padding around the bounds in pixels.
"""
self.call_js_method("fitBounds", bounds, {"padding": padding})
get_draw_data(self)
¶
Get all drawn features as GeoJSON.
Returns:
Type | Description |
---|---|
Dict[str, Any] |
Dict containing GeoJSON FeatureCollection with drawn features |
Source code in anymap/maplibre.py
def get_draw_data(self) -> Dict[str, Any]:
"""Get all drawn features as GeoJSON.
Returns:
Dict containing GeoJSON FeatureCollection with drawn features
"""
# Try to get current data first
if self._draw_data:
return self._draw_data
# If no data in trait, call JavaScript to get fresh data
self.call_js_method("getDrawData")
# Give JavaScript time to execute and sync data
import time
time.sleep(0.2)
# Return the synced data or empty FeatureCollection if nothing
return (
self._draw_data
if self._draw_data
else {"type": "FeatureCollection", "features": []}
)
get_layer_type(self, layer_id)
¶
Get the type of a layer.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
layer_id |
str |
Unique identifier of the layer. |
required |
Returns:
Type | Description |
---|---|
Optional[str] |
Layer type string, or None if layer doesn't exist. |
Source code in anymap/maplibre.py
def get_layer_type(self, layer_id: str) -> Optional[str]:
"""Get the type of a layer.
Args:
layer_id: Unique identifier of the layer.
Returns:
Layer type string, or None if layer doesn't exist.
"""
if layer_id in self._layers:
return self._layers[layer_id]["type"]
else:
return None
get_style(self)
¶
Get the style of the map.
Returns:
Type | Description |
---|---|
Dict |
The style of the map. |
Source code in anymap/maplibre.py
def get_style(self):
"""
Get the style of the map.
Returns:
Dict: The style of the map.
"""
if self._style is not None:
if isinstance(self._style, str):
response = requests.get(self._style, timeout=10)
style = response.json()
elif isinstance(self._style, dict):
style = self._style
else:
style = {}
return style
else:
return {}
get_style_layers(self, return_ids=False, sorted=True)
¶
Get the names of the basemap layers.
Returns:
Type | Description |
---|---|
List[str] |
The names of the basemap layers. |
Source code in anymap/maplibre.py
def get_style_layers(self, return_ids=False, sorted=True) -> List[str]:
"""
Get the names of the basemap layers.
Returns:
List[str]: The names of the basemap layers.
"""
style = self.get_style()
if "layers" in style:
layers = style["layers"]
if return_ids:
ids = [layer["id"] for layer in layers]
if sorted:
ids.sort()
return ids
else:
return layers
else:
return []
get_terra_draw_data(self)
¶
Get all Terra Draw features as GeoJSON.
Returns:
Type | Description |
---|---|
Dict[str, Any] |
Dict containing GeoJSON FeatureCollection with drawn features |
Source code in anymap/maplibre.py
def get_terra_draw_data(self) -> Dict[str, Any]:
"""Get all Terra Draw features as GeoJSON.
Returns:
Dict containing GeoJSON FeatureCollection with drawn features
"""
# Try to get current data first
if self._terra_draw_data:
return self._terra_draw_data
# If no data in trait, call JavaScript to get fresh data
self.call_js_method("getTerraDrawData")
# Give JavaScript time to execute and sync data
import time
time.sleep(0.2)
# Return the synced data or empty FeatureCollection if nothing
return (
self._terra_draw_data
if self._terra_draw_data
else {"type": "FeatureCollection", "features": []}
)
load_draw_data(self, geojson_data)
¶
Load GeoJSON data into the draw control.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
geojson_data |
Union[Dict[str, Any], str] |
GeoJSON data as dictionary or JSON string |
required |
Source code in anymap/maplibre.py
def load_draw_data(self, geojson_data: Union[Dict[str, Any], str]) -> None:
"""Load GeoJSON data into the draw control.
Args:
geojson_data: GeoJSON data as dictionary or JSON string
"""
if isinstance(geojson_data, str):
geojson_data = json.loads(geojson_data)
# Update the trait immediately to ensure consistency
self._draw_data = geojson_data
# Send to JavaScript
self.call_js_method("loadDrawData", geojson_data)
load_terra_draw_data(self, geojson_data)
¶
Load GeoJSON data into the Terra Draw control.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
geojson_data |
Union[Dict[str, Any], str] |
GeoJSON data as dictionary or JSON string |
required |
Source code in anymap/maplibre.py
def load_terra_draw_data(self, geojson_data: Union[Dict[str, Any], str]) -> None:
"""Load GeoJSON data into the Terra Draw control.
Args:
geojson_data: GeoJSON data as dictionary or JSON string
"""
if isinstance(geojson_data, str):
geojson_data = json.loads(geojson_data)
# Update the trait immediately to ensure consistency
self._terra_draw_data = geojson_data
# Send to JavaScript
self.call_js_method("loadTerraDrawData", geojson_data)
remove_control(self, control_type, position='top-right')
¶
Remove a control from the map.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
control_type |
str |
Type of control to remove ('navigation', 'scale', 'fullscreen', 'geolocate', 'attribution', 'globe') |
required |
position |
str |
Position where the control was added ('top-left', 'top-right', 'bottom-left', 'bottom-right') |
'top-right' |
Source code in anymap/maplibre.py
def remove_control(
self,
control_type: str,
position: str = "top-right",
) -> None:
"""Remove a control from the map.
Args:
control_type: Type of control to remove ('navigation', 'scale', 'fullscreen', 'geolocate', 'attribution', 'globe')
position: Position where the control was added ('top-left', 'top-right', 'bottom-left', 'bottom-right')
"""
# Remove control from persistent state
control_key = f"{control_type}_{position}"
current_controls = dict(self._controls)
if control_key in current_controls:
del current_controls[control_key]
self._controls = current_controls
self.call_js_method("removeControl", control_type, position)
remove_from_sidebar(self, widget=None, name=None)
¶
Removes a widget from the sidebar content.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
widget |
widgets.Widget |
The widget to remove from the sidebar. |
None |
name |
str |
The name of the widget to remove from the sidebar. |
None |
Source code in anymap/maplibre.py
def remove_from_sidebar(
self, widget: widgets.Widget = None, name: str = None
) -> None:
"""
Removes a widget from the sidebar content.
Args:
widget (widgets.Widget): The widget to remove from the sidebar.
name (str): The name of the widget to remove from the sidebar.
"""
if self.container is not None:
self.container.remove_from_sidebar(widget, name)
remove_layer(self, layer_id)
¶
Remove a layer from the map.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
layer_id |
str |
Unique identifier for the layer to remove. |
required |
Source code in anymap/maplibre.py
def remove_layer(self, layer_id: str) -> None:
"""Remove a layer from the map.
Args:
layer_id: Unique identifier for the layer to remove.
"""
# Remove from JavaScript map
self.call_js_method("removeLayer", layer_id)
# Remove from local state
if layer_id in self._layers:
current_layers = dict(self._layers)
del current_layers[layer_id]
self._layers = current_layers
# Remove from layer_dict
if layer_id in self.layer_dict:
del self.layer_dict[layer_id]
# Update layer controls if they exist
self._update_layer_controls()
set_bearing(self, bearing)
¶
Set the map bearing (rotation).
Parameters:
Name | Type | Description | Default |
---|---|---|---|
bearing |
float |
Map rotation in degrees (0-360). |
required |
Source code in anymap/maplibre.py
def set_bearing(self, bearing: float) -> None:
"""Set the map bearing (rotation).
Args:
bearing: Map rotation in degrees (0-360).
"""
self.bearing = bearing
set_center(self, lng, lat)
¶
Set the map center coordinates.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
lng |
float |
Longitude coordinate. |
required |
lat |
float |
Latitude coordinate. |
required |
Source code in anymap/maplibre.py
def set_center(self, lng: float, lat: float) -> None:
"""Set the map center coordinates.
Args:
lng: Longitude coordinate.
lat: Latitude coordinate.
"""
self.center = [lng, lat]
self._current_center = [lng, lat]
set_draw_mode(self, mode)
¶
Set the draw control mode.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
mode |
str |
Draw mode ('simple_select', 'direct_select', 'draw_point', 'draw_line_string', 'draw_polygon', 'static') |
required |
Source code in anymap/maplibre.py
def set_draw_mode(self, mode: str) -> None:
"""Set the draw control mode.
Args:
mode: Draw mode ('simple_select', 'direct_select', 'draw_point',
'draw_line_string', 'draw_polygon', 'static')
"""
self.call_js_method("setDrawMode", mode)
set_layout_property(self, layer_id, name, value)
¶
Set a layout property for a layer.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
layer_id |
str |
Unique identifier of the layer. |
required |
name |
str |
Name of the layout property to set. |
required |
value |
Any |
Value to set for the property. |
required |
Source code in anymap/maplibre.py
def set_layout_property(self, layer_id: str, name: str, value: Any) -> None:
"""Set a layout property for a layer.
Args:
layer_id: Unique identifier of the layer.
name: Name of the layout property to set.
value: Value to set for the property.
"""
self.call_js_method("setLayoutProperty", layer_id, name, value)
set_opacity(self, layer_id, opacity)
¶
Set the opacity of a layer.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
layer_id |
str |
Unique identifier of the layer. |
required |
opacity |
float |
Opacity value between 0.0 (transparent) and 1.0 (opaque). |
required |
Source code in anymap/maplibre.py
def set_opacity(self, layer_id: str, opacity: float) -> None:
"""Set the opacity of a layer.
Args:
layer_id: Unique identifier of the layer.
opacity: Opacity value between 0.0 (transparent) and 1.0 (opaque).
"""
layer_type = self.get_layer_type(layer_id)
if layer_id == "Background":
for layer in self.get_style_layers():
layer_type = layer.get("type")
if layer_type != "symbol":
self.set_paint_property(
layer["id"], f"{layer_type}-opacity", opacity
)
else:
self.set_paint_property(layer["id"], "icon-opacity", opacity)
self.set_paint_property(layer["id"], "text-opacity", opacity)
return
if layer_id in self.layer_dict:
layer_type = self.layer_dict[layer_id]["layer"]["type"]
prop_name = f"{layer_type}-opacity"
self.layer_dict[layer_id]["opacity"] = opacity
self._update_layer_controls()
elif layer_id in self.style_dict:
layer = self.style_dict[layer_id]
layer_type = layer.get("type")
prop_name = f"{layer_type}-opacity"
if "paint" in layer:
layer["paint"][prop_name] = opacity
if layer_type != "symbol":
self.set_paint_property(layer_id, f"{layer_type}-opacity", opacity)
else:
self.set_paint_property(layer_id, "icon-opacity", opacity)
self.set_paint_property(layer_id, "text-opacity", opacity)
set_paint_property(self, layer_id, name, value)
¶
Set a paint property for a layer.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
layer_id |
str |
Unique identifier of the layer. |
required |
name |
str |
Name of the paint property to set. |
required |
value |
Any |
Value to set for the property. |
required |
Source code in anymap/maplibre.py
def set_paint_property(self, layer_id: str, name: str, value: Any) -> None:
"""Set a paint property for a layer.
Args:
layer_id: Unique identifier of the layer.
name: Name of the paint property to set.
value: Value to set for the property.
"""
self.call_js_method("setPaintProperty", layer_id, name, value)
set_pitch(self, pitch)
¶
Set the map pitch (tilt).
Parameters:
Name | Type | Description | Default |
---|---|---|---|
pitch |
float |
Map tilt in degrees (0-60). |
required |
Source code in anymap/maplibre.py
def set_pitch(self, pitch: float) -> None:
"""Set the map pitch (tilt).
Args:
pitch: Map tilt in degrees (0-60).
"""
self.pitch = pitch
set_projection(self, projection)
¶
Set the map projection.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
projection |
Dict[str, Any] |
Projection configuration dictionary. |
required |
Source code in anymap/maplibre.py
def set_projection(self, projection: Dict[str, Any]) -> None:
"""Set the map projection.
Args:
projection: Projection configuration dictionary.
"""
# Store projection in persistent state
self._projection = projection
self.call_js_method("setProjection", projection)
set_sidebar_content(self, content)
¶
Replaces all content in the sidebar (except the toggle button).
Parameters:
Name | Type | Description | Default |
---|---|---|---|
content |
Union[widgets.VBox, List[widgets.Widget]] |
The new content for the sidebar. |
required |
Source code in anymap/maplibre.py
def set_sidebar_content(
self, content: Union[widgets.VBox, List[widgets.Widget]]
) -> None:
"""
Replaces all content in the sidebar (except the toggle button).
Args:
content (Union[widgets.VBox, List[widgets.Widget]]): The new content for the sidebar.
"""
if self.container is not None:
self.container.set_sidebar_content(content)
set_sidebar_width(self, min_width=None, max_width=None)
¶
Dynamically updates the sidebar's minimum and maximum width.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
min_width |
int |
New minimum width in pixels. If None, keep current. |
None |
max_width |
int |
New maximum width in pixels. If None, keep current. |
None |
Source code in anymap/maplibre.py
def set_sidebar_width(self, min_width: int = None, max_width: int = None) -> None:
"""
Dynamically updates the sidebar's minimum and maximum width.
Args:
min_width (int, optional): New minimum width in pixels. If None, keep current.
max_width (int, optional): New maximum width in pixels. If None, keep current.
"""
if self.container is None:
self.create_container()
self.container.set_sidebar_width(min_width, max_width)
set_style(self, style)
¶
Set the map style.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
style |
Union[str, Dict[str, Any]] |
Map style as URL string or style object dictionary. |
required |
Source code in anymap/maplibre.py
def set_style(self, style: Union[str, Dict[str, Any]]) -> None:
"""Set the map style.
Args:
style: Map style as URL string or style object dictionary.
"""
if isinstance(style, str):
self.style = style
else:
self.call_js_method("setStyle", style)
set_terrain(self, source='https://elevation-tiles-prod.s3.amazonaws.com/terrarium/{z}/{x}/{y}.png', exaggeration=1.0, tile_size=256, encoding='terrarium', source_id='terrain-dem')
¶
Add terrain visualization to the map.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
source |
str |
URL template for terrain tiles. Defaults to AWS elevation tiles. |
'https://elevation-tiles-prod.s3.amazonaws.com/terrarium/{z}/{x}/{y}.png' |
exaggeration |
float |
Terrain exaggeration factor. Defaults to 1.0. |
1.0 |
tile_size |
int |
Tile size in pixels. Defaults to 256. |
256 |
encoding |
str |
Encoding for the terrain tiles. Defaults to "terrarium". |
'terrarium' |
source_id |
str |
Unique identifier for the terrain source. Defaults to "terrain-dem". |
'terrain-dem' |
Source code in anymap/maplibre.py
def set_terrain(
self,
source: str = "https://elevation-tiles-prod.s3.amazonaws.com/terrarium/{z}/{x}/{y}.png",
exaggeration: float = 1.0,
tile_size: int = 256,
encoding: str = "terrarium",
source_id: str = "terrain-dem",
) -> None:
"""Add terrain visualization to the map.
Args:
source: URL template for terrain tiles. Defaults to AWS elevation tiles.
exaggeration: Terrain exaggeration factor. Defaults to 1.0.
tile_size: Tile size in pixels. Defaults to 256.
encoding: Encoding for the terrain tiles. Defaults to "terrarium".
source_id: Unique identifier for the terrain source. Defaults to "terrain-dem".
"""
# Add terrain source
self.add_source(
source_id,
{
"type": "raster-dem",
"tiles": [source],
"tileSize": tile_size,
"encoding": encoding,
},
)
# Set terrain on the map
terrain_config = {"source": source_id, "exaggeration": exaggeration}
# Store terrain configuration in persistent state
self._terrain = terrain_config
self.call_js_method("setTerrain", terrain_config)
set_visibility(self, layer_id, visible)
¶
Set the visibility of a layer.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
layer_id |
str |
Unique identifier of the layer. |
required |
visible |
bool |
Whether the layer should be visible. |
required |
Source code in anymap/maplibre.py
def set_visibility(self, layer_id: str, visible: bool) -> None:
"""Set the visibility of a layer.
Args:
layer_id: Unique identifier of the layer.
visible: Whether the layer should be visible.
"""
if visible:
visibility = "visible"
else:
visibility = "none"
if layer_id == "Background":
for layer in self.get_style_layers():
self.set_layout_property(layer["id"], "visibility", visibility)
else:
self.set_layout_property(layer_id, "visibility", visibility)
if layer_id in self.layer_dict:
self.layer_dict[layer_id]["visible"] = visible
self._update_layer_controls()
set_zoom(self, zoom)
¶
Set the map zoom level.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
zoom |
float |
Zoom level (typically 0-20). |
required |
Source code in anymap/maplibre.py
def set_zoom(self, zoom: float) -> None:
"""Set the map zoom level.
Args:
zoom: Zoom level (typically 0-20).
"""
self.zoom = zoom
self._current_zoom = zoom
show(self, sidebar_visible=False, min_width=360, max_width=360, sidebar_content=None, **kwargs)
¶
Displays the map with an optional sidebar.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
sidebar_visible |
bool |
Whether the sidebar is visible. Defaults to False. |
False |
min_width |
int |
Minimum width of the sidebar in pixels. Defaults to 250. |
360 |
max_width |
int |
Maximum width of the sidebar in pixels. Defaults to 300. |
360 |
sidebar_content |
Optional[Any] |
Content to display in the sidebar. Defaults to None. |
None |
**kwargs |
Any |
Additional keyword arguments. |
{} |
Returns:
Type | Description |
---|---|
None |
None |
Source code in anymap/maplibre.py
def show(
self,
sidebar_visible: bool = False,
min_width: int = 360,
max_width: int = 360,
sidebar_content: Optional[Any] = None,
**kwargs: Any,
) -> None:
"""
Displays the map with an optional sidebar.
Args:
sidebar_visible (bool): Whether the sidebar is visible. Defaults to False.
min_width (int): Minimum width of the sidebar in pixels. Defaults to 250.
max_width (int): Maximum width of the sidebar in pixels. Defaults to 300.
sidebar_content (Optional[Any]): Content to display in the sidebar. Defaults to None.
**kwargs (Any): Additional keyword arguments.
Returns:
None
"""
return Container(
self,
sidebar_visible=sidebar_visible,
min_width=min_width,
max_width=max_width,
sidebar_content=sidebar_content,
**kwargs,
)