anymap_ts module¶
base
¶
Base MapWidget class for all map implementations.
MapWidget (AnyWidget)
¶
Base class for interactive map widgets.
This class provides the core functionality for Python-JavaScript communication using anywidget's traitlet synchronization system.
Source code in anymap_ts/base.py
class MapWidget(anywidget.AnyWidget):
"""Base class for interactive map widgets.
This class provides the core functionality for Python-JavaScript communication
using anywidget's traitlet synchronization system.
"""
# Synchronized traits for map state
center = traitlets.List([0.0, 0.0]).tag(sync=True)
zoom = traitlets.Float(2.0).tag(sync=True)
width = traitlets.Unicode("100%").tag(sync=True)
height = traitlets.Unicode("400px").tag(sync=True)
style = traitlets.Union([traitlets.Unicode(), traitlets.Dict()]).tag(sync=True)
# JavaScript method call queue
_js_calls = traitlets.List([]).tag(sync=True)
_js_method_counter = traitlets.Int(0)
# Events from JavaScript
_js_events = traitlets.List([]).tag(sync=True)
# State persistence for layers, sources, and controls
_layers = traitlets.Dict({}).tag(sync=True)
_sources = traitlets.Dict({}).tag(sync=True)
_controls = traitlets.Dict({}).tag(sync=True)
# Interaction state
clicked = traitlets.Dict({}).tag(sync=True)
current_bounds = traitlets.List([]).tag(sync=True)
current_center = traitlets.List([]).tag(sync=True)
current_zoom = traitlets.Float(0.0).tag(sync=True)
# Drawing data
_draw_data = traitlets.Dict({}).tag(sync=True)
def __init__(self, **kwargs):
"""Initialize the MapWidget.
Args:
**kwargs: Additional widget arguments
"""
super().__init__(**kwargs)
self._event_handlers: Dict[str, List[Callable]] = {}
self.observe(self._handle_js_events, names=["_js_events"])
def _handle_js_events(self, change: Dict[str, Any]) -> None:
"""Process events received from JavaScript.
Args:
change: Traitlet change dict
"""
events = change.get("new", [])
for event in events:
event_type = event.get("type")
if event_type in self._event_handlers:
for handler in self._event_handlers[event_type]:
try:
handler(event.get("data"))
except Exception as e:
print(f"Error in event handler for {event_type}: {e}")
# Clear processed events
self._js_events = []
def call_js_method(self, method: str, *args, **kwargs) -> None:
"""Queue a JavaScript method call.
Args:
method: Name of the JavaScript method to call
*args: Positional arguments for the method
**kwargs: Keyword arguments for the method
"""
self._js_method_counter += 1
call = {
"id": self._js_method_counter,
"method": method,
"args": list(args),
"kwargs": kwargs,
}
self._js_calls = self._js_calls + [call]
def on_map_event(self, event_type: str, handler: Callable) -> None:
"""Register an event handler.
Args:
event_type: Type of event (e.g., 'click', 'moveend')
handler: Callback function to handle the event
"""
if event_type not in self._event_handlers:
self._event_handlers[event_type] = []
self._event_handlers[event_type].append(handler)
def off_map_event(
self, event_type: str, handler: Optional[Callable] = None
) -> None:
"""Unregister an event handler.
Args:
event_type: Type of event
handler: Specific handler to remove. If None, removes all handlers.
"""
if event_type in self._event_handlers:
if handler is None:
del self._event_handlers[event_type]
else:
self._event_handlers[event_type] = [
h for h in self._event_handlers[event_type] if h != handler
]
def set_center(self, lng: float, lat: float) -> None:
"""Set the map center.
Args:
lng: Longitude
lat: Latitude
"""
self.center = [lng, lat]
def set_zoom(self, zoom: float) -> None:
"""Set the map zoom level.
Args:
zoom: Zoom level
"""
self.zoom = zoom
def fly_to(
self,
lng: float,
lat: float,
zoom: Optional[float] = None,
duration: int = 2000,
) -> None:
"""Fly to a location with animation.
Args:
lng: Longitude
lat: Latitude
zoom: Optional zoom level
duration: Animation duration in milliseconds
"""
self.call_js_method("flyTo", lng, lat, zoom=zoom, duration=duration)
def fit_bounds(
self,
bounds: List[float],
padding: int = 50,
duration: int = 1000,
) -> None:
"""Fit the map to the given bounds.
Args:
bounds: [west, south, east, north] bounds
padding: Padding in pixels
duration: Animation duration in milliseconds
"""
self.call_js_method("fitBounds", bounds, padding=padding, duration=duration)
@property
def viewstate(self) -> Dict[str, Any]:
"""Get current view state."""
return {
"center": self.current_center or self.center,
"zoom": self.current_zoom or self.zoom,
"bounds": self.current_bounds,
}
def _generate_html_template(self) -> str:
"""Generate HTML template for standalone export.
Override in subclasses for library-specific templates.
"""
raise NotImplementedError("Subclasses must implement _generate_html_template")
def to_html(
self,
filepath: Optional[Union[str, Path]] = None,
title: str = "Interactive Map",
) -> Optional[str]:
"""Export map to standalone HTML file.
Args:
filepath: Path to save the HTML file. If None, returns HTML string.
title: Title for the HTML page.
Returns:
HTML string if filepath is None, otherwise None.
"""
html = self._generate_html_template()
html = html.replace("{{title}}", title)
if filepath:
Path(filepath).write_text(html, encoding="utf-8")
return None
return html
viewstate: Dict[str, Any]
property
readonly
¶
Get current view state.
__init__(self, **kwargs)
special
¶
Initialize the MapWidget.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
**kwargs |
Additional widget arguments |
{} |
Source code in anymap_ts/base.py
def __init__(self, **kwargs):
"""Initialize the MapWidget.
Args:
**kwargs: Additional widget arguments
"""
super().__init__(**kwargs)
self._event_handlers: Dict[str, List[Callable]] = {}
self.observe(self._handle_js_events, names=["_js_events"])
call_js_method(self, method, *args, **kwargs)
¶
Queue a JavaScript method call.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
method |
str |
Name of the JavaScript method to call |
required |
*args |
Positional arguments for the method |
() |
|
**kwargs |
Keyword arguments for the method |
{} |
Source code in anymap_ts/base.py
def call_js_method(self, method: str, *args, **kwargs) -> None:
"""Queue a JavaScript method call.
Args:
method: Name of the JavaScript method to call
*args: Positional arguments for the method
**kwargs: Keyword arguments for the method
"""
self._js_method_counter += 1
call = {
"id": self._js_method_counter,
"method": method,
"args": list(args),
"kwargs": kwargs,
}
self._js_calls = self._js_calls + [call]
fit_bounds(self, bounds, padding=50, duration=1000)
¶
Fit the map to the given bounds.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
bounds |
List[float] |
[west, south, east, north] bounds |
required |
padding |
int |
Padding in pixels |
50 |
duration |
int |
Animation duration in milliseconds |
1000 |
Source code in anymap_ts/base.py
def fit_bounds(
self,
bounds: List[float],
padding: int = 50,
duration: int = 1000,
) -> None:
"""Fit the map to the given bounds.
Args:
bounds: [west, south, east, north] bounds
padding: Padding in pixels
duration: Animation duration in milliseconds
"""
self.call_js_method("fitBounds", bounds, padding=padding, duration=duration)
fly_to(self, lng, lat, zoom=None, duration=2000)
¶
Fly to a location with animation.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
lng |
float |
Longitude |
required |
lat |
float |
Latitude |
required |
zoom |
Optional[float] |
Optional zoom level |
None |
duration |
int |
Animation duration in milliseconds |
2000 |
Source code in anymap_ts/base.py
def fly_to(
self,
lng: float,
lat: float,
zoom: Optional[float] = None,
duration: int = 2000,
) -> None:
"""Fly to a location with animation.
Args:
lng: Longitude
lat: Latitude
zoom: Optional zoom level
duration: Animation duration in milliseconds
"""
self.call_js_method("flyTo", lng, lat, zoom=zoom, duration=duration)
off_map_event(self, event_type, handler=None)
¶
Unregister an event handler.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
event_type |
str |
Type of event |
required |
handler |
Optional[Callable] |
Specific handler to remove. If None, removes all handlers. |
None |
Source code in anymap_ts/base.py
def off_map_event(
self, event_type: str, handler: Optional[Callable] = None
) -> None:
"""Unregister an event handler.
Args:
event_type: Type of event
handler: Specific handler to remove. If None, removes all handlers.
"""
if event_type in self._event_handlers:
if handler is None:
del self._event_handlers[event_type]
else:
self._event_handlers[event_type] = [
h for h in self._event_handlers[event_type] if h != handler
]
on_map_event(self, event_type, handler)
¶
Register an event handler.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
event_type |
str |
Type of event (e.g., 'click', 'moveend') |
required |
handler |
Callable |
Callback function to handle the event |
required |
Source code in anymap_ts/base.py
def on_map_event(self, event_type: str, handler: Callable) -> None:
"""Register an event handler.
Args:
event_type: Type of event (e.g., 'click', 'moveend')
handler: Callback function to handle the event
"""
if event_type not in self._event_handlers:
self._event_handlers[event_type] = []
self._event_handlers[event_type].append(handler)
set_center(self, lng, lat)
¶
Set the map center.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
lng |
float |
Longitude |
required |
lat |
float |
Latitude |
required |
Source code in anymap_ts/base.py
def set_center(self, lng: float, lat: float) -> None:
"""Set the map center.
Args:
lng: Longitude
lat: Latitude
"""
self.center = [lng, lat]
set_zoom(self, zoom)
¶
Set the map zoom level.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
zoom |
float |
Zoom level |
required |
Source code in anymap_ts/base.py
def set_zoom(self, zoom: float) -> None:
"""Set the map zoom level.
Args:
zoom: Zoom level
"""
self.zoom = zoom
to_html(self, filepath=None, title='Interactive Map')
¶
Export map to standalone HTML file.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
filepath |
Optional[Union[str, Path]] |
Path to save the HTML file. If None, returns HTML string. |
None |
title |
str |
Title for the HTML page. |
'Interactive Map' |
Returns:
| Type | Description |
|---|---|
Optional[str] |
HTML string if filepath is None, otherwise None. |
Source code in anymap_ts/base.py
def to_html(
self,
filepath: Optional[Union[str, Path]] = None,
title: str = "Interactive Map",
) -> Optional[str]:
"""Export map to standalone HTML file.
Args:
filepath: Path to save the HTML file. If None, returns HTML string.
title: Title for the HTML page.
Returns:
HTML string if filepath is None, otherwise None.
"""
html = self._generate_html_template()
html = html.replace("{{title}}", title)
if filepath:
Path(filepath).write_text(html, encoding="utf-8")
return None
return html
basemaps
¶
Basemap provider utilities.
get_basemap_names()
¶
Get list of available basemap names.
Returns:
| Type | Description |
|---|---|
list |
List of basemap provider names |
Source code in anymap_ts/basemaps.py
def get_basemap_names() -> list:
"""Get list of available basemap names.
Returns:
List of basemap provider names
"""
return list(xyz.flatten().keys())
get_basemap_url(name)
¶
Get tile URL and attribution for a named basemap.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
name |
str |
Basemap provider name (e.g., "OpenStreetMap", "CartoDB.Positron") |
required |
Returns:
| Type | Description |
|---|---|
Tuple[str, str] |
Tuple of (tile_url, attribution) |
Exceptions:
| Type | Description |
|---|---|
ValueError |
If basemap name is not found |
Source code in anymap_ts/basemaps.py
def get_basemap_url(name: str) -> Tuple[str, str]:
"""Get tile URL and attribution for a named basemap.
Args:
name: Basemap provider name (e.g., "OpenStreetMap", "CartoDB.Positron")
Returns:
Tuple of (tile_url, attribution)
Raises:
ValueError: If basemap name is not found
"""
# Handle shortcuts
if name in BASEMAP_SHORTCUTS:
name = BASEMAP_SHORTCUTS[name]
# Handle dot notation for nested providers
parts = name.split(".")
provider = xyz
for part in parts:
provider = getattr(provider, part, None)
if provider is None:
raise ValueError(f"Unknown basemap: {name}")
url = provider.build_url()
attribution = provider.get("attribution", "")
return url, attribution
get_maplibre_style(name)
¶
Get MapLibre style URL by name.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
name |
str |
Style name (e.g., "positron", "dark-matter") |
required |
Returns:
| Type | Description |
|---|---|
str |
Style URL |
Exceptions:
| Type | Description |
|---|---|
ValueError |
If style name is not found |
Source code in anymap_ts/basemaps.py
def get_maplibre_style(name: str) -> str:
"""Get MapLibre style URL by name.
Args:
name: Style name (e.g., "positron", "dark-matter")
Returns:
Style URL
Raises:
ValueError: If style name is not found
"""
name_lower = name.lower().replace("_", "-")
if name_lower in MAPLIBRE_STYLES:
return MAPLIBRE_STYLES[name_lower]
# Assume it's already a URL
if name.startswith("http"):
return name
raise ValueError(f"Unknown MapLibre style: {name}")
cesium
¶
Cesium 3D globe widget implementation.
CesiumMap (MapWidget)
¶
Interactive 3D globe widget using Cesium.
This class provides a Python interface to Cesium for 3D globe visualization with terrain, 3D Tiles, and imagery layer support.
Examples:
>>> from anymap_ts import CesiumMap
>>> m = CesiumMap(center=[-122.4, 37.8], zoom=10)
>>> m.set_terrain() # Enable Cesium World Terrain
>>> m.add_3d_tileset(url="path/to/tileset.json")
>>> m
Source code in anymap_ts/cesium.py
class CesiumMap(MapWidget):
"""Interactive 3D globe widget using Cesium.
This class provides a Python interface to Cesium for 3D globe
visualization with terrain, 3D Tiles, and imagery layer support.
Example:
>>> from anymap_ts import CesiumMap
>>> m = CesiumMap(center=[-122.4, 37.8], zoom=10)
>>> m.set_terrain() # Enable Cesium World Terrain
>>> m.add_3d_tileset(url="path/to/tileset.json")
>>> m
"""
# ESM module for frontend
_esm = STATIC_DIR / "cesium.js"
# Cesium-specific traits
access_token = traitlets.Unicode("").tag(sync=True)
# Camera position traits
camera_height = traitlets.Float(10000000).tag(sync=True)
heading = traitlets.Float(0.0).tag(sync=True)
pitch = traitlets.Float(-90.0).tag(sync=True)
roll = traitlets.Float(0.0).tag(sync=True)
# Terrain
terrain_enabled = traitlets.Bool(False).tag(sync=True)
def __init__(
self,
center: Tuple[float, float] = (0.0, 0.0),
zoom: float = 2.0,
width: str = "100%",
height: str = "600px",
access_token: Optional[str] = None,
terrain: bool = False,
**kwargs,
):
"""Initialize a Cesium 3D globe.
Args:
center: Globe center as (longitude, latitude).
zoom: Initial zoom level (converted to camera height).
width: Widget width as CSS string.
height: Widget height as CSS string.
access_token: Cesium Ion access token (uses CESIUM_TOKEN env var if not provided).
terrain: Whether to enable terrain on initialization.
**kwargs: Additional widget arguments.
"""
# Get access token from env if not provided
if access_token is None:
access_token = get_cesium_token()
super().__init__(
center=list(center),
zoom=zoom,
width=width,
height=height,
access_token=access_token,
terrain_enabled=terrain,
**kwargs,
)
# Enable terrain if requested
if terrain:
self.set_terrain()
# -------------------------------------------------------------------------
# Basemap/Imagery Methods
# -------------------------------------------------------------------------
def add_basemap(
self,
basemap: str = "OpenStreetMap",
**kwargs,
) -> None:
"""Add a basemap imagery layer.
Args:
basemap: Name of basemap (e.g., "OpenStreetMap", "Bing").
**kwargs: Additional options.
"""
# Common basemap URLs
basemap_urls = {
"OpenStreetMap": "https://tile.openstreetmap.org/{z}/{x}/{y}.png",
"CartoDB.Positron": "https://a.basemaps.cartocdn.com/light_all/{z}/{x}/{y}.png",
"CartoDB.DarkMatter": "https://a.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}.png",
"Stamen.Terrain": "https://tiles.stadiamaps.com/tiles/stamen_terrain/{z}/{x}/{y}.png",
}
url = basemap_urls.get(basemap, basemap_urls["OpenStreetMap"])
self.call_js_method("addBasemap", url, name=basemap, **kwargs)
def add_imagery_layer(
self,
url: str,
name: Optional[str] = None,
layer_type: str = "xyz",
alpha: float = 1.0,
**kwargs,
) -> None:
"""Add an imagery layer.
Args:
url: Imagery URL or service endpoint.
name: Layer name.
layer_type: Type of imagery ('xyz', 'wms', 'wmts', 'arcgis').
alpha: Layer opacity (0-1).
**kwargs: Additional options (layers, parameters for WMS, etc.).
"""
layer_id = name or f"imagery-{len(self._layers)}"
self.call_js_method(
"addImageryLayer",
url=url,
name=layer_id,
type=layer_type,
alpha=alpha,
**kwargs,
)
self._layers = {
**self._layers,
layer_id: {"id": layer_id, "type": "imagery"},
}
def remove_imagery_layer(self, name: str) -> None:
"""Remove an imagery layer.
Args:
name: Layer name to remove.
"""
if name in self._layers:
layers = dict(self._layers)
del layers[name]
self._layers = layers
self.call_js_method("removeImageryLayer", name)
# -------------------------------------------------------------------------
# Terrain Methods
# -------------------------------------------------------------------------
def set_terrain(
self,
url: Optional[str] = None,
request_vertex_normals: bool = True,
request_water_mask: bool = True,
) -> None:
"""Enable terrain.
Args:
url: Terrain provider URL. If None, uses Cesium World Terrain (requires Ion token).
request_vertex_normals: Request vertex normals for lighting.
request_water_mask: Request water mask for water effects.
"""
self.terrain_enabled = True
self.call_js_method(
"setTerrain",
url=url or "cesium-world-terrain",
requestVertexNormals=request_vertex_normals,
requestWaterMask=request_water_mask,
)
def remove_terrain(self) -> None:
"""Disable terrain and use ellipsoid."""
self.terrain_enabled = False
self.call_js_method("removeTerrain")
# -------------------------------------------------------------------------
# 3D Tiles Methods
# -------------------------------------------------------------------------
def add_3d_tileset(
self,
url: Union[str, int],
name: Optional[str] = None,
maximum_screen_space_error: float = 16,
fly_to: bool = True,
**kwargs,
) -> None:
"""Add a 3D Tileset.
Args:
url: URL to tileset.json or Cesium Ion asset ID.
name: Tileset name.
maximum_screen_space_error: Maximum screen space error for LOD.
fly_to: Whether to fly to the tileset after loading.
**kwargs: Additional options.
"""
layer_id = name or f"tileset-{len(self._layers)}"
self.call_js_method(
"add3DTileset",
url=str(url),
name=layer_id,
maximumScreenSpaceError=maximum_screen_space_error,
flyTo=fly_to,
**kwargs,
)
self._layers = {
**self._layers,
layer_id: {"id": layer_id, "type": "3dtiles"},
}
def remove_3d_tileset(self, name: str) -> None:
"""Remove a 3D Tileset.
Args:
name: Tileset name to remove.
"""
if name in self._layers:
layers = dict(self._layers)
del layers[name]
self._layers = layers
self.call_js_method("remove3DTileset", name)
# -------------------------------------------------------------------------
# GeoJSON Methods
# -------------------------------------------------------------------------
def add_geojson(
self,
data: Any,
name: Optional[str] = None,
stroke: str = "#3388ff",
stroke_width: float = 2,
fill: str = "rgba(51, 136, 255, 0.5)",
clamp_to_ground: bool = True,
fly_to: bool = True,
**kwargs,
) -> None:
"""Add GeoJSON data.
Args:
data: GeoJSON dict or file path.
name: Data source name.
stroke: Stroke color.
stroke_width: Stroke width.
fill: Fill color.
clamp_to_ground: Whether to clamp features to terrain.
fly_to: Whether to fly to the data after loading.
**kwargs: Additional options.
"""
geojson = to_geojson(data)
layer_id = name or f"geojson-{len(self._layers)}"
self.call_js_method(
"addGeoJSON",
data=geojson,
name=layer_id,
stroke=stroke,
strokeWidth=stroke_width,
fill=fill,
clampToGround=clamp_to_ground,
flyTo=fly_to,
**kwargs,
)
self._layers = {
**self._layers,
layer_id: {"id": layer_id, "type": "geojson"},
}
def remove_data_source(self, name: str) -> None:
"""Remove a data source (GeoJSON, etc.).
Args:
name: Data source name to remove.
"""
if name in self._layers:
layers = dict(self._layers)
del layers[name]
self._layers = layers
self.call_js_method("removeDataSource", name)
# -------------------------------------------------------------------------
# Camera Methods
# -------------------------------------------------------------------------
def fly_to(
self,
lng: float,
lat: float,
height: Optional[float] = None,
zoom: Optional[float] = None,
heading: float = 0,
pitch: float = -90,
roll: float = 0,
duration: float = 2,
) -> None:
"""Fly to a location.
Args:
lng: Target longitude.
lat: Target latitude.
height: Camera height in meters (overrides zoom).
zoom: Zoom level (converted to height if height not provided).
heading: Camera heading in degrees.
pitch: Camera pitch in degrees (default -90 = looking down).
roll: Camera roll in degrees.
duration: Flight duration in seconds.
"""
self.call_js_method(
"flyTo",
lng,
lat,
height=height,
zoom=zoom,
heading=heading,
pitch=pitch,
roll=roll,
duration=duration,
)
def zoom_to(self, target: str) -> None:
"""Zoom to a layer or data source.
Args:
target: Name of the layer or data source to zoom to.
"""
self.call_js_method("zoomTo", target=target)
def set_camera(
self,
longitude: float = 0,
latitude: float = 0,
height: float = 10000000,
heading: float = 0,
pitch: float = -90,
roll: float = 0,
) -> None:
"""Set the camera position immediately (no animation).
Args:
longitude: Camera longitude.
latitude: Camera latitude.
height: Camera height in meters.
heading: Camera heading in degrees.
pitch: Camera pitch in degrees.
roll: Camera roll in degrees.
"""
self.call_js_method(
"setCamera",
longitude=longitude,
latitude=latitude,
height=height,
heading=heading,
pitch=pitch,
roll=roll,
)
def reset_view(self, duration: float = 2) -> None:
"""Reset camera to home position.
Args:
duration: Animation duration in seconds.
"""
self.call_js_method("resetView", duration=duration)
# -------------------------------------------------------------------------
# Layer Management
# -------------------------------------------------------------------------
def set_visibility(self, name: str, visible: bool) -> None:
"""Set layer visibility.
Args:
name: Layer name.
visible: Whether layer should be visible.
"""
self.call_js_method("setVisibility", name, visible)
def set_opacity(self, name: str, opacity: float) -> None:
"""Set layer opacity (imagery layers only).
Args:
name: Layer name.
opacity: Opacity value (0-1).
"""
self.call_js_method("setOpacity", name, opacity)
# -------------------------------------------------------------------------
# HTML Export
# -------------------------------------------------------------------------
def _generate_html_template(self) -> str:
"""Generate standalone HTML for the globe."""
template_path = Path(__file__).parent / "templates" / "cesium.html"
if template_path.exists():
template = template_path.read_text(encoding="utf-8")
else:
template = self._get_default_template()
state = {
"center": self.center,
"zoom": self.zoom,
"access_token": self.access_token,
"terrain_enabled": self.terrain_enabled,
"width": self.width,
"height": self.height,
"layers": self._layers,
"js_calls": self._js_calls,
}
template = template.replace("{{state}}", json.dumps(state, indent=2))
template = template.replace("{{access_token}}", self.access_token)
return template
def _get_default_template(self) -> str:
"""Get default HTML template."""
return """<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Cesium Globe</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<script src="https://cesium.com/downloads/cesiumjs/releases/1.120/Build/Cesium/Cesium.js"></script>
<link href="https://cesium.com/downloads/cesiumjs/releases/1.120/Build/Cesium/Widgets/widgets.css" rel="stylesheet">
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
html, body { height: 100%; }
#cesiumContainer { position: absolute; top: 0; bottom: 0; width: 100%; }
</style>
</head>
<body>
<div id="cesiumContainer"></div>
<script>
const state = {{state}};
if (state.access_token) {
Cesium.Ion.defaultAccessToken = state.access_token;
}
const viewer = new Cesium.Viewer('cesiumContainer', {
baseLayerPicker: false,
geocoder: false,
homeButton: false,
sceneModePicker: false,
navigationHelpButton: false,
animation: false,
timeline: false
});
for (const call of state.js_calls || []) {
executeMethod(call.method, call.args, call.kwargs);
}
function executeMethod(method, args, kwargs) {
console.log('Executing:', method, args, kwargs);
}
</script>
</body>
</html>"""
__init__(self, center=(0.0, 0.0), zoom=2.0, width='100%', height='600px', access_token=None, terrain=False, **kwargs)
special
¶
Initialize a Cesium 3D globe.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
center |
Tuple[float, float] |
Globe center as (longitude, latitude). |
(0.0, 0.0) |
zoom |
float |
Initial zoom level (converted to camera height). |
2.0 |
width |
str |
Widget width as CSS string. |
'100%' |
height |
str |
Widget height as CSS string. |
'600px' |
access_token |
Optional[str] |
Cesium Ion access token (uses CESIUM_TOKEN env var if not provided). |
None |
terrain |
bool |
Whether to enable terrain on initialization. |
False |
**kwargs |
Additional widget arguments. |
{} |
Source code in anymap_ts/cesium.py
def __init__(
self,
center: Tuple[float, float] = (0.0, 0.0),
zoom: float = 2.0,
width: str = "100%",
height: str = "600px",
access_token: Optional[str] = None,
terrain: bool = False,
**kwargs,
):
"""Initialize a Cesium 3D globe.
Args:
center: Globe center as (longitude, latitude).
zoom: Initial zoom level (converted to camera height).
width: Widget width as CSS string.
height: Widget height as CSS string.
access_token: Cesium Ion access token (uses CESIUM_TOKEN env var if not provided).
terrain: Whether to enable terrain on initialization.
**kwargs: Additional widget arguments.
"""
# Get access token from env if not provided
if access_token is None:
access_token = get_cesium_token()
super().__init__(
center=list(center),
zoom=zoom,
width=width,
height=height,
access_token=access_token,
terrain_enabled=terrain,
**kwargs,
)
# Enable terrain if requested
if terrain:
self.set_terrain()
add_3d_tileset(self, url, name=None, maximum_screen_space_error=16, fly_to=True, **kwargs)
¶
Add a 3D Tileset.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
url |
Union[str, int] |
URL to tileset.json or Cesium Ion asset ID. |
required |
name |
Optional[str] |
Tileset name. |
None |
maximum_screen_space_error |
float |
Maximum screen space error for LOD. |
16 |
fly_to |
bool |
Whether to fly to the tileset after loading. |
True |
**kwargs |
Additional options. |
{} |
Source code in anymap_ts/cesium.py
def add_3d_tileset(
self,
url: Union[str, int],
name: Optional[str] = None,
maximum_screen_space_error: float = 16,
fly_to: bool = True,
**kwargs,
) -> None:
"""Add a 3D Tileset.
Args:
url: URL to tileset.json or Cesium Ion asset ID.
name: Tileset name.
maximum_screen_space_error: Maximum screen space error for LOD.
fly_to: Whether to fly to the tileset after loading.
**kwargs: Additional options.
"""
layer_id = name or f"tileset-{len(self._layers)}"
self.call_js_method(
"add3DTileset",
url=str(url),
name=layer_id,
maximumScreenSpaceError=maximum_screen_space_error,
flyTo=fly_to,
**kwargs,
)
self._layers = {
**self._layers,
layer_id: {"id": layer_id, "type": "3dtiles"},
}
add_basemap(self, basemap='OpenStreetMap', **kwargs)
¶
Add a basemap imagery layer.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
basemap |
str |
Name of basemap (e.g., "OpenStreetMap", "Bing"). |
'OpenStreetMap' |
**kwargs |
Additional options. |
{} |
Source code in anymap_ts/cesium.py
def add_basemap(
self,
basemap: str = "OpenStreetMap",
**kwargs,
) -> None:
"""Add a basemap imagery layer.
Args:
basemap: Name of basemap (e.g., "OpenStreetMap", "Bing").
**kwargs: Additional options.
"""
# Common basemap URLs
basemap_urls = {
"OpenStreetMap": "https://tile.openstreetmap.org/{z}/{x}/{y}.png",
"CartoDB.Positron": "https://a.basemaps.cartocdn.com/light_all/{z}/{x}/{y}.png",
"CartoDB.DarkMatter": "https://a.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}.png",
"Stamen.Terrain": "https://tiles.stadiamaps.com/tiles/stamen_terrain/{z}/{x}/{y}.png",
}
url = basemap_urls.get(basemap, basemap_urls["OpenStreetMap"])
self.call_js_method("addBasemap", url, name=basemap, **kwargs)
add_geojson(self, data, name=None, stroke='#3388ff', stroke_width=2, fill='rgba(51, 136, 255, 0.5)', clamp_to_ground=True, fly_to=True, **kwargs)
¶
Add GeoJSON data.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data |
Any |
GeoJSON dict or file path. |
required |
name |
Optional[str] |
Data source name. |
None |
stroke |
str |
Stroke color. |
'#3388ff' |
stroke_width |
float |
Stroke width. |
2 |
fill |
str |
Fill color. |
'rgba(51, 136, 255, 0.5)' |
clamp_to_ground |
bool |
Whether to clamp features to terrain. |
True |
fly_to |
bool |
Whether to fly to the data after loading. |
True |
**kwargs |
Additional options. |
{} |
Source code in anymap_ts/cesium.py
def add_geojson(
self,
data: Any,
name: Optional[str] = None,
stroke: str = "#3388ff",
stroke_width: float = 2,
fill: str = "rgba(51, 136, 255, 0.5)",
clamp_to_ground: bool = True,
fly_to: bool = True,
**kwargs,
) -> None:
"""Add GeoJSON data.
Args:
data: GeoJSON dict or file path.
name: Data source name.
stroke: Stroke color.
stroke_width: Stroke width.
fill: Fill color.
clamp_to_ground: Whether to clamp features to terrain.
fly_to: Whether to fly to the data after loading.
**kwargs: Additional options.
"""
geojson = to_geojson(data)
layer_id = name or f"geojson-{len(self._layers)}"
self.call_js_method(
"addGeoJSON",
data=geojson,
name=layer_id,
stroke=stroke,
strokeWidth=stroke_width,
fill=fill,
clampToGround=clamp_to_ground,
flyTo=fly_to,
**kwargs,
)
self._layers = {
**self._layers,
layer_id: {"id": layer_id, "type": "geojson"},
}
add_imagery_layer(self, url, name=None, layer_type='xyz', alpha=1.0, **kwargs)
¶
Add an imagery layer.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
url |
str |
Imagery URL or service endpoint. |
required |
name |
Optional[str] |
Layer name. |
None |
layer_type |
str |
Type of imagery ('xyz', 'wms', 'wmts', 'arcgis'). |
'xyz' |
alpha |
float |
Layer opacity (0-1). |
1.0 |
**kwargs |
Additional options (layers, parameters for WMS, etc.). |
{} |
Source code in anymap_ts/cesium.py
def add_imagery_layer(
self,
url: str,
name: Optional[str] = None,
layer_type: str = "xyz",
alpha: float = 1.0,
**kwargs,
) -> None:
"""Add an imagery layer.
Args:
url: Imagery URL or service endpoint.
name: Layer name.
layer_type: Type of imagery ('xyz', 'wms', 'wmts', 'arcgis').
alpha: Layer opacity (0-1).
**kwargs: Additional options (layers, parameters for WMS, etc.).
"""
layer_id = name or f"imagery-{len(self._layers)}"
self.call_js_method(
"addImageryLayer",
url=url,
name=layer_id,
type=layer_type,
alpha=alpha,
**kwargs,
)
self._layers = {
**self._layers,
layer_id: {"id": layer_id, "type": "imagery"},
}
fly_to(self, lng, lat, height=None, zoom=None, heading=0, pitch=-90, roll=0, duration=2)
¶
Fly to a location.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
lng |
float |
Target longitude. |
required |
lat |
float |
Target latitude. |
required |
height |
Optional[float] |
Camera height in meters (overrides zoom). |
None |
zoom |
Optional[float] |
Zoom level (converted to height if height not provided). |
None |
heading |
float |
Camera heading in degrees. |
0 |
pitch |
float |
Camera pitch in degrees (default -90 = looking down). |
-90 |
roll |
float |
Camera roll in degrees. |
0 |
duration |
float |
Flight duration in seconds. |
2 |
Source code in anymap_ts/cesium.py
def fly_to(
self,
lng: float,
lat: float,
height: Optional[float] = None,
zoom: Optional[float] = None,
heading: float = 0,
pitch: float = -90,
roll: float = 0,
duration: float = 2,
) -> None:
"""Fly to a location.
Args:
lng: Target longitude.
lat: Target latitude.
height: Camera height in meters (overrides zoom).
zoom: Zoom level (converted to height if height not provided).
heading: Camera heading in degrees.
pitch: Camera pitch in degrees (default -90 = looking down).
roll: Camera roll in degrees.
duration: Flight duration in seconds.
"""
self.call_js_method(
"flyTo",
lng,
lat,
height=height,
zoom=zoom,
heading=heading,
pitch=pitch,
roll=roll,
duration=duration,
)
remove_3d_tileset(self, name)
¶
Remove a 3D Tileset.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
name |
str |
Tileset name to remove. |
required |
Source code in anymap_ts/cesium.py
def remove_3d_tileset(self, name: str) -> None:
"""Remove a 3D Tileset.
Args:
name: Tileset name to remove.
"""
if name in self._layers:
layers = dict(self._layers)
del layers[name]
self._layers = layers
self.call_js_method("remove3DTileset", name)
remove_data_source(self, name)
¶
Remove a data source (GeoJSON, etc.).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
name |
str |
Data source name to remove. |
required |
Source code in anymap_ts/cesium.py
def remove_data_source(self, name: str) -> None:
"""Remove a data source (GeoJSON, etc.).
Args:
name: Data source name to remove.
"""
if name in self._layers:
layers = dict(self._layers)
del layers[name]
self._layers = layers
self.call_js_method("removeDataSource", name)
remove_imagery_layer(self, name)
¶
Remove an imagery layer.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
name |
str |
Layer name to remove. |
required |
Source code in anymap_ts/cesium.py
def remove_imagery_layer(self, name: str) -> None:
"""Remove an imagery layer.
Args:
name: Layer name to remove.
"""
if name in self._layers:
layers = dict(self._layers)
del layers[name]
self._layers = layers
self.call_js_method("removeImageryLayer", name)
remove_terrain(self)
¶
Disable terrain and use ellipsoid.
Source code in anymap_ts/cesium.py
def remove_terrain(self) -> None:
"""Disable terrain and use ellipsoid."""
self.terrain_enabled = False
self.call_js_method("removeTerrain")
reset_view(self, duration=2)
¶
Reset camera to home position.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
duration |
float |
Animation duration in seconds. |
2 |
Source code in anymap_ts/cesium.py
def reset_view(self, duration: float = 2) -> None:
"""Reset camera to home position.
Args:
duration: Animation duration in seconds.
"""
self.call_js_method("resetView", duration=duration)
set_camera(self, longitude=0, latitude=0, height=10000000, heading=0, pitch=-90, roll=0)
¶
Set the camera position immediately (no animation).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
longitude |
float |
Camera longitude. |
0 |
latitude |
float |
Camera latitude. |
0 |
height |
float |
Camera height in meters. |
10000000 |
heading |
float |
Camera heading in degrees. |
0 |
pitch |
float |
Camera pitch in degrees. |
-90 |
roll |
float |
Camera roll in degrees. |
0 |
Source code in anymap_ts/cesium.py
def set_camera(
self,
longitude: float = 0,
latitude: float = 0,
height: float = 10000000,
heading: float = 0,
pitch: float = -90,
roll: float = 0,
) -> None:
"""Set the camera position immediately (no animation).
Args:
longitude: Camera longitude.
latitude: Camera latitude.
height: Camera height in meters.
heading: Camera heading in degrees.
pitch: Camera pitch in degrees.
roll: Camera roll in degrees.
"""
self.call_js_method(
"setCamera",
longitude=longitude,
latitude=latitude,
height=height,
heading=heading,
pitch=pitch,
roll=roll,
)
set_opacity(self, name, opacity)
¶
Set layer opacity (imagery layers only).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
name |
str |
Layer name. |
required |
opacity |
float |
Opacity value (0-1). |
required |
Source code in anymap_ts/cesium.py
def set_opacity(self, name: str, opacity: float) -> None:
"""Set layer opacity (imagery layers only).
Args:
name: Layer name.
opacity: Opacity value (0-1).
"""
self.call_js_method("setOpacity", name, opacity)
set_terrain(self, url=None, request_vertex_normals=True, request_water_mask=True)
¶
Enable terrain.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
url |
Optional[str] |
Terrain provider URL. If None, uses Cesium World Terrain (requires Ion token). |
None |
request_vertex_normals |
bool |
Request vertex normals for lighting. |
True |
request_water_mask |
bool |
Request water mask for water effects. |
True |
Source code in anymap_ts/cesium.py
def set_terrain(
self,
url: Optional[str] = None,
request_vertex_normals: bool = True,
request_water_mask: bool = True,
) -> None:
"""Enable terrain.
Args:
url: Terrain provider URL. If None, uses Cesium World Terrain (requires Ion token).
request_vertex_normals: Request vertex normals for lighting.
request_water_mask: Request water mask for water effects.
"""
self.terrain_enabled = True
self.call_js_method(
"setTerrain",
url=url or "cesium-world-terrain",
requestVertexNormals=request_vertex_normals,
requestWaterMask=request_water_mask,
)
set_visibility(self, name, visible)
¶
Set layer visibility.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
name |
str |
Layer name. |
required |
visible |
bool |
Whether layer should be visible. |
required |
Source code in anymap_ts/cesium.py
def set_visibility(self, name: str, visible: bool) -> None:
"""Set layer visibility.
Args:
name: Layer name.
visible: Whether layer should be visible.
"""
self.call_js_method("setVisibility", name, visible)
zoom_to(self, target)
¶
Zoom to a layer or data source.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
target |
str |
Name of the layer or data source to zoom to. |
required |
Source code in anymap_ts/cesium.py
def zoom_to(self, target: str) -> None:
"""Zoom to a layer or data source.
Args:
target: Name of the layer or data source to zoom to.
"""
self.call_js_method("zoomTo", target=target)
get_cesium_token()
¶
Get Cesium Ion access token from environment variable.
Returns:
| Type | Description |
|---|---|
str |
Cesium Ion access token or empty string if not set. |
Source code in anymap_ts/cesium.py
def get_cesium_token() -> str:
"""Get Cesium Ion access token from environment variable.
Returns:
Cesium Ion access token or empty string if not set.
"""
return os.environ.get("CESIUM_TOKEN", "")
deckgl
¶
DeckGL map widget implementation extending MapLibre with deck.gl layers.
DeckGLMap (MapLibreMap)
¶
Interactive map widget using MapLibre GL JS with deck.gl overlay.
This class extends MapLibreMap with deck.gl visualization layer support for GPU-accelerated geospatial visualizations.
Examples:
>>> from anymap_ts import DeckGLMap
>>> m = DeckGLMap(center=[-122.4, 37.8], zoom=10)
>>> m.add_scatterplot_layer(
... data=points,
... get_position='coordinates',
... get_radius=100,
... get_fill_color=[255, 0, 0]
... )
>>> m
Source code in anymap_ts/deckgl.py
class DeckGLMap(MapLibreMap):
"""Interactive map widget using MapLibre GL JS with deck.gl overlay.
This class extends MapLibreMap with deck.gl visualization layer support
for GPU-accelerated geospatial visualizations.
Example:
>>> from anymap_ts import DeckGLMap
>>> m = DeckGLMap(center=[-122.4, 37.8], zoom=10)
>>> m.add_scatterplot_layer(
... data=points,
... get_position='coordinates',
... get_radius=100,
... get_fill_color=[255, 0, 0]
... )
>>> m
"""
# ESM module for frontend (uses DeckGL-enabled version)
_esm = STATIC_DIR / "deckgl.js"
# DeckGL layer tracking
_deck_layers = traitlets.Dict({}).tag(sync=True)
def __init__(
self,
center: Tuple[float, float] = (0.0, 0.0),
zoom: float = 2.0,
width: str = "100%",
height: str = "600px",
style: Union[str, Dict] = "https://demotiles.maplibre.org/style.json",
bearing: float = 0.0,
pitch: float = 0.0,
controls: Optional[Dict[str, Any]] = None,
**kwargs,
):
"""Initialize a DeckGL map.
Args:
center: Map center as (longitude, latitude).
zoom: Initial zoom level.
width: Map width as CSS string.
height: Map height as CSS string.
style: MapLibre style URL or style object.
bearing: Map bearing in degrees.
pitch: Map pitch in degrees.
controls: Dict of controls to add (e.g., {"navigation": True}).
**kwargs: Additional widget arguments.
"""
super().__init__(
center=center,
zoom=zoom,
width=width,
height=height,
style=style,
bearing=bearing,
pitch=pitch,
controls=controls,
**kwargs,
)
self._deck_layers = {}
# -------------------------------------------------------------------------
# DeckGL Scatterplot Layer
# -------------------------------------------------------------------------
def add_scatterplot_layer(
self,
data: Any,
name: Optional[str] = None,
get_position: Union[str, Callable] = "coordinates",
get_radius: Union[float, str, Callable] = 5,
get_fill_color: Union[List[int], str, Callable] = None,
get_line_color: Union[List[int], str, Callable] = None,
radius_scale: float = 1,
radius_min_pixels: float = 1,
radius_max_pixels: float = 100,
line_width_min_pixels: float = 1,
stroked: bool = True,
filled: bool = True,
pickable: bool = True,
opacity: float = 0.8,
**kwargs,
) -> None:
"""Add a scatterplot layer for point visualization.
Args:
data: Array of data objects or GeoJSON.
name: Layer ID.
get_position: Accessor for point position [lng, lat].
get_radius: Accessor for point radius.
get_fill_color: Accessor for fill color [r, g, b, a].
get_line_color: Accessor for stroke color [r, g, b, a].
radius_scale: Global radius multiplier.
radius_min_pixels: Minimum radius in pixels.
radius_max_pixels: Maximum radius in pixels.
line_width_min_pixels: Minimum stroke width.
stroked: Whether to draw stroke.
filled: Whether to fill points.
pickable: Whether layer responds to hover/click.
opacity: Layer opacity.
**kwargs: Additional layer props.
"""
layer_id = name or f"scatterplot-{len(self._deck_layers)}"
processed_data = self._process_deck_data(data)
self.call_js_method(
"addScatterplotLayer",
id=layer_id,
data=processed_data,
getPosition=get_position,
getRadius=get_radius,
getFillColor=get_fill_color or [51, 136, 255, 200],
getLineColor=get_line_color or [255, 255, 255, 255],
radiusScale=radius_scale,
radiusMinPixels=radius_min_pixels,
radiusMaxPixels=radius_max_pixels,
lineWidthMinPixels=line_width_min_pixels,
stroked=stroked,
filled=filled,
pickable=pickable,
opacity=opacity,
**kwargs,
)
self._deck_layers = {
**self._deck_layers,
layer_id: {"type": "ScatterplotLayer", "id": layer_id},
}
# -------------------------------------------------------------------------
# DeckGL Arc Layer
# -------------------------------------------------------------------------
def add_arc_layer(
self,
data: Any,
name: Optional[str] = None,
get_source_position: Union[str, Callable] = "source",
get_target_position: Union[str, Callable] = "target",
get_source_color: Union[List[int], str, Callable] = None,
get_target_color: Union[List[int], str, Callable] = None,
get_width: Union[float, str, Callable] = 1,
pickable: bool = True,
opacity: float = 0.8,
**kwargs,
) -> None:
"""Add an arc layer for origin-destination visualization.
Args:
data: Array of data objects with source/target coordinates.
name: Layer ID.
get_source_position: Accessor for source position [lng, lat].
get_target_position: Accessor for target position [lng, lat].
get_source_color: Accessor for source color [r, g, b, a].
get_target_color: Accessor for target color [r, g, b, a].
get_width: Accessor for arc width.
pickable: Whether layer responds to hover/click.
opacity: Layer opacity.
**kwargs: Additional layer props.
"""
layer_id = name or f"arc-{len(self._deck_layers)}"
processed_data = self._process_deck_data(data)
self.call_js_method(
"addArcLayer",
id=layer_id,
data=processed_data,
getSourcePosition=get_source_position,
getTargetPosition=get_target_position,
getSourceColor=get_source_color or [51, 136, 255, 255],
getTargetColor=get_target_color or [255, 136, 51, 255],
getWidth=get_width,
pickable=pickable,
opacity=opacity,
**kwargs,
)
self._deck_layers = {
**self._deck_layers,
layer_id: {"type": "ArcLayer", "id": layer_id},
}
# -------------------------------------------------------------------------
# DeckGL Point Cloud Layer
# -------------------------------------------------------------------------
def add_point_cloud_layer(
self,
data: Any,
name: Optional[str] = None,
get_position: Union[str, Callable] = "position",
get_color: Union[List[int], str, Callable] = None,
get_normal: Union[str, Callable] = None,
point_size: float = 2,
size_units: str = "pixels",
coordinate_system: Optional[str] = None,
coordinate_origin: Optional[List[float]] = None,
pickable: bool = True,
opacity: float = 1.0,
**kwargs,
) -> None:
"""Add a point cloud layer for 3D point visualization.
Renders large point cloud datasets typically from LiDAR or 3D scanning.
Supports both 2D and 3D coordinates with optional normal vectors for
lighting effects.
Args:
data: Array of point data with position [x, y, z] coordinates.
name: Layer ID. If None, auto-generated.
get_position: Accessor for point position [x, y, z].
get_color: Accessor for point color [r, g, b, a].
get_normal: Accessor for point normal [nx, ny, nz] for lighting.
point_size: Point size in size_units.
size_units: Units for point_size ('pixels' or 'meters').
coordinate_system: Coordinate system ('CARTESIAN', 'METER_OFFSETS',
'LNGLAT', 'LNGLAT_OFFSETS').
coordinate_origin: Origin for offset coordinate systems [lng, lat, z].
pickable: Whether layer responds to hover/click.
opacity: Layer opacity (0-1).
**kwargs: Additional PointCloudLayer props.
Example:
>>> m = DeckGLMap()
>>> points = [
... {"position": [-122.4, 37.8, 100], "color": [255, 0, 0]},
... {"position": [-122.5, 37.7, 200], "color": [0, 255, 0]},
... ]
>>> m.add_point_cloud_layer(
... data=points,
... point_size=5,
... get_color="color"
... )
"""
layer_id = name or f"pointcloud-{len(self._deck_layers)}"
processed_data = self._process_deck_data(data)
layer_kwargs = {
"id": layer_id,
"data": processed_data,
"getPosition": get_position,
"getColor": get_color or [255, 255, 255, 255],
"pointSize": point_size,
"sizeUnits": size_units,
"pickable": pickable,
"opacity": opacity,
}
if get_normal is not None:
layer_kwargs["getNormal"] = get_normal
if coordinate_system is not None:
layer_kwargs["coordinateSystem"] = coordinate_system
if coordinate_origin is not None:
layer_kwargs["coordinateOrigin"] = coordinate_origin
layer_kwargs.update(kwargs)
self.call_js_method("addPointCloudLayer", **layer_kwargs)
self._deck_layers = {
**self._deck_layers,
layer_id: {"type": "PointCloudLayer", "id": layer_id},
}
# -------------------------------------------------------------------------
# DeckGL Path Layer
# -------------------------------------------------------------------------
def add_path_layer(
self,
data: Any,
name: Optional[str] = None,
get_path: Union[str, Callable] = "path",
get_color: Union[List[int], str, Callable] = None,
get_width: Union[float, str, Callable] = 1,
width_scale: float = 1,
width_min_pixels: float = 1,
pickable: bool = True,
opacity: float = 0.8,
**kwargs,
) -> None:
"""Add a path layer for polyline visualization.
Args:
data: Array of data objects with path coordinates.
name: Layer ID.
get_path: Accessor for path coordinates [[lng, lat], ...].
get_color: Accessor for path color [r, g, b, a].
get_width: Accessor for path width.
width_scale: Global width multiplier.
width_min_pixels: Minimum width in pixels.
pickable: Whether layer responds to hover/click.
opacity: Layer opacity.
**kwargs: Additional layer props.
"""
layer_id = name or f"path-{len(self._deck_layers)}"
processed_data = self._process_deck_data(data)
self.call_js_method(
"addPathLayer",
id=layer_id,
data=processed_data,
getPath=get_path,
getColor=get_color or [51, 136, 255, 200],
getWidth=get_width,
widthScale=width_scale,
widthMinPixels=width_min_pixels,
pickable=pickable,
opacity=opacity,
**kwargs,
)
self._deck_layers = {
**self._deck_layers,
layer_id: {"type": "PathLayer", "id": layer_id},
}
# -------------------------------------------------------------------------
# DeckGL Polygon Layer
# -------------------------------------------------------------------------
def add_polygon_layer(
self,
data: Any,
name: Optional[str] = None,
get_polygon: Union[str, Callable] = "polygon",
get_fill_color: Union[List[int], str, Callable] = None,
get_line_color: Union[List[int], str, Callable] = None,
get_line_width: Union[float, str, Callable] = 1,
get_elevation: Union[float, str, Callable] = 0,
extruded: bool = False,
wireframe: bool = False,
filled: bool = True,
stroked: bool = True,
line_width_min_pixels: float = 1,
pickable: bool = True,
opacity: float = 0.5,
**kwargs,
) -> None:
"""Add a polygon layer for filled polygon visualization.
Args:
data: Array of data objects with polygon coordinates.
name: Layer ID.
get_polygon: Accessor for polygon coordinates.
get_fill_color: Accessor for fill color [r, g, b, a].
get_line_color: Accessor for stroke color [r, g, b, a].
get_line_width: Accessor for stroke width.
get_elevation: Accessor for 3D extrusion height.
extruded: Whether to render as 3D polygons.
wireframe: Whether to render wireframe (extruded only).
filled: Whether to fill polygons.
stroked: Whether to draw stroke.
line_width_min_pixels: Minimum stroke width.
pickable: Whether layer responds to hover/click.
opacity: Layer opacity.
**kwargs: Additional layer props.
"""
layer_id = name or f"polygon-{len(self._deck_layers)}"
processed_data = self._process_deck_data(data)
self.call_js_method(
"addPolygonLayer",
id=layer_id,
data=processed_data,
getPolygon=get_polygon,
getFillColor=get_fill_color or [51, 136, 255, 128],
getLineColor=get_line_color or [0, 0, 255, 255],
getLineWidth=get_line_width,
getElevation=get_elevation,
extruded=extruded,
wireframe=wireframe,
filled=filled,
stroked=stroked,
lineWidthMinPixels=line_width_min_pixels,
pickable=pickable,
opacity=opacity,
**kwargs,
)
self._deck_layers = {
**self._deck_layers,
layer_id: {"type": "PolygonLayer", "id": layer_id},
}
# -------------------------------------------------------------------------
# DeckGL Hexagon Layer
# -------------------------------------------------------------------------
def add_hexagon_layer(
self,
data: Any,
name: Optional[str] = None,
get_position: Union[str, Callable] = "coordinates",
radius: float = 1000,
elevation_scale: float = 4,
extruded: bool = True,
color_range: Optional[List[List[int]]] = None,
pickable: bool = True,
opacity: float = 0.8,
**kwargs,
) -> None:
"""Add a hexagon layer for hexbin aggregation visualization.
Args:
data: Array of data objects with position coordinates.
name: Layer ID.
get_position: Accessor for point position [lng, lat].
radius: Hexagon radius in meters.
elevation_scale: Elevation multiplier for 3D.
extruded: Whether to render as 3D hexagons.
color_range: Color gradient for aggregation [[r, g, b], ...].
pickable: Whether layer responds to hover/click.
opacity: Layer opacity.
**kwargs: Additional layer props.
"""
layer_id = name or f"hexagon-{len(self._deck_layers)}"
processed_data = self._process_deck_data(data)
default_color_range = [
[1, 152, 189],
[73, 227, 206],
[216, 254, 181],
[254, 237, 177],
[254, 173, 84],
[209, 55, 78],
]
self.call_js_method(
"addHexagonLayer",
id=layer_id,
data=processed_data,
getPosition=get_position,
radius=radius,
elevationScale=elevation_scale,
extruded=extruded,
colorRange=color_range or default_color_range,
pickable=pickable,
opacity=opacity,
**kwargs,
)
self._deck_layers = {
**self._deck_layers,
layer_id: {"type": "HexagonLayer", "id": layer_id},
}
# -------------------------------------------------------------------------
# DeckGL Heatmap Layer
# -------------------------------------------------------------------------
def add_heatmap_layer(
self,
data: Any,
name: Optional[str] = None,
get_position: Union[str, Callable] = "coordinates",
get_weight: Union[float, str, Callable] = 1,
radius_pixels: float = 30,
intensity: float = 1,
threshold: float = 0.05,
color_range: Optional[List[List[int]]] = None,
opacity: float = 1,
**kwargs,
) -> None:
"""Add a heatmap layer for density visualization.
Args:
data: Array of data objects with position coordinates.
name: Layer ID.
get_position: Accessor for point position [lng, lat].
get_weight: Accessor for point weight.
radius_pixels: Influence radius in pixels.
intensity: Intensity multiplier.
threshold: Minimum density threshold.
color_range: Color gradient [[r, g, b, a], ...].
opacity: Layer opacity.
**kwargs: Additional layer props.
"""
layer_id = name or f"heatmap-{len(self._deck_layers)}"
processed_data = self._process_deck_data(data)
default_color_range = [
[255, 255, 178, 25],
[254, 217, 118, 85],
[254, 178, 76, 127],
[253, 141, 60, 170],
[240, 59, 32, 212],
[189, 0, 38, 255],
]
self.call_js_method(
"addHeatmapLayer",
id=layer_id,
data=processed_data,
getPosition=get_position,
getWeight=get_weight,
radiusPixels=radius_pixels,
intensity=intensity,
threshold=threshold,
colorRange=color_range or default_color_range,
opacity=opacity,
**kwargs,
)
self._deck_layers = {
**self._deck_layers,
layer_id: {"type": "HeatmapLayer", "id": layer_id},
}
# -------------------------------------------------------------------------
# DeckGL Grid Layer
# -------------------------------------------------------------------------
def add_grid_layer(
self,
data: Any,
name: Optional[str] = None,
get_position: Union[str, Callable] = "coordinates",
cell_size: float = 200,
elevation_scale: float = 4,
extruded: bool = True,
color_range: Optional[List[List[int]]] = None,
pickable: bool = True,
opacity: float = 0.8,
**kwargs,
) -> None:
"""Add a grid layer for square grid aggregation visualization.
Args:
data: Array of data objects with position coordinates.
name: Layer ID.
get_position: Accessor for point position [lng, lat].
cell_size: Grid cell size in meters.
elevation_scale: Elevation multiplier for 3D.
extruded: Whether to render as 3D cells.
color_range: Color gradient [[r, g, b], ...].
pickable: Whether layer responds to hover/click.
opacity: Layer opacity.
**kwargs: Additional layer props.
"""
layer_id = name or f"grid-{len(self._deck_layers)}"
processed_data = self._process_deck_data(data)
default_color_range = [
[1, 152, 189],
[73, 227, 206],
[216, 254, 181],
[254, 237, 177],
[254, 173, 84],
[209, 55, 78],
]
self.call_js_method(
"addGridLayer",
id=layer_id,
data=processed_data,
getPosition=get_position,
cellSize=cell_size,
elevationScale=elevation_scale,
extruded=extruded,
colorRange=color_range or default_color_range,
pickable=pickable,
opacity=opacity,
**kwargs,
)
self._deck_layers = {
**self._deck_layers,
layer_id: {"type": "GridLayer", "id": layer_id},
}
# -------------------------------------------------------------------------
# DeckGL Icon Layer
# -------------------------------------------------------------------------
def add_icon_layer(
self,
data: Any,
name: Optional[str] = None,
get_position: Union[str, Callable] = "coordinates",
get_icon: Union[str, Callable] = "icon",
get_size: Union[float, str, Callable] = 20,
get_color: Union[List[int], str, Callable] = None,
icon_atlas: Optional[str] = None,
icon_mapping: Optional[Dict] = None,
pickable: bool = True,
opacity: float = 1,
**kwargs,
) -> None:
"""Add an icon layer for custom marker visualization.
Args:
data: Array of data objects with position coordinates.
name: Layer ID.
get_position: Accessor for icon position [lng, lat].
get_icon: Accessor for icon name in icon_mapping.
get_size: Accessor for icon size.
get_color: Accessor for icon tint color [r, g, b, a].
icon_atlas: URL to icon atlas image.
icon_mapping: Dict mapping icon names to atlas coordinates.
pickable: Whether layer responds to hover/click.
opacity: Layer opacity.
**kwargs: Additional layer props.
"""
layer_id = name or f"icon-{len(self._deck_layers)}"
processed_data = self._process_deck_data(data)
self.call_js_method(
"addIconLayer",
id=layer_id,
data=processed_data,
getPosition=get_position,
getIcon=get_icon,
getSize=get_size,
getColor=get_color or [255, 255, 255, 255],
iconAtlas=icon_atlas,
iconMapping=icon_mapping,
pickable=pickable,
opacity=opacity,
**kwargs,
)
self._deck_layers = {
**self._deck_layers,
layer_id: {"type": "IconLayer", "id": layer_id},
}
# -------------------------------------------------------------------------
# DeckGL Text Layer
# -------------------------------------------------------------------------
def add_text_layer(
self,
data: Any,
name: Optional[str] = None,
get_position: Union[str, Callable] = "coordinates",
get_text: Union[str, Callable] = "text",
get_size: Union[float, str, Callable] = 12,
get_color: Union[List[int], str, Callable] = None,
get_angle: Union[float, str, Callable] = 0,
text_anchor: str = "middle",
alignment_baseline: str = "center",
pickable: bool = True,
opacity: float = 1,
**kwargs,
) -> None:
"""Add a text layer for label visualization.
Args:
data: Array of data objects with position and text.
name: Layer ID.
get_position: Accessor for text position [lng, lat].
get_text: Accessor for text content.
get_size: Accessor for text size.
get_color: Accessor for text color [r, g, b, a].
get_angle: Accessor for text rotation in degrees.
text_anchor: Horizontal alignment ('start', 'middle', 'end').
alignment_baseline: Vertical alignment ('top', 'center', 'bottom').
pickable: Whether layer responds to hover/click.
opacity: Layer opacity.
**kwargs: Additional layer props.
"""
layer_id = name or f"text-{len(self._deck_layers)}"
processed_data = self._process_deck_data(data)
self.call_js_method(
"addTextLayer",
id=layer_id,
data=processed_data,
getPosition=get_position,
getText=get_text,
getSize=get_size,
getColor=get_color or [0, 0, 0, 255],
getAngle=get_angle,
getTextAnchor=text_anchor,
getAlignmentBaseline=alignment_baseline,
pickable=pickable,
opacity=opacity,
**kwargs,
)
self._deck_layers = {
**self._deck_layers,
layer_id: {"type": "TextLayer", "id": layer_id},
}
# -------------------------------------------------------------------------
# DeckGL GeoJSON Layer
# -------------------------------------------------------------------------
def add_geojson_layer(
self,
data: Any,
name: Optional[str] = None,
get_fill_color: Union[List[int], str, Callable] = None,
get_line_color: Union[List[int], str, Callable] = None,
get_line_width: Union[float, str, Callable] = 1,
get_point_radius: Union[float, str, Callable] = 5,
get_elevation: Union[float, str, Callable] = 0,
extruded: bool = False,
wireframe: bool = False,
filled: bool = True,
stroked: bool = True,
line_width_min_pixels: float = 1,
point_radius_min_pixels: float = 2,
pickable: bool = True,
opacity: float = 0.8,
**kwargs,
) -> None:
"""Add a GeoJSON layer for rendering GeoJSON features.
Args:
data: GeoJSON object or URL.
name: Layer ID.
get_fill_color: Accessor for fill color [r, g, b, a].
get_line_color: Accessor for stroke color [r, g, b, a].
get_line_width: Accessor for stroke width.
get_point_radius: Accessor for point radius.
get_elevation: Accessor for 3D extrusion height.
extruded: Whether to render as 3D features.
wireframe: Whether to render wireframe (extruded only).
filled: Whether to fill features.
stroked: Whether to draw stroke.
line_width_min_pixels: Minimum stroke width.
point_radius_min_pixels: Minimum point radius.
pickable: Whether layer responds to hover/click.
opacity: Layer opacity.
**kwargs: Additional layer props.
"""
layer_id = name or f"geojson-{len(self._deck_layers)}"
processed_data = self._process_deck_data(data)
self.call_js_method(
"addGeoJsonLayer",
id=layer_id,
data=processed_data,
getFillColor=get_fill_color or [51, 136, 255, 128],
getLineColor=get_line_color or [0, 0, 0, 255],
getLineWidth=get_line_width,
getPointRadius=get_point_radius,
getElevation=get_elevation,
extruded=extruded,
wireframe=wireframe,
filled=filled,
stroked=stroked,
lineWidthMinPixels=line_width_min_pixels,
pointRadiusMinPixels=point_radius_min_pixels,
pickable=pickable,
opacity=opacity,
**kwargs,
)
self._deck_layers = {
**self._deck_layers,
layer_id: {"type": "GeoJsonLayer", "id": layer_id},
}
# -------------------------------------------------------------------------
# DeckGL Contour Layer
# -------------------------------------------------------------------------
def add_contour_layer(
self,
data: Any,
name: Optional[str] = None,
get_position: Union[str, Callable] = "coordinates",
get_weight: Union[float, str, Callable] = 1,
cell_size: float = 200,
contours: Optional[List[Dict]] = None,
pickable: bool = True,
opacity: float = 1,
**kwargs,
) -> None:
"""Add a contour layer for isoline visualization.
Args:
data: Array of data objects with position coordinates.
name: Layer ID.
get_position: Accessor for point position [lng, lat].
get_weight: Accessor for point weight.
cell_size: Grid cell size for aggregation.
contours: Contour definitions [{threshold, color, strokeWidth}, ...].
pickable: Whether layer responds to hover/click.
opacity: Layer opacity.
**kwargs: Additional layer props.
"""
layer_id = name or f"contour-{len(self._deck_layers)}"
processed_data = self._process_deck_data(data)
default_contours = [
{"threshold": 1, "color": [255, 255, 255], "strokeWidth": 1},
{"threshold": 5, "color": [51, 136, 255], "strokeWidth": 2},
{"threshold": 10, "color": [0, 0, 255], "strokeWidth": 3},
]
self.call_js_method(
"addContourLayer",
id=layer_id,
data=processed_data,
getPosition=get_position,
getWeight=get_weight,
cellSize=cell_size,
contours=contours or default_contours,
pickable=pickable,
opacity=opacity,
**kwargs,
)
self._deck_layers = {
**self._deck_layers,
layer_id: {"type": "ContourLayer", "id": layer_id},
}
# -------------------------------------------------------------------------
# DeckGL Screen Grid Layer
# -------------------------------------------------------------------------
def add_screen_grid_layer(
self,
data: Any,
name: Optional[str] = None,
get_position: Union[str, Callable] = "coordinates",
get_weight: Union[float, str, Callable] = 1,
cell_size_pixels: float = 50,
color_range: Optional[List[List[int]]] = None,
pickable: bool = True,
opacity: float = 0.8,
**kwargs,
) -> None:
"""Add a screen grid layer for screen-space grid aggregation.
Args:
data: Array of data objects with position coordinates.
name: Layer ID.
get_position: Accessor for point position [lng, lat].
get_weight: Accessor for point weight.
cell_size_pixels: Grid cell size in pixels.
color_range: Color gradient [[r, g, b, a], ...].
pickable: Whether layer responds to hover/click.
opacity: Layer opacity.
**kwargs: Additional layer props.
"""
layer_id = name or f"screengrid-{len(self._deck_layers)}"
processed_data = self._process_deck_data(data)
default_color_range = [
[255, 255, 178, 25],
[254, 217, 118, 85],
[254, 178, 76, 127],
[253, 141, 60, 170],
[240, 59, 32, 212],
[189, 0, 38, 255],
]
self.call_js_method(
"addScreenGridLayer",
id=layer_id,
data=processed_data,
getPosition=get_position,
getWeight=get_weight,
cellSizePixels=cell_size_pixels,
colorRange=color_range or default_color_range,
pickable=pickable,
opacity=opacity,
**kwargs,
)
self._deck_layers = {
**self._deck_layers,
layer_id: {"type": "ScreenGridLayer", "id": layer_id},
}
# -------------------------------------------------------------------------
# Generic DeckGL Layer
# -------------------------------------------------------------------------
def add_deckgl_layer(
self,
layer_type: str,
data: Any,
name: Optional[str] = None,
**kwargs,
) -> None:
"""Add a generic deck.gl layer to the map.
This method provides a flexible way to add any supported deck.gl layer
type using a single interface. For commonly used layers, prefer the
specific methods (e.g., add_scatterplot_layer) for better IDE support.
Args:
layer_type: The deck.gl layer type. Supported types include:
'ScatterplotLayer', 'ArcLayer', 'PathLayer', 'PolygonLayer',
'HexagonLayer', 'HeatmapLayer', 'GridLayer', 'IconLayer',
'TextLayer', 'GeoJsonLayer', 'ContourLayer', 'ScreenGridLayer',
'PointCloudLayer', 'TripsLayer', 'LineLayer'.
data: Array of data objects or GeoJSON.
name: Layer ID. If None, auto-generated from layer_type.
**kwargs: Layer-specific properties passed directly to deck.gl.
Common properties include:
- opacity: Layer opacity (0-1)
- pickable: Whether layer responds to hover/click
- getPosition: Accessor for position coordinates
- getColor/getFillColor/getLineColor: Color accessors
Example:
>>> m = DeckGLMap()
>>> # Add a TripsLayer with animation
>>> m.add_deckgl_layer(
... 'TripsLayer',
... data=trips_data,
... getPath='waypoints',
... getTimestamps='timestamps',
... getColor=[253, 128, 93],
... trailLength=180,
... )
>>> # Add a LineLayer
>>> m.add_deckgl_layer(
... 'LineLayer',
... data=lines_data,
... getSourcePosition='source',
... getTargetPosition='target',
... getColor=[0, 128, 255],
... )
"""
# Normalize layer type and create prefix
layer_type_clean = layer_type.replace("Layer", "")
prefix = layer_type_clean.lower()
layer_id = name or f"{prefix}-{len(self._deck_layers)}"
processed_data = self._process_deck_data(data)
self.call_js_method(
"addDeckGLLayer",
layerType=layer_type,
id=layer_id,
data=processed_data,
**kwargs,
)
self._deck_layers = {
**self._deck_layers,
layer_id: {"type": layer_type, "id": layer_id},
}
# -------------------------------------------------------------------------
# DeckGL Trips Layer
# -------------------------------------------------------------------------
def add_trips_layer(
self,
data: Any,
name: Optional[str] = None,
get_path: Union[str, Callable] = "waypoints",
get_timestamps: Union[str, Callable] = "timestamps",
get_color: Union[List[int], str, Callable] = None,
width_min_pixels: float = 2,
trail_length: float = 180,
current_time: float = 0,
pickable: bool = True,
opacity: float = 0.8,
**kwargs,
) -> None:
"""Add a trips layer for animated path visualization.
The TripsLayer renders animated paths showing movement over time,
ideal for visualizing vehicle routes, migration patterns, or any
time-based trajectory data.
Args:
data: Array of trip objects with waypoints and timestamps.
name: Layer ID. If None, auto-generated.
get_path: Accessor for waypoint coordinates [[lng, lat], ...].
get_timestamps: Accessor for timestamps at each waypoint.
get_color: Accessor for trip color [r, g, b] or [r, g, b, a].
width_min_pixels: Minimum trail width in pixels.
trail_length: Trail length in timestamp units.
current_time: Current animation time.
pickable: Whether layer responds to hover/click.
opacity: Layer opacity (0-1).
**kwargs: Additional TripsLayer props.
Example:
>>> m = DeckGLMap()
>>> trips = [
... {
... "waypoints": [[-122.4, 37.8], [-122.5, 37.7], [-122.6, 37.8]],
... "timestamps": [0, 50, 100]
... }
... ]
>>> m.add_trips_layer(
... data=trips,
... trail_length=180,
... current_time=50,
... )
"""
layer_id = name or f"trips-{len(self._deck_layers)}"
processed_data = self._process_deck_data(data)
self.call_js_method(
"addTripsLayer",
id=layer_id,
data=processed_data,
getPath=get_path,
getTimestamps=get_timestamps,
getColor=get_color or [253, 128, 93],
widthMinPixels=width_min_pixels,
trailLength=trail_length,
currentTime=current_time,
pickable=pickable,
opacity=opacity,
**kwargs,
)
self._deck_layers = {
**self._deck_layers,
layer_id: {"type": "TripsLayer", "id": layer_id},
}
# -------------------------------------------------------------------------
# DeckGL Line Layer
# -------------------------------------------------------------------------
def add_line_layer(
self,
data: Any,
name: Optional[str] = None,
get_source_position: Union[str, Callable] = "sourcePosition",
get_target_position: Union[str, Callable] = "targetPosition",
get_color: Union[List[int], str, Callable] = None,
get_width: Union[float, str, Callable] = 1,
width_min_pixels: float = 1,
pickable: bool = True,
opacity: float = 0.8,
**kwargs,
) -> None:
"""Add a line layer for simple line segment visualization.
The LineLayer renders straight line segments between source and
target positions. Unlike ArcLayer, lines are drawn directly
without curvature.
Args:
data: Array of line objects with source/target positions.
name: Layer ID. If None, auto-generated.
get_source_position: Accessor for source position [lng, lat].
get_target_position: Accessor for target position [lng, lat].
get_color: Accessor for line color [r, g, b] or [r, g, b, a].
get_width: Accessor for line width.
width_min_pixels: Minimum line width in pixels.
pickable: Whether layer responds to hover/click.
opacity: Layer opacity (0-1).
**kwargs: Additional LineLayer props.
Example:
>>> m = DeckGLMap()
>>> lines = [
... {"sourcePosition": [-122.4, 37.8], "targetPosition": [-122.5, 37.7]},
... {"sourcePosition": [-122.5, 37.7], "targetPosition": [-122.6, 37.8]},
... ]
>>> m.add_line_layer(
... data=lines,
... get_color=[0, 128, 255],
... get_width=2,
... )
"""
layer_id = name or f"line-{len(self._deck_layers)}"
processed_data = self._process_deck_data(data)
self.call_js_method(
"addLineLayer",
id=layer_id,
data=processed_data,
getSourcePosition=get_source_position,
getTargetPosition=get_target_position,
getColor=get_color or [51, 136, 255, 200],
getWidth=get_width,
widthMinPixels=width_min_pixels,
pickable=pickable,
opacity=opacity,
**kwargs,
)
self._deck_layers = {
**self._deck_layers,
layer_id: {"type": "LineLayer", "id": layer_id},
}
# -------------------------------------------------------------------------
# DeckGL COG Layer
# -------------------------------------------------------------------------
def add_cog_layer(
self,
url: str,
name: Optional[str] = None,
opacity: float = 1.0,
visible: bool = True,
debug: bool = False,
debug_opacity: float = 0.25,
max_error: float = 0.125,
fit_bounds: bool = True,
before_id: Optional[str] = None,
**kwargs,
) -> None:
"""Add a Cloud Optimized GeoTIFF (COG) layer using @developmentseed/deck.gl-geotiff.
This method renders COG files directly in the browser using GPU-accelerated
deck.gl rendering with automatic reprojection support.
Args:
url: URL to the Cloud Optimized GeoTIFF file.
name: Layer ID. If None, auto-generated.
opacity: Layer opacity (0-1).
visible: Whether layer is visible.
debug: Show reprojection mesh for debugging.
debug_opacity: Opacity of debug mesh (0-1).
max_error: Maximum reprojection error in pixels. Lower values
create denser mesh for better accuracy.
fit_bounds: Whether to fit map to COG bounds after loading.
before_id: ID of layer to insert before.
**kwargs: Additional COGLayer props.
Example:
>>> m = DeckGLMap()
>>> m.add_cog_layer(
... "https://example.com/landcover.tif",
... name="landcover",
... opacity=0.8
... )
"""
# Use a monotonically increasing counter to avoid ID collisions when layers are removed.
counter = getattr(self, "_cog_layer_counter", 0)
layer_id = name or f"cog-{counter}"
self._cog_layer_counter = counter + 1
self.call_js_method(
"addCOGLayer",
id=layer_id,
geotiff=url,
opacity=opacity,
visible=visible,
debug=debug,
debugOpacity=debug_opacity,
maxError=max_error,
fitBounds=fit_bounds,
beforeId=before_id,
**kwargs,
)
self._deck_layers = {
**self._deck_layers,
layer_id: {"type": "COGLayer", "id": layer_id, "url": url},
}
# -------------------------------------------------------------------------
# New DeckGL Layer Types
# -------------------------------------------------------------------------
def add_bitmap_layer(
self,
image: str,
bounds: List[float],
name: Optional[str] = None,
opacity: float = 1.0,
visible: bool = True,
pickable: bool = False,
desaturate: float = 0,
transparent_color: Optional[List[int]] = None,
tint_color: Optional[List[int]] = None,
**kwargs,
) -> None:
"""Add a bitmap layer for raster image overlay.
Args:
image: URL or data URI of the image.
bounds: Bounding box [west, south, east, north].
name: Layer ID.
opacity: Layer opacity (0-1).
visible: Whether layer is visible.
pickable: Whether layer responds to hover/click.
desaturate: Desaturation amount (0-1).
transparent_color: Color to make transparent [r, g, b, a].
tint_color: Color to tint the image [r, g, b].
**kwargs: Additional BitmapLayer props.
"""
layer_id = name or f"bitmap-{len(self._deck_layers)}"
self.call_js_method(
"addBitmapLayer",
id=layer_id,
image=image,
bounds=bounds,
opacity=opacity,
visible=visible,
pickable=pickable,
desaturate=desaturate,
transparentColor=transparent_color or [0, 0, 0, 0],
tintColor=tint_color or [255, 255, 255],
**kwargs,
)
self._deck_layers = {
**self._deck_layers,
layer_id: {"type": "BitmapLayer", "id": layer_id},
}
def add_column_layer(
self,
data: Any,
name: Optional[str] = None,
get_position: Union[str, Callable] = "coordinates",
get_fill_color: Union[List[int], str, Callable] = None,
get_line_color: Union[List[int], str, Callable] = None,
get_elevation: Union[float, str, Callable] = 1000,
radius: float = 1000,
disk_resolution: int = 20,
elevation_scale: float = 1,
coverage: float = 1,
extruded: bool = True,
filled: bool = True,
stroked: bool = False,
wireframe: bool = False,
pickable: bool = True,
opacity: float = 0.8,
**kwargs,
) -> None:
"""Add a column layer for 3D column/bar visualization.
Args:
data: Array of data objects with position coordinates.
name: Layer ID.
get_position: Accessor for column position [lng, lat].
get_fill_color: Accessor for fill color [r, g, b, a].
get_line_color: Accessor for stroke color [r, g, b, a].
get_elevation: Accessor for column height.
radius: Column radius in meters.
disk_resolution: Number of sides for column polygon.
elevation_scale: Elevation multiplier.
coverage: Column coverage (0-1).
extruded: Whether to extrude columns.
filled: Whether to fill columns.
stroked: Whether to stroke columns.
wireframe: Whether to render as wireframe.
pickable: Whether layer responds to hover/click.
opacity: Layer opacity.
**kwargs: Additional ColumnLayer props.
"""
layer_id = name or f"column-{len(self._deck_layers)}"
processed_data = self._process_deck_data(data)
self.call_js_method(
"addColumnLayer",
id=layer_id,
data=processed_data,
getPosition=get_position,
getFillColor=get_fill_color or [255, 140, 0, 200],
getLineColor=get_line_color or [0, 0, 0, 255],
getElevation=get_elevation,
radius=radius,
diskResolution=disk_resolution,
elevationScale=elevation_scale,
coverage=coverage,
extruded=extruded,
filled=filled,
stroked=stroked,
wireframe=wireframe,
pickable=pickable,
opacity=opacity,
**kwargs,
)
self._deck_layers = {
**self._deck_layers,
layer_id: {"type": "ColumnLayer", "id": layer_id},
}
def add_grid_cell_layer(
self,
data: Any,
name: Optional[str] = None,
get_position: Union[str, Callable] = "coordinates",
get_color: Union[List[int], str, Callable] = None,
get_elevation: Union[float, str, Callable] = 1000,
cell_size: float = 200,
coverage: float = 1,
elevation_scale: float = 1,
extruded: bool = True,
pickable: bool = True,
opacity: float = 0.8,
**kwargs,
) -> None:
"""Add a grid cell layer for pre-aggregated grid visualization.
Args:
data: Array of data objects with position coordinates.
name: Layer ID.
get_position: Accessor for cell position [lng, lat].
get_color: Accessor for cell color [r, g, b, a].
get_elevation: Accessor for cell height.
cell_size: Cell size in meters.
coverage: Cell coverage (0-1).
elevation_scale: Elevation multiplier.
extruded: Whether to extrude cells.
pickable: Whether layer responds to hover/click.
opacity: Layer opacity.
**kwargs: Additional GridCellLayer props.
"""
layer_id = name or f"gridcell-{len(self._deck_layers)}"
processed_data = self._process_deck_data(data)
self.call_js_method(
"addGridCellLayer",
id=layer_id,
data=processed_data,
getPosition=get_position,
getColor=get_color or [255, 140, 0, 200],
getElevation=get_elevation,
cellSize=cell_size,
coverage=coverage,
elevationScale=elevation_scale,
extruded=extruded,
pickable=pickable,
opacity=opacity,
**kwargs,
)
self._deck_layers = {
**self._deck_layers,
layer_id: {"type": "GridCellLayer", "id": layer_id},
}
def add_solid_polygon_layer(
self,
data: Any,
name: Optional[str] = None,
get_polygon: Union[str, Callable] = "polygon",
get_fill_color: Union[List[int], str, Callable] = None,
get_line_color: Union[List[int], str, Callable] = None,
get_elevation: Union[float, str, Callable] = 0,
filled: bool = True,
extruded: bool = False,
wireframe: bool = False,
elevation_scale: float = 1,
pickable: bool = True,
opacity: float = 0.8,
**kwargs,
) -> None:
"""Add a solid polygon layer for filled polygon visualization.
Args:
data: Array of data objects with polygon coordinates.
name: Layer ID.
get_polygon: Accessor for polygon coordinates.
get_fill_color: Accessor for fill color [r, g, b, a].
get_line_color: Accessor for stroke color [r, g, b, a].
get_elevation: Accessor for 3D extrusion height.
filled: Whether to fill polygons.
extruded: Whether to render as 3D polygons.
wireframe: Whether to render wireframe.
elevation_scale: Elevation multiplier.
pickable: Whether layer responds to hover/click.
opacity: Layer opacity.
**kwargs: Additional SolidPolygonLayer props.
"""
layer_id = name or f"solidpolygon-{len(self._deck_layers)}"
processed_data = self._process_deck_data(data)
self.call_js_method(
"addSolidPolygonLayer",
id=layer_id,
data=processed_data,
getPolygon=get_polygon,
getFillColor=get_fill_color or [51, 136, 255, 128],
getLineColor=get_line_color or [0, 0, 0, 255],
getElevation=get_elevation,
filled=filled,
extruded=extruded,
wireframe=wireframe,
elevationScale=elevation_scale,
pickable=pickable,
opacity=opacity,
**kwargs,
)
self._deck_layers = {
**self._deck_layers,
layer_id: {"type": "SolidPolygonLayer", "id": layer_id},
}
def add_tile_layer(
self,
data: Union[str, List[str]],
name: Optional[str] = None,
min_zoom: int = 0,
max_zoom: int = 19,
tile_size: int = 256,
pickable: bool = False,
visible: bool = True,
opacity: float = 1.0,
**kwargs,
) -> None:
"""Add a tile layer for raster tile visualization.
Args:
data: Tile URL template with {z}/{x}/{y} placeholders.
name: Layer ID.
min_zoom: Minimum zoom level.
max_zoom: Maximum zoom level.
tile_size: Tile size in pixels.
pickable: Whether layer responds to hover/click.
visible: Whether layer is visible.
opacity: Layer opacity (0-1).
**kwargs: Additional TileLayer props.
"""
layer_id = name or f"tile-{len(self._deck_layers)}"
self.call_js_method(
"addTileLayer",
id=layer_id,
data=data,
minZoom=min_zoom,
maxZoom=max_zoom,
tileSize=tile_size,
pickable=pickable,
visible=visible,
opacity=opacity,
**kwargs,
)
self._deck_layers = {
**self._deck_layers,
layer_id: {"type": "TileLayer", "id": layer_id},
}
def add_mvt_layer(
self,
data: Union[str, List[str]],
name: Optional[str] = None,
min_zoom: int = 0,
max_zoom: int = 14,
binary: bool = True,
get_fill_color: Union[List[int], str, Callable] = None,
get_line_color: Union[List[int], str, Callable] = None,
get_line_width: Union[float, str, Callable] = 1,
get_point_radius: Union[float, str, Callable] = 5,
line_width_min_pixels: float = 1,
point_radius_min_pixels: float = 2,
pickable: bool = True,
visible: bool = True,
opacity: float = 1.0,
**kwargs,
) -> None:
"""Add a Mapbox Vector Tile (MVT) layer.
Args:
data: MVT tile URL template with {z}/{x}/{y} placeholders.
name: Layer ID.
min_zoom: Minimum zoom level.
max_zoom: Maximum zoom level.
binary: Whether to use binary format.
get_fill_color: Accessor for fill color [r, g, b, a].
get_line_color: Accessor for stroke color [r, g, b, a].
get_line_width: Accessor for line width.
get_point_radius: Accessor for point radius.
line_width_min_pixels: Minimum line width in pixels.
point_radius_min_pixels: Minimum point radius in pixels.
pickable: Whether layer responds to hover/click.
visible: Whether layer is visible.
opacity: Layer opacity (0-1).
**kwargs: Additional MVTLayer props.
"""
layer_id = name or f"mvt-{len(self._deck_layers)}"
self.call_js_method(
"addMVTLayer",
id=layer_id,
data=data,
minZoom=min_zoom,
maxZoom=max_zoom,
binary=binary,
getFillColor=get_fill_color or [51, 136, 255, 128],
getLineColor=get_line_color or [0, 0, 0, 255],
getLineWidth=get_line_width,
getPointRadius=get_point_radius,
lineWidthMinPixels=line_width_min_pixels,
pointRadiusMinPixels=point_radius_min_pixels,
pickable=pickable,
visible=visible,
opacity=opacity,
**kwargs,
)
self._deck_layers = {
**self._deck_layers,
layer_id: {"type": "MVTLayer", "id": layer_id},
}
def add_tile3d_layer(
self,
data: str,
name: Optional[str] = None,
point_size: float = 1,
pickable: bool = True,
visible: bool = True,
opacity: float = 1.0,
load_options: Optional[Dict] = None,
**kwargs,
) -> None:
"""Add a 3D Tiles layer for 3D building/terrain visualization.
Args:
data: URL to tileset.json.
name: Layer ID.
point_size: Point size for point cloud tiles.
pickable: Whether layer responds to hover/click.
visible: Whether layer is visible.
opacity: Layer opacity (0-1).
load_options: Loader options for tile loading.
**kwargs: Additional Tile3DLayer props.
"""
layer_id = name or f"tile3d-{len(self._deck_layers)}"
self.call_js_method(
"addTile3DLayer",
id=layer_id,
data=data,
pointSize=point_size,
pickable=pickable,
visible=visible,
opacity=opacity,
loadOptions=load_options or {},
**kwargs,
)
self._deck_layers = {
**self._deck_layers,
layer_id: {"type": "Tile3DLayer", "id": layer_id},
}
def add_terrain_layer(
self,
elevation_data: Union[str, List[str]],
name: Optional[str] = None,
texture: Optional[str] = None,
mesh_max_error: float = 4.0,
bounds: Optional[List[float]] = None,
elevation_decoder: Optional[Dict] = None,
pickable: bool = False,
visible: bool = True,
opacity: float = 1.0,
wireframe: bool = False,
**kwargs,
) -> None:
"""Add a terrain layer for 3D terrain visualization.
Args:
elevation_data: URL to elevation tiles (e.g., Mapbox terrain).
name: Layer ID.
texture: URL to texture tiles for terrain surface.
mesh_max_error: Maximum mesh error in meters.
bounds: Bounding box [west, south, east, north].
elevation_decoder: Decoder for elevation data format.
pickable: Whether layer responds to hover/click.
visible: Whether layer is visible.
opacity: Layer opacity (0-1).
wireframe: Whether to render as wireframe.
**kwargs: Additional TerrainLayer props.
"""
layer_id = name or f"terrain-{len(self._deck_layers)}"
default_decoder = {
"rScaler": 256,
"gScaler": 1,
"bScaler": 1 / 256,
"offset": -32768,
}
self.call_js_method(
"addTerrainLayer",
id=layer_id,
elevationData=elevation_data,
texture=texture,
meshMaxError=mesh_max_error,
bounds=bounds,
elevationDecoder=elevation_decoder or default_decoder,
pickable=pickable,
visible=visible,
opacity=opacity,
wireframe=wireframe,
**kwargs,
)
self._deck_layers = {
**self._deck_layers,
layer_id: {"type": "TerrainLayer", "id": layer_id},
}
def add_great_circle_layer(
self,
data: Any,
name: Optional[str] = None,
get_source_position: Union[str, Callable] = "source",
get_target_position: Union[str, Callable] = "target",
get_source_color: Union[List[int], str, Callable] = None,
get_target_color: Union[List[int], str, Callable] = None,
get_width: Union[float, str, Callable] = 1,
width_min_pixels: float = 1,
width_max_pixels: float = 100,
pickable: bool = True,
opacity: float = 0.8,
**kwargs,
) -> None:
"""Add a great circle layer for geodesic arc visualization.
Args:
data: Array of data objects with source/target coordinates.
name: Layer ID.
get_source_position: Accessor for source position [lng, lat].
get_target_position: Accessor for target position [lng, lat].
get_source_color: Accessor for source color [r, g, b, a].
get_target_color: Accessor for target color [r, g, b, a].
get_width: Accessor for line width.
width_min_pixels: Minimum line width in pixels.
width_max_pixels: Maximum line width in pixels.
pickable: Whether layer responds to hover/click.
opacity: Layer opacity.
**kwargs: Additional GreatCircleLayer props.
"""
layer_id = name or f"greatcircle-{len(self._deck_layers)}"
processed_data = self._process_deck_data(data)
self.call_js_method(
"addGreatCircleLayer",
id=layer_id,
data=processed_data,
getSourcePosition=get_source_position,
getTargetPosition=get_target_position,
getSourceColor=get_source_color or [51, 136, 255, 255],
getTargetColor=get_target_color or [255, 136, 51, 255],
getWidth=get_width,
widthMinPixels=width_min_pixels,
widthMaxPixels=width_max_pixels,
pickable=pickable,
opacity=opacity,
**kwargs,
)
self._deck_layers = {
**self._deck_layers,
layer_id: {"type": "GreatCircleLayer", "id": layer_id},
}
def add_h3_hexagon_layer(
self,
data: Any,
name: Optional[str] = None,
get_hexagon: Union[str, Callable] = "hexagon",
get_fill_color: Union[List[int], str, Callable] = None,
get_line_color: Union[List[int], str, Callable] = None,
get_elevation: Union[float, str, Callable] = 0,
filled: bool = True,
stroked: bool = True,
extruded: bool = False,
wireframe: bool = False,
elevation_scale: float = 1,
coverage: float = 1,
high_precision: bool = False,
pickable: bool = True,
opacity: float = 0.8,
**kwargs,
) -> None:
"""Add an H3 hexagon layer for H3 spatial index visualization.
Args:
data: Array of data objects with H3 index.
name: Layer ID.
get_hexagon: Accessor for H3 index string.
get_fill_color: Accessor for fill color [r, g, b, a].
get_line_color: Accessor for stroke color [r, g, b, a].
get_elevation: Accessor for 3D extrusion height.
filled: Whether to fill hexagons.
stroked: Whether to stroke hexagons.
extruded: Whether to render as 3D hexagons.
wireframe: Whether to render wireframe.
elevation_scale: Elevation multiplier.
coverage: Hexagon coverage (0-1).
high_precision: Use high precision rendering.
pickable: Whether layer responds to hover/click.
opacity: Layer opacity.
**kwargs: Additional H3HexagonLayer props.
"""
layer_id = name or f"h3hexagon-{len(self._deck_layers)}"
processed_data = self._process_deck_data(data)
self.call_js_method(
"addH3HexagonLayer",
id=layer_id,
data=processed_data,
getHexagon=get_hexagon,
getFillColor=get_fill_color or [51, 136, 255, 128],
getLineColor=get_line_color or [0, 0, 0, 255],
getElevation=get_elevation,
filled=filled,
stroked=stroked,
extruded=extruded,
wireframe=wireframe,
elevationScale=elevation_scale,
coverage=coverage,
highPrecision=high_precision,
pickable=pickable,
opacity=opacity,
**kwargs,
)
self._deck_layers = {
**self._deck_layers,
layer_id: {"type": "H3HexagonLayer", "id": layer_id},
}
def add_h3_cluster_layer(
self,
data: Any,
name: Optional[str] = None,
get_hexagons: Union[str, Callable] = "hexagons",
get_fill_color: Union[List[int], str, Callable] = None,
get_line_color: Union[List[int], str, Callable] = None,
get_line_width: Union[float, str, Callable] = 1,
filled: bool = True,
stroked: bool = True,
extruded: bool = False,
elevation_scale: float = 1,
pickable: bool = True,
opacity: float = 0.8,
**kwargs,
) -> None:
"""Add an H3 cluster layer for grouped H3 cell visualization.
Args:
data: Array of data objects with H3 index arrays.
name: Layer ID.
get_hexagons: Accessor for array of H3 index strings.
get_fill_color: Accessor for fill color [r, g, b, a].
get_line_color: Accessor for stroke color [r, g, b, a].
get_line_width: Accessor for line width.
filled: Whether to fill clusters.
stroked: Whether to stroke clusters.
extruded: Whether to render as 3D.
elevation_scale: Elevation multiplier.
pickable: Whether layer responds to hover/click.
opacity: Layer opacity.
**kwargs: Additional H3ClusterLayer props.
"""
layer_id = name or f"h3cluster-{len(self._deck_layers)}"
processed_data = self._process_deck_data(data)
self.call_js_method(
"addH3ClusterLayer",
id=layer_id,
data=processed_data,
getHexagons=get_hexagons,
getFillColor=get_fill_color or [51, 136, 255, 128],
getLineColor=get_line_color or [0, 0, 0, 255],
getLineWidth=get_line_width,
filled=filled,
stroked=stroked,
extruded=extruded,
elevationScale=elevation_scale,
pickable=pickable,
opacity=opacity,
**kwargs,
)
self._deck_layers = {
**self._deck_layers,
layer_id: {"type": "H3ClusterLayer", "id": layer_id},
}
def add_s2_layer(
self,
data: Any,
name: Optional[str] = None,
get_s2_token: Union[str, Callable] = "s2Token",
get_fill_color: Union[List[int], str, Callable] = None,
get_line_color: Union[List[int], str, Callable] = None,
get_line_width: Union[float, str, Callable] = 1,
get_elevation: Union[float, str, Callable] = 0,
filled: bool = True,
stroked: bool = True,
extruded: bool = False,
wireframe: bool = False,
elevation_scale: float = 1,
pickable: bool = True,
opacity: float = 0.8,
**kwargs,
) -> None:
"""Add an S2 layer for S2 geometry cell visualization.
Args:
data: Array of data objects with S2 token.
name: Layer ID.
get_s2_token: Accessor for S2 token string.
get_fill_color: Accessor for fill color [r, g, b, a].
get_line_color: Accessor for stroke color [r, g, b, a].
get_line_width: Accessor for line width.
get_elevation: Accessor for 3D extrusion height.
filled: Whether to fill cells.
stroked: Whether to stroke cells.
extruded: Whether to render as 3D.
wireframe: Whether to render wireframe.
elevation_scale: Elevation multiplier.
pickable: Whether layer responds to hover/click.
opacity: Layer opacity.
**kwargs: Additional S2Layer props.
"""
layer_id = name or f"s2-{len(self._deck_layers)}"
processed_data = self._process_deck_data(data)
self.call_js_method(
"addS2Layer",
id=layer_id,
data=processed_data,
getS2Token=get_s2_token,
getFillColor=get_fill_color or [51, 136, 255, 128],
getLineColor=get_line_color or [0, 0, 0, 255],
getLineWidth=get_line_width,
getElevation=get_elevation,
filled=filled,
stroked=stroked,
extruded=extruded,
wireframe=wireframe,
elevationScale=elevation_scale,
pickable=pickable,
opacity=opacity,
**kwargs,
)
self._deck_layers = {
**self._deck_layers,
layer_id: {"type": "S2Layer", "id": layer_id},
}
def add_quadkey_layer(
self,
data: Any,
name: Optional[str] = None,
get_quadkey: Union[str, Callable] = "quadkey",
get_fill_color: Union[List[int], str, Callable] = None,
get_line_color: Union[List[int], str, Callable] = None,
get_line_width: Union[float, str, Callable] = 1,
get_elevation: Union[float, str, Callable] = 0,
filled: bool = True,
stroked: bool = True,
extruded: bool = False,
wireframe: bool = False,
elevation_scale: float = 1,
pickable: bool = True,
opacity: float = 0.8,
**kwargs,
) -> None:
"""Add a Quadkey layer for Bing Maps tile index visualization.
Args:
data: Array of data objects with quadkey.
name: Layer ID.
get_quadkey: Accessor for quadkey string.
get_fill_color: Accessor for fill color [r, g, b, a].
get_line_color: Accessor for stroke color [r, g, b, a].
get_line_width: Accessor for line width.
get_elevation: Accessor for 3D extrusion height.
filled: Whether to fill cells.
stroked: Whether to stroke cells.
extruded: Whether to render as 3D.
wireframe: Whether to render wireframe.
elevation_scale: Elevation multiplier.
pickable: Whether layer responds to hover/click.
opacity: Layer opacity.
**kwargs: Additional QuadkeyLayer props.
"""
layer_id = name or f"quadkey-{len(self._deck_layers)}"
processed_data = self._process_deck_data(data)
self.call_js_method(
"addQuadkeyLayer",
id=layer_id,
data=processed_data,
getQuadkey=get_quadkey,
getFillColor=get_fill_color or [51, 136, 255, 128],
getLineColor=get_line_color or [0, 0, 0, 255],
getLineWidth=get_line_width,
getElevation=get_elevation,
filled=filled,
stroked=stroked,
extruded=extruded,
wireframe=wireframe,
elevationScale=elevation_scale,
pickable=pickable,
opacity=opacity,
**kwargs,
)
self._deck_layers = {
**self._deck_layers,
layer_id: {"type": "QuadkeyLayer", "id": layer_id},
}
def add_geohash_layer(
self,
data: Any,
name: Optional[str] = None,
get_geohash: Union[str, Callable] = "geohash",
get_fill_color: Union[List[int], str, Callable] = None,
get_line_color: Union[List[int], str, Callable] = None,
get_line_width: Union[float, str, Callable] = 1,
get_elevation: Union[float, str, Callable] = 0,
filled: bool = True,
stroked: bool = True,
extruded: bool = False,
wireframe: bool = False,
elevation_scale: float = 1,
pickable: bool = True,
opacity: float = 0.8,
**kwargs,
) -> None:
"""Add a Geohash layer for geohash cell visualization.
Args:
data: Array of data objects with geohash.
name: Layer ID.
get_geohash: Accessor for geohash string.
get_fill_color: Accessor for fill color [r, g, b, a].
get_line_color: Accessor for stroke color [r, g, b, a].
get_line_width: Accessor for line width.
get_elevation: Accessor for 3D extrusion height.
filled: Whether to fill cells.
stroked: Whether to stroke cells.
extruded: Whether to render as 3D.
wireframe: Whether to render wireframe.
elevation_scale: Elevation multiplier.
pickable: Whether layer responds to hover/click.
opacity: Layer opacity.
**kwargs: Additional GeohashLayer props.
"""
layer_id = name or f"geohash-{len(self._deck_layers)}"
processed_data = self._process_deck_data(data)
self.call_js_method(
"addGeohashLayer",
id=layer_id,
data=processed_data,
getGeohash=get_geohash,
getFillColor=get_fill_color or [51, 136, 255, 128],
getLineColor=get_line_color or [0, 0, 0, 255],
getLineWidth=get_line_width,
getElevation=get_elevation,
filled=filled,
stroked=stroked,
extruded=extruded,
wireframe=wireframe,
elevationScale=elevation_scale,
pickable=pickable,
opacity=opacity,
**kwargs,
)
self._deck_layers = {
**self._deck_layers,
layer_id: {"type": "GeohashLayer", "id": layer_id},
}
def add_wms_layer(
self,
data: str,
name: Optional[str] = None,
service_type: str = "wms",
layers: Optional[List[str]] = None,
srs: Optional[str] = None,
pickable: bool = False,
visible: bool = True,
opacity: float = 1.0,
**kwargs,
) -> None:
"""Add a WMS layer for OGC Web Map Service visualization.
Args:
data: WMS base URL.
name: Layer ID.
service_type: Service type ('wms' or 'template').
layers: WMS layer names to request.
srs: Spatial reference system (e.g., 'EPSG:4326').
pickable: Whether layer responds to hover/click.
visible: Whether layer is visible.
opacity: Layer opacity (0-1).
**kwargs: Additional WMSLayer props.
"""
layer_id = name or f"wms-{len(self._deck_layers)}"
self.call_js_method(
"addWMSLayer",
id=layer_id,
data=data,
serviceType=service_type,
layers=layers,
srs=srs,
pickable=pickable,
visible=visible,
opacity=opacity,
**kwargs,
)
self._deck_layers = {
**self._deck_layers,
layer_id: {"type": "WMSLayer", "id": layer_id},
}
def add_simple_mesh_layer(
self,
data: Any,
mesh: str,
name: Optional[str] = None,
texture: Optional[str] = None,
get_position: Union[str, Callable] = "coordinates",
get_color: Union[List[int], str, Callable] = None,
get_orientation: Union[str, Callable] = None,
get_scale: Union[str, Callable] = None,
get_translation: Union[str, Callable] = None,
size_scale: float = 1,
wireframe: bool = False,
pickable: bool = True,
opacity: float = 1.0,
**kwargs,
) -> None:
"""Add a simple mesh layer for 3D mesh visualization.
Args:
data: Array of data objects with position coordinates.
mesh: URL to OBJ/glTF mesh file.
name: Layer ID.
texture: URL to texture image.
get_position: Accessor for mesh position [lng, lat, z].
get_color: Accessor for mesh color [r, g, b, a].
get_orientation: Accessor for mesh orientation [pitch, yaw, roll].
get_scale: Accessor for mesh scale [x, y, z].
get_translation: Accessor for mesh translation [x, y, z].
size_scale: Global size multiplier.
wireframe: Whether to render as wireframe.
pickable: Whether layer responds to hover/click.
opacity: Layer opacity (0-1).
**kwargs: Additional SimpleMeshLayer props.
"""
layer_id = name or f"simplemesh-{len(self._deck_layers)}"
processed_data = self._process_deck_data(data)
layer_kwargs = {
"id": layer_id,
"data": processed_data,
"mesh": mesh,
"getPosition": get_position,
"getColor": get_color or [255, 255, 255, 255],
"sizeScale": size_scale,
"wireframe": wireframe,
"pickable": pickable,
"opacity": opacity,
}
if texture:
layer_kwargs["texture"] = texture
if get_orientation:
layer_kwargs["getOrientation"] = get_orientation
if get_scale:
layer_kwargs["getScale"] = get_scale
if get_translation:
layer_kwargs["getTranslation"] = get_translation
layer_kwargs.update(kwargs)
self.call_js_method("addSimpleMeshLayer", **layer_kwargs)
self._deck_layers = {
**self._deck_layers,
layer_id: {"type": "SimpleMeshLayer", "id": layer_id},
}
def add_scenegraph_layer(
self,
data: Any,
scenegraph: str,
name: Optional[str] = None,
get_position: Union[str, Callable] = "coordinates",
get_color: Union[List[int], str, Callable] = None,
get_orientation: Union[str, Callable] = None,
get_scale: Union[str, Callable] = None,
get_translation: Union[str, Callable] = None,
size_scale: float = 1,
size_min_pixels: float = 0,
size_max_pixels: float = 10000,
pickable: bool = True,
opacity: float = 1.0,
**kwargs,
) -> None:
"""Add a scenegraph layer for glTF model visualization.
Args:
data: Array of data objects with position coordinates.
scenegraph: URL to glTF/GLB model file.
name: Layer ID.
get_position: Accessor for model position [lng, lat, z].
get_color: Accessor for model tint color [r, g, b, a].
get_orientation: Accessor for model orientation [pitch, yaw, roll].
get_scale: Accessor for model scale [x, y, z].
get_translation: Accessor for model translation [x, y, z].
size_scale: Global size multiplier.
size_min_pixels: Minimum model size in pixels.
size_max_pixels: Maximum model size in pixels.
pickable: Whether layer responds to hover/click.
opacity: Layer opacity (0-1).
**kwargs: Additional ScenegraphLayer props.
"""
layer_id = name or f"scenegraph-{len(self._deck_layers)}"
processed_data = self._process_deck_data(data)
layer_kwargs = {
"id": layer_id,
"data": processed_data,
"scenegraph": scenegraph,
"getPosition": get_position,
"getColor": get_color or [255, 255, 255, 255],
"sizeScale": size_scale,
"sizeMinPixels": size_min_pixels,
"sizeMaxPixels": size_max_pixels,
"pickable": pickable,
"opacity": opacity,
}
if get_orientation:
layer_kwargs["getOrientation"] = get_orientation
if get_scale:
layer_kwargs["getScale"] = get_scale
if get_translation:
layer_kwargs["getTranslation"] = get_translation
layer_kwargs.update(kwargs)
self.call_js_method("addScenegraphLayer", **layer_kwargs)
self._deck_layers = {
**self._deck_layers,
layer_id: {"type": "ScenegraphLayer", "id": layer_id},
}
def remove_cog_layer(self, layer_id: str) -> None:
"""Remove a COG layer.
Args:
layer_id: Layer identifier to remove.
"""
self.remove_deck_layer(layer_id)
# -------------------------------------------------------------------------
# DeckGL Layer Management
# -------------------------------------------------------------------------
def remove_deck_layer(self, layer_id: str) -> None:
"""Remove a deck.gl layer.
Args:
layer_id: Layer identifier to remove.
"""
if layer_id in self._deck_layers:
layers = dict(self._deck_layers)
del layers[layer_id]
self._deck_layers = layers
self.call_js_method("removeDeckLayer", layer_id)
def set_deck_layer_visibility(self, layer_id: str, visible: bool) -> None:
"""Set deck.gl layer visibility.
Args:
layer_id: Layer identifier.
visible: Whether layer should be visible.
"""
self.call_js_method("setDeckLayerVisibility", layer_id, visible)
# -------------------------------------------------------------------------
# Data Processing Helpers
# -------------------------------------------------------------------------
def _process_deck_data(self, data: Any) -> Any:
"""Process data for deck.gl layers.
Handles GeoDataFrame, GeoJSON, and list of dicts.
Args:
data: Input data in various formats.
Returns:
Processed data suitable for deck.gl.
"""
# Handle GeoDataFrame
if hasattr(data, "__geo_interface__"):
return json.loads(data.to_json())
# Handle file path
if isinstance(data, (str, Path)):
path = Path(data)
if path.exists() and path.suffix.lower() in [".geojson", ".json"]:
with open(path) as f:
return json.load(f)
# Could be URL, return as-is
return str(data)
# Handle dict (GeoJSON or config)
if isinstance(data, dict):
return data
# Handle list of dicts
if isinstance(data, list):
return data
return data
# -------------------------------------------------------------------------
# HTML Export
# -------------------------------------------------------------------------
def _generate_html_template(self) -> str:
"""Generate standalone HTML for the DeckGL map."""
template_path = Path(__file__).parent / "templates" / "deckgl.html"
if template_path.exists():
template = template_path.read_text(encoding="utf-8")
else:
template = self._get_default_template()
# Serialize state
state = {
"center": self.center,
"zoom": self.zoom,
"style": self.style,
"bearing": self.bearing,
"pitch": self.pitch,
"width": self.width,
"height": self.height,
"layers": self._layers,
"sources": self._sources,
"controls": self._controls,
"deckLayers": self._deck_layers,
"js_calls": self._js_calls,
}
template = template.replace("{{state}}", json.dumps(state, indent=2))
return template
__init__(self, center=(0.0, 0.0), zoom=2.0, width='100%', height='600px', style='https://demotiles.maplibre.org/style.json', bearing=0.0, pitch=0.0, controls=None, **kwargs)
special
¶
Initialize a DeckGL map.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
center |
Tuple[float, float] |
Map center as (longitude, latitude). |
(0.0, 0.0) |
zoom |
float |
Initial zoom level. |
2.0 |
width |
str |
Map width as CSS string. |
'100%' |
height |
str |
Map height as CSS string. |
'600px' |
style |
Union[str, Dict] |
MapLibre style URL or style object. |
'https://demotiles.maplibre.org/style.json' |
bearing |
float |
Map bearing in degrees. |
0.0 |
pitch |
float |
Map pitch in degrees. |
0.0 |
controls |
Optional[Dict[str, Any]] |
Dict of controls to add (e.g., {"navigation": True}). |
None |
**kwargs |
Additional widget arguments. |
{} |
Source code in anymap_ts/deckgl.py
def __init__(
self,
center: Tuple[float, float] = (0.0, 0.0),
zoom: float = 2.0,
width: str = "100%",
height: str = "600px",
style: Union[str, Dict] = "https://demotiles.maplibre.org/style.json",
bearing: float = 0.0,
pitch: float = 0.0,
controls: Optional[Dict[str, Any]] = None,
**kwargs,
):
"""Initialize a DeckGL map.
Args:
center: Map center as (longitude, latitude).
zoom: Initial zoom level.
width: Map width as CSS string.
height: Map height as CSS string.
style: MapLibre style URL or style object.
bearing: Map bearing in degrees.
pitch: Map pitch in degrees.
controls: Dict of controls to add (e.g., {"navigation": True}).
**kwargs: Additional widget arguments.
"""
super().__init__(
center=center,
zoom=zoom,
width=width,
height=height,
style=style,
bearing=bearing,
pitch=pitch,
controls=controls,
**kwargs,
)
self._deck_layers = {}
add_arc_layer(self, data, name=None, get_source_position='source', get_target_position='target', get_source_color=None, get_target_color=None, get_width=1, pickable=True, opacity=0.8, **kwargs)
¶
Add an arc layer for origin-destination visualization.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data |
Any |
Array of data objects with source/target coordinates. |
required |
name |
Optional[str] |
Layer ID. |
None |
get_source_position |
Union[str, Callable] |
Accessor for source position [lng, lat]. |
'source' |
get_target_position |
Union[str, Callable] |
Accessor for target position [lng, lat]. |
'target' |
get_source_color |
Union[List[int], str, Callable] |
Accessor for source color [r, g, b, a]. |
None |
get_target_color |
Union[List[int], str, Callable] |
Accessor for target color [r, g, b, a]. |
None |
get_width |
Union[float, str, Callable] |
Accessor for arc width. |
1 |
pickable |
bool |
Whether layer responds to hover/click. |
True |
opacity |
float |
Layer opacity. |
0.8 |
**kwargs |
Additional layer props. |
{} |
Source code in anymap_ts/deckgl.py
def add_arc_layer(
self,
data: Any,
name: Optional[str] = None,
get_source_position: Union[str, Callable] = "source",
get_target_position: Union[str, Callable] = "target",
get_source_color: Union[List[int], str, Callable] = None,
get_target_color: Union[List[int], str, Callable] = None,
get_width: Union[float, str, Callable] = 1,
pickable: bool = True,
opacity: float = 0.8,
**kwargs,
) -> None:
"""Add an arc layer for origin-destination visualization.
Args:
data: Array of data objects with source/target coordinates.
name: Layer ID.
get_source_position: Accessor for source position [lng, lat].
get_target_position: Accessor for target position [lng, lat].
get_source_color: Accessor for source color [r, g, b, a].
get_target_color: Accessor for target color [r, g, b, a].
get_width: Accessor for arc width.
pickable: Whether layer responds to hover/click.
opacity: Layer opacity.
**kwargs: Additional layer props.
"""
layer_id = name or f"arc-{len(self._deck_layers)}"
processed_data = self._process_deck_data(data)
self.call_js_method(
"addArcLayer",
id=layer_id,
data=processed_data,
getSourcePosition=get_source_position,
getTargetPosition=get_target_position,
getSourceColor=get_source_color or [51, 136, 255, 255],
getTargetColor=get_target_color or [255, 136, 51, 255],
getWidth=get_width,
pickable=pickable,
opacity=opacity,
**kwargs,
)
self._deck_layers = {
**self._deck_layers,
layer_id: {"type": "ArcLayer", "id": layer_id},
}
add_bitmap_layer(self, image, bounds, name=None, opacity=1.0, visible=True, pickable=False, desaturate=0, transparent_color=None, tint_color=None, **kwargs)
¶
Add a bitmap layer for raster image overlay.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
image |
str |
URL or data URI of the image. |
required |
bounds |
List[float] |
Bounding box [west, south, east, north]. |
required |
name |
Optional[str] |
Layer ID. |
None |
opacity |
float |
Layer opacity (0-1). |
1.0 |
visible |
bool |
Whether layer is visible. |
True |
pickable |
bool |
Whether layer responds to hover/click. |
False |
desaturate |
float |
Desaturation amount (0-1). |
0 |
transparent_color |
Optional[List[int]] |
Color to make transparent [r, g, b, a]. |
None |
tint_color |
Optional[List[int]] |
Color to tint the image [r, g, b]. |
None |
**kwargs |
Additional BitmapLayer props. |
{} |
Source code in anymap_ts/deckgl.py
def add_bitmap_layer(
self,
image: str,
bounds: List[float],
name: Optional[str] = None,
opacity: float = 1.0,
visible: bool = True,
pickable: bool = False,
desaturate: float = 0,
transparent_color: Optional[List[int]] = None,
tint_color: Optional[List[int]] = None,
**kwargs,
) -> None:
"""Add a bitmap layer for raster image overlay.
Args:
image: URL or data URI of the image.
bounds: Bounding box [west, south, east, north].
name: Layer ID.
opacity: Layer opacity (0-1).
visible: Whether layer is visible.
pickable: Whether layer responds to hover/click.
desaturate: Desaturation amount (0-1).
transparent_color: Color to make transparent [r, g, b, a].
tint_color: Color to tint the image [r, g, b].
**kwargs: Additional BitmapLayer props.
"""
layer_id = name or f"bitmap-{len(self._deck_layers)}"
self.call_js_method(
"addBitmapLayer",
id=layer_id,
image=image,
bounds=bounds,
opacity=opacity,
visible=visible,
pickable=pickable,
desaturate=desaturate,
transparentColor=transparent_color or [0, 0, 0, 0],
tintColor=tint_color or [255, 255, 255],
**kwargs,
)
self._deck_layers = {
**self._deck_layers,
layer_id: {"type": "BitmapLayer", "id": layer_id},
}
add_cog_layer(self, url, name=None, opacity=1.0, visible=True, debug=False, debug_opacity=0.25, max_error=0.125, fit_bounds=True, before_id=None, **kwargs)
¶
Add a Cloud Optimized GeoTIFF (COG) layer using @developmentseed/deck.gl-geotiff.
This method renders COG files directly in the browser using GPU-accelerated deck.gl rendering with automatic reprojection support.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
url |
str |
URL to the Cloud Optimized GeoTIFF file. |
required |
name |
Optional[str] |
Layer ID. If None, auto-generated. |
None |
opacity |
float |
Layer opacity (0-1). |
1.0 |
visible |
bool |
Whether layer is visible. |
True |
debug |
bool |
Show reprojection mesh for debugging. |
False |
debug_opacity |
float |
Opacity of debug mesh (0-1). |
0.25 |
max_error |
float |
Maximum reprojection error in pixels. Lower values create denser mesh for better accuracy. |
0.125 |
fit_bounds |
bool |
Whether to fit map to COG bounds after loading. |
True |
before_id |
Optional[str] |
ID of layer to insert before. |
None |
**kwargs |
Additional COGLayer props. |
{} |
Examples:
>>> m = DeckGLMap()
>>> m.add_cog_layer(
... "https://example.com/landcover.tif",
... name="landcover",
... opacity=0.8
... )
Source code in anymap_ts/deckgl.py
def add_cog_layer(
self,
url: str,
name: Optional[str] = None,
opacity: float = 1.0,
visible: bool = True,
debug: bool = False,
debug_opacity: float = 0.25,
max_error: float = 0.125,
fit_bounds: bool = True,
before_id: Optional[str] = None,
**kwargs,
) -> None:
"""Add a Cloud Optimized GeoTIFF (COG) layer using @developmentseed/deck.gl-geotiff.
This method renders COG files directly in the browser using GPU-accelerated
deck.gl rendering with automatic reprojection support.
Args:
url: URL to the Cloud Optimized GeoTIFF file.
name: Layer ID. If None, auto-generated.
opacity: Layer opacity (0-1).
visible: Whether layer is visible.
debug: Show reprojection mesh for debugging.
debug_opacity: Opacity of debug mesh (0-1).
max_error: Maximum reprojection error in pixels. Lower values
create denser mesh for better accuracy.
fit_bounds: Whether to fit map to COG bounds after loading.
before_id: ID of layer to insert before.
**kwargs: Additional COGLayer props.
Example:
>>> m = DeckGLMap()
>>> m.add_cog_layer(
... "https://example.com/landcover.tif",
... name="landcover",
... opacity=0.8
... )
"""
# Use a monotonically increasing counter to avoid ID collisions when layers are removed.
counter = getattr(self, "_cog_layer_counter", 0)
layer_id = name or f"cog-{counter}"
self._cog_layer_counter = counter + 1
self.call_js_method(
"addCOGLayer",
id=layer_id,
geotiff=url,
opacity=opacity,
visible=visible,
debug=debug,
debugOpacity=debug_opacity,
maxError=max_error,
fitBounds=fit_bounds,
beforeId=before_id,
**kwargs,
)
self._deck_layers = {
**self._deck_layers,
layer_id: {"type": "COGLayer", "id": layer_id, "url": url},
}
add_column_layer(self, data, name=None, get_position='coordinates', get_fill_color=None, get_line_color=None, get_elevation=1000, radius=1000, disk_resolution=20, elevation_scale=1, coverage=1, extruded=True, filled=True, stroked=False, wireframe=False, pickable=True, opacity=0.8, **kwargs)
¶
Add a column layer for 3D column/bar visualization.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data |
Any |
Array of data objects with position coordinates. |
required |
name |
Optional[str] |
Layer ID. |
None |
get_position |
Union[str, Callable] |
Accessor for column position [lng, lat]. |
'coordinates' |
get_fill_color |
Union[List[int], str, Callable] |
Accessor for fill color [r, g, b, a]. |
None |
get_line_color |
Union[List[int], str, Callable] |
Accessor for stroke color [r, g, b, a]. |
None |
get_elevation |
Union[float, str, Callable] |
Accessor for column height. |
1000 |
radius |
float |
Column radius in meters. |
1000 |
disk_resolution |
int |
Number of sides for column polygon. |
20 |
elevation_scale |
float |
Elevation multiplier. |
1 |
coverage |
float |
Column coverage (0-1). |
1 |
extruded |
bool |
Whether to extrude columns. |
True |
filled |
bool |
Whether to fill columns. |
True |
stroked |
bool |
Whether to stroke columns. |
False |
wireframe |
bool |
Whether to render as wireframe. |
False |
pickable |
bool |
Whether layer responds to hover/click. |
True |
opacity |
float |
Layer opacity. |
0.8 |
**kwargs |
Additional ColumnLayer props. |
{} |
Source code in anymap_ts/deckgl.py
def add_column_layer(
self,
data: Any,
name: Optional[str] = None,
get_position: Union[str, Callable] = "coordinates",
get_fill_color: Union[List[int], str, Callable] = None,
get_line_color: Union[List[int], str, Callable] = None,
get_elevation: Union[float, str, Callable] = 1000,
radius: float = 1000,
disk_resolution: int = 20,
elevation_scale: float = 1,
coverage: float = 1,
extruded: bool = True,
filled: bool = True,
stroked: bool = False,
wireframe: bool = False,
pickable: bool = True,
opacity: float = 0.8,
**kwargs,
) -> None:
"""Add a column layer for 3D column/bar visualization.
Args:
data: Array of data objects with position coordinates.
name: Layer ID.
get_position: Accessor for column position [lng, lat].
get_fill_color: Accessor for fill color [r, g, b, a].
get_line_color: Accessor for stroke color [r, g, b, a].
get_elevation: Accessor for column height.
radius: Column radius in meters.
disk_resolution: Number of sides for column polygon.
elevation_scale: Elevation multiplier.
coverage: Column coverage (0-1).
extruded: Whether to extrude columns.
filled: Whether to fill columns.
stroked: Whether to stroke columns.
wireframe: Whether to render as wireframe.
pickable: Whether layer responds to hover/click.
opacity: Layer opacity.
**kwargs: Additional ColumnLayer props.
"""
layer_id = name or f"column-{len(self._deck_layers)}"
processed_data = self._process_deck_data(data)
self.call_js_method(
"addColumnLayer",
id=layer_id,
data=processed_data,
getPosition=get_position,
getFillColor=get_fill_color or [255, 140, 0, 200],
getLineColor=get_line_color or [0, 0, 0, 255],
getElevation=get_elevation,
radius=radius,
diskResolution=disk_resolution,
elevationScale=elevation_scale,
coverage=coverage,
extruded=extruded,
filled=filled,
stroked=stroked,
wireframe=wireframe,
pickable=pickable,
opacity=opacity,
**kwargs,
)
self._deck_layers = {
**self._deck_layers,
layer_id: {"type": "ColumnLayer", "id": layer_id},
}
add_contour_layer(self, data, name=None, get_position='coordinates', get_weight=1, cell_size=200, contours=None, pickable=True, opacity=1, **kwargs)
¶
Add a contour layer for isoline visualization.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data |
Any |
Array of data objects with position coordinates. |
required |
name |
Optional[str] |
Layer ID. |
None |
get_position |
Union[str, Callable] |
Accessor for point position [lng, lat]. |
'coordinates' |
get_weight |
Union[float, str, Callable] |
Accessor for point weight. |
1 |
cell_size |
float |
Grid cell size for aggregation. |
200 |
contours |
Optional[List[Dict]] |
Contour definitions [{threshold, color, strokeWidth}, ...]. |
None |
pickable |
bool |
Whether layer responds to hover/click. |
True |
opacity |
float |
Layer opacity. |
1 |
**kwargs |
Additional layer props. |
{} |
Source code in anymap_ts/deckgl.py
def add_contour_layer(
self,
data: Any,
name: Optional[str] = None,
get_position: Union[str, Callable] = "coordinates",
get_weight: Union[float, str, Callable] = 1,
cell_size: float = 200,
contours: Optional[List[Dict]] = None,
pickable: bool = True,
opacity: float = 1,
**kwargs,
) -> None:
"""Add a contour layer for isoline visualization.
Args:
data: Array of data objects with position coordinates.
name: Layer ID.
get_position: Accessor for point position [lng, lat].
get_weight: Accessor for point weight.
cell_size: Grid cell size for aggregation.
contours: Contour definitions [{threshold, color, strokeWidth}, ...].
pickable: Whether layer responds to hover/click.
opacity: Layer opacity.
**kwargs: Additional layer props.
"""
layer_id = name or f"contour-{len(self._deck_layers)}"
processed_data = self._process_deck_data(data)
default_contours = [
{"threshold": 1, "color": [255, 255, 255], "strokeWidth": 1},
{"threshold": 5, "color": [51, 136, 255], "strokeWidth": 2},
{"threshold": 10, "color": [0, 0, 255], "strokeWidth": 3},
]
self.call_js_method(
"addContourLayer",
id=layer_id,
data=processed_data,
getPosition=get_position,
getWeight=get_weight,
cellSize=cell_size,
contours=contours or default_contours,
pickable=pickable,
opacity=opacity,
**kwargs,
)
self._deck_layers = {
**self._deck_layers,
layer_id: {"type": "ContourLayer", "id": layer_id},
}
add_deckgl_layer(self, layer_type, data, name=None, **kwargs)
¶
Add a generic deck.gl layer to the map.
This method provides a flexible way to add any supported deck.gl layer type using a single interface. For commonly used layers, prefer the specific methods (e.g., add_scatterplot_layer) for better IDE support.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
layer_type |
str |
The deck.gl layer type. Supported types include: 'ScatterplotLayer', 'ArcLayer', 'PathLayer', 'PolygonLayer', 'HexagonLayer', 'HeatmapLayer', 'GridLayer', 'IconLayer', 'TextLayer', 'GeoJsonLayer', 'ContourLayer', 'ScreenGridLayer', 'PointCloudLayer', 'TripsLayer', 'LineLayer'. |
required |
data |
Any |
Array of data objects or GeoJSON. |
required |
name |
Optional[str] |
Layer ID. If None, auto-generated from layer_type. |
None |
**kwargs |
Layer-specific properties passed directly to deck.gl. Common properties include: - opacity: Layer opacity (0-1) - pickable: Whether layer responds to hover/click - getPosition: Accessor for position coordinates - getColor/getFillColor/getLineColor: Color accessors |
{} |
Examples:
>>> m = DeckGLMap()
>>> # Add a TripsLayer with animation
>>> m.add_deckgl_layer(
... 'TripsLayer',
... data=trips_data,
... getPath='waypoints',
... getTimestamps='timestamps',
... getColor=[253, 128, 93],
... trailLength=180,
... )
>>> # Add a LineLayer
>>> m.add_deckgl_layer(
... 'LineLayer',
... data=lines_data,
... getSourcePosition='source',
... getTargetPosition='target',
... getColor=[0, 128, 255],
... )
Source code in anymap_ts/deckgl.py
def add_deckgl_layer(
self,
layer_type: str,
data: Any,
name: Optional[str] = None,
**kwargs,
) -> None:
"""Add a generic deck.gl layer to the map.
This method provides a flexible way to add any supported deck.gl layer
type using a single interface. For commonly used layers, prefer the
specific methods (e.g., add_scatterplot_layer) for better IDE support.
Args:
layer_type: The deck.gl layer type. Supported types include:
'ScatterplotLayer', 'ArcLayer', 'PathLayer', 'PolygonLayer',
'HexagonLayer', 'HeatmapLayer', 'GridLayer', 'IconLayer',
'TextLayer', 'GeoJsonLayer', 'ContourLayer', 'ScreenGridLayer',
'PointCloudLayer', 'TripsLayer', 'LineLayer'.
data: Array of data objects or GeoJSON.
name: Layer ID. If None, auto-generated from layer_type.
**kwargs: Layer-specific properties passed directly to deck.gl.
Common properties include:
- opacity: Layer opacity (0-1)
- pickable: Whether layer responds to hover/click
- getPosition: Accessor for position coordinates
- getColor/getFillColor/getLineColor: Color accessors
Example:
>>> m = DeckGLMap()
>>> # Add a TripsLayer with animation
>>> m.add_deckgl_layer(
... 'TripsLayer',
... data=trips_data,
... getPath='waypoints',
... getTimestamps='timestamps',
... getColor=[253, 128, 93],
... trailLength=180,
... )
>>> # Add a LineLayer
>>> m.add_deckgl_layer(
... 'LineLayer',
... data=lines_data,
... getSourcePosition='source',
... getTargetPosition='target',
... getColor=[0, 128, 255],
... )
"""
# Normalize layer type and create prefix
layer_type_clean = layer_type.replace("Layer", "")
prefix = layer_type_clean.lower()
layer_id = name or f"{prefix}-{len(self._deck_layers)}"
processed_data = self._process_deck_data(data)
self.call_js_method(
"addDeckGLLayer",
layerType=layer_type,
id=layer_id,
data=processed_data,
**kwargs,
)
self._deck_layers = {
**self._deck_layers,
layer_id: {"type": layer_type, "id": layer_id},
}
add_geohash_layer(self, data, name=None, get_geohash='geohash', get_fill_color=None, get_line_color=None, get_line_width=1, get_elevation=0, filled=True, stroked=True, extruded=False, wireframe=False, elevation_scale=1, pickable=True, opacity=0.8, **kwargs)
¶
Add a Geohash layer for geohash cell visualization.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data |
Any |
Array of data objects with geohash. |
required |
name |
Optional[str] |
Layer ID. |
None |
get_geohash |
Union[str, Callable] |
Accessor for geohash string. |
'geohash' |
get_fill_color |
Union[List[int], str, Callable] |
Accessor for fill color [r, g, b, a]. |
None |
get_line_color |
Union[List[int], str, Callable] |
Accessor for stroke color [r, g, b, a]. |
None |
get_line_width |
Union[float, str, Callable] |
Accessor for line width. |
1 |
get_elevation |
Union[float, str, Callable] |
Accessor for 3D extrusion height. |
0 |
filled |
bool |
Whether to fill cells. |
True |
stroked |
bool |
Whether to stroke cells. |
True |
extruded |
bool |
Whether to render as 3D. |
False |
wireframe |
bool |
Whether to render wireframe. |
False |
elevation_scale |
float |
Elevation multiplier. |
1 |
pickable |
bool |
Whether layer responds to hover/click. |
True |
opacity |
float |
Layer opacity. |
0.8 |
**kwargs |
Additional GeohashLayer props. |
{} |
Source code in anymap_ts/deckgl.py
def add_geohash_layer(
self,
data: Any,
name: Optional[str] = None,
get_geohash: Union[str, Callable] = "geohash",
get_fill_color: Union[List[int], str, Callable] = None,
get_line_color: Union[List[int], str, Callable] = None,
get_line_width: Union[float, str, Callable] = 1,
get_elevation: Union[float, str, Callable] = 0,
filled: bool = True,
stroked: bool = True,
extruded: bool = False,
wireframe: bool = False,
elevation_scale: float = 1,
pickable: bool = True,
opacity: float = 0.8,
**kwargs,
) -> None:
"""Add a Geohash layer for geohash cell visualization.
Args:
data: Array of data objects with geohash.
name: Layer ID.
get_geohash: Accessor for geohash string.
get_fill_color: Accessor for fill color [r, g, b, a].
get_line_color: Accessor for stroke color [r, g, b, a].
get_line_width: Accessor for line width.
get_elevation: Accessor for 3D extrusion height.
filled: Whether to fill cells.
stroked: Whether to stroke cells.
extruded: Whether to render as 3D.
wireframe: Whether to render wireframe.
elevation_scale: Elevation multiplier.
pickable: Whether layer responds to hover/click.
opacity: Layer opacity.
**kwargs: Additional GeohashLayer props.
"""
layer_id = name or f"geohash-{len(self._deck_layers)}"
processed_data = self._process_deck_data(data)
self.call_js_method(
"addGeohashLayer",
id=layer_id,
data=processed_data,
getGeohash=get_geohash,
getFillColor=get_fill_color or [51, 136, 255, 128],
getLineColor=get_line_color or [0, 0, 0, 255],
getLineWidth=get_line_width,
getElevation=get_elevation,
filled=filled,
stroked=stroked,
extruded=extruded,
wireframe=wireframe,
elevationScale=elevation_scale,
pickable=pickable,
opacity=opacity,
**kwargs,
)
self._deck_layers = {
**self._deck_layers,
layer_id: {"type": "GeohashLayer", "id": layer_id},
}
add_geojson_layer(self, data, name=None, get_fill_color=None, get_line_color=None, get_line_width=1, get_point_radius=5, get_elevation=0, extruded=False, wireframe=False, filled=True, stroked=True, line_width_min_pixels=1, point_radius_min_pixels=2, pickable=True, opacity=0.8, **kwargs)
¶
Add a GeoJSON layer for rendering GeoJSON features.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data |
Any |
GeoJSON object or URL. |
required |
name |
Optional[str] |
Layer ID. |
None |
get_fill_color |
Union[List[int], str, Callable] |
Accessor for fill color [r, g, b, a]. |
None |
get_line_color |
Union[List[int], str, Callable] |
Accessor for stroke color [r, g, b, a]. |
None |
get_line_width |
Union[float, str, Callable] |
Accessor for stroke width. |
1 |
get_point_radius |
Union[float, str, Callable] |
Accessor for point radius. |
5 |
get_elevation |
Union[float, str, Callable] |
Accessor for 3D extrusion height. |
0 |
extruded |
bool |
Whether to render as 3D features. |
False |
wireframe |
bool |
Whether to render wireframe (extruded only). |
False |
filled |
bool |
Whether to fill features. |
True |
stroked |
bool |
Whether to draw stroke. |
True |
line_width_min_pixels |
float |
Minimum stroke width. |
1 |
point_radius_min_pixels |
float |
Minimum point radius. |
2 |
pickable |
bool |
Whether layer responds to hover/click. |
True |
opacity |
float |
Layer opacity. |
0.8 |
**kwargs |
Additional layer props. |
{} |
Source code in anymap_ts/deckgl.py
def add_geojson_layer(
self,
data: Any,
name: Optional[str] = None,
get_fill_color: Union[List[int], str, Callable] = None,
get_line_color: Union[List[int], str, Callable] = None,
get_line_width: Union[float, str, Callable] = 1,
get_point_radius: Union[float, str, Callable] = 5,
get_elevation: Union[float, str, Callable] = 0,
extruded: bool = False,
wireframe: bool = False,
filled: bool = True,
stroked: bool = True,
line_width_min_pixels: float = 1,
point_radius_min_pixels: float = 2,
pickable: bool = True,
opacity: float = 0.8,
**kwargs,
) -> None:
"""Add a GeoJSON layer for rendering GeoJSON features.
Args:
data: GeoJSON object or URL.
name: Layer ID.
get_fill_color: Accessor for fill color [r, g, b, a].
get_line_color: Accessor for stroke color [r, g, b, a].
get_line_width: Accessor for stroke width.
get_point_radius: Accessor for point radius.
get_elevation: Accessor for 3D extrusion height.
extruded: Whether to render as 3D features.
wireframe: Whether to render wireframe (extruded only).
filled: Whether to fill features.
stroked: Whether to draw stroke.
line_width_min_pixels: Minimum stroke width.
point_radius_min_pixels: Minimum point radius.
pickable: Whether layer responds to hover/click.
opacity: Layer opacity.
**kwargs: Additional layer props.
"""
layer_id = name or f"geojson-{len(self._deck_layers)}"
processed_data = self._process_deck_data(data)
self.call_js_method(
"addGeoJsonLayer",
id=layer_id,
data=processed_data,
getFillColor=get_fill_color or [51, 136, 255, 128],
getLineColor=get_line_color or [0, 0, 0, 255],
getLineWidth=get_line_width,
getPointRadius=get_point_radius,
getElevation=get_elevation,
extruded=extruded,
wireframe=wireframe,
filled=filled,
stroked=stroked,
lineWidthMinPixels=line_width_min_pixels,
pointRadiusMinPixels=point_radius_min_pixels,
pickable=pickable,
opacity=opacity,
**kwargs,
)
self._deck_layers = {
**self._deck_layers,
layer_id: {"type": "GeoJsonLayer", "id": layer_id},
}
add_great_circle_layer(self, data, name=None, get_source_position='source', get_target_position='target', get_source_color=None, get_target_color=None, get_width=1, width_min_pixels=1, width_max_pixels=100, pickable=True, opacity=0.8, **kwargs)
¶
Add a great circle layer for geodesic arc visualization.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data |
Any |
Array of data objects with source/target coordinates. |
required |
name |
Optional[str] |
Layer ID. |
None |
get_source_position |
Union[str, Callable] |
Accessor for source position [lng, lat]. |
'source' |
get_target_position |
Union[str, Callable] |
Accessor for target position [lng, lat]. |
'target' |
get_source_color |
Union[List[int], str, Callable] |
Accessor for source color [r, g, b, a]. |
None |
get_target_color |
Union[List[int], str, Callable] |
Accessor for target color [r, g, b, a]. |
None |
get_width |
Union[float, str, Callable] |
Accessor for line width. |
1 |
width_min_pixels |
float |
Minimum line width in pixels. |
1 |
width_max_pixels |
float |
Maximum line width in pixels. |
100 |
pickable |
bool |
Whether layer responds to hover/click. |
True |
opacity |
float |
Layer opacity. |
0.8 |
**kwargs |
Additional GreatCircleLayer props. |
{} |
Source code in anymap_ts/deckgl.py
def add_great_circle_layer(
self,
data: Any,
name: Optional[str] = None,
get_source_position: Union[str, Callable] = "source",
get_target_position: Union[str, Callable] = "target",
get_source_color: Union[List[int], str, Callable] = None,
get_target_color: Union[List[int], str, Callable] = None,
get_width: Union[float, str, Callable] = 1,
width_min_pixels: float = 1,
width_max_pixels: float = 100,
pickable: bool = True,
opacity: float = 0.8,
**kwargs,
) -> None:
"""Add a great circle layer for geodesic arc visualization.
Args:
data: Array of data objects with source/target coordinates.
name: Layer ID.
get_source_position: Accessor for source position [lng, lat].
get_target_position: Accessor for target position [lng, lat].
get_source_color: Accessor for source color [r, g, b, a].
get_target_color: Accessor for target color [r, g, b, a].
get_width: Accessor for line width.
width_min_pixels: Minimum line width in pixels.
width_max_pixels: Maximum line width in pixels.
pickable: Whether layer responds to hover/click.
opacity: Layer opacity.
**kwargs: Additional GreatCircleLayer props.
"""
layer_id = name or f"greatcircle-{len(self._deck_layers)}"
processed_data = self._process_deck_data(data)
self.call_js_method(
"addGreatCircleLayer",
id=layer_id,
data=processed_data,
getSourcePosition=get_source_position,
getTargetPosition=get_target_position,
getSourceColor=get_source_color or [51, 136, 255, 255],
getTargetColor=get_target_color or [255, 136, 51, 255],
getWidth=get_width,
widthMinPixels=width_min_pixels,
widthMaxPixels=width_max_pixels,
pickable=pickable,
opacity=opacity,
**kwargs,
)
self._deck_layers = {
**self._deck_layers,
layer_id: {"type": "GreatCircleLayer", "id": layer_id},
}
add_grid_cell_layer(self, data, name=None, get_position='coordinates', get_color=None, get_elevation=1000, cell_size=200, coverage=1, elevation_scale=1, extruded=True, pickable=True, opacity=0.8, **kwargs)
¶
Add a grid cell layer for pre-aggregated grid visualization.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data |
Any |
Array of data objects with position coordinates. |
required |
name |
Optional[str] |
Layer ID. |
None |
get_position |
Union[str, Callable] |
Accessor for cell position [lng, lat]. |
'coordinates' |
get_color |
Union[List[int], str, Callable] |
Accessor for cell color [r, g, b, a]. |
None |
get_elevation |
Union[float, str, Callable] |
Accessor for cell height. |
1000 |
cell_size |
float |
Cell size in meters. |
200 |
coverage |
float |
Cell coverage (0-1). |
1 |
elevation_scale |
float |
Elevation multiplier. |
1 |
extruded |
bool |
Whether to extrude cells. |
True |
pickable |
bool |
Whether layer responds to hover/click. |
True |
opacity |
float |
Layer opacity. |
0.8 |
**kwargs |
Additional GridCellLayer props. |
{} |
Source code in anymap_ts/deckgl.py
def add_grid_cell_layer(
self,
data: Any,
name: Optional[str] = None,
get_position: Union[str, Callable] = "coordinates",
get_color: Union[List[int], str, Callable] = None,
get_elevation: Union[float, str, Callable] = 1000,
cell_size: float = 200,
coverage: float = 1,
elevation_scale: float = 1,
extruded: bool = True,
pickable: bool = True,
opacity: float = 0.8,
**kwargs,
) -> None:
"""Add a grid cell layer for pre-aggregated grid visualization.
Args:
data: Array of data objects with position coordinates.
name: Layer ID.
get_position: Accessor for cell position [lng, lat].
get_color: Accessor for cell color [r, g, b, a].
get_elevation: Accessor for cell height.
cell_size: Cell size in meters.
coverage: Cell coverage (0-1).
elevation_scale: Elevation multiplier.
extruded: Whether to extrude cells.
pickable: Whether layer responds to hover/click.
opacity: Layer opacity.
**kwargs: Additional GridCellLayer props.
"""
layer_id = name or f"gridcell-{len(self._deck_layers)}"
processed_data = self._process_deck_data(data)
self.call_js_method(
"addGridCellLayer",
id=layer_id,
data=processed_data,
getPosition=get_position,
getColor=get_color or [255, 140, 0, 200],
getElevation=get_elevation,
cellSize=cell_size,
coverage=coverage,
elevationScale=elevation_scale,
extruded=extruded,
pickable=pickable,
opacity=opacity,
**kwargs,
)
self._deck_layers = {
**self._deck_layers,
layer_id: {"type": "GridCellLayer", "id": layer_id},
}
add_grid_layer(self, data, name=None, get_position='coordinates', cell_size=200, elevation_scale=4, extruded=True, color_range=None, pickable=True, opacity=0.8, **kwargs)
¶
Add a grid layer for square grid aggregation visualization.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data |
Any |
Array of data objects with position coordinates. |
required |
name |
Optional[str] |
Layer ID. |
None |
get_position |
Union[str, Callable] |
Accessor for point position [lng, lat]. |
'coordinates' |
cell_size |
float |
Grid cell size in meters. |
200 |
elevation_scale |
float |
Elevation multiplier for 3D. |
4 |
extruded |
bool |
Whether to render as 3D cells. |
True |
color_range |
Optional[List[List[int]]] |
Color gradient [[r, g, b], ...]. |
None |
pickable |
bool |
Whether layer responds to hover/click. |
True |
opacity |
float |
Layer opacity. |
0.8 |
**kwargs |
Additional layer props. |
{} |
Source code in anymap_ts/deckgl.py
def add_grid_layer(
self,
data: Any,
name: Optional[str] = None,
get_position: Union[str, Callable] = "coordinates",
cell_size: float = 200,
elevation_scale: float = 4,
extruded: bool = True,
color_range: Optional[List[List[int]]] = None,
pickable: bool = True,
opacity: float = 0.8,
**kwargs,
) -> None:
"""Add a grid layer for square grid aggregation visualization.
Args:
data: Array of data objects with position coordinates.
name: Layer ID.
get_position: Accessor for point position [lng, lat].
cell_size: Grid cell size in meters.
elevation_scale: Elevation multiplier for 3D.
extruded: Whether to render as 3D cells.
color_range: Color gradient [[r, g, b], ...].
pickable: Whether layer responds to hover/click.
opacity: Layer opacity.
**kwargs: Additional layer props.
"""
layer_id = name or f"grid-{len(self._deck_layers)}"
processed_data = self._process_deck_data(data)
default_color_range = [
[1, 152, 189],
[73, 227, 206],
[216, 254, 181],
[254, 237, 177],
[254, 173, 84],
[209, 55, 78],
]
self.call_js_method(
"addGridLayer",
id=layer_id,
data=processed_data,
getPosition=get_position,
cellSize=cell_size,
elevationScale=elevation_scale,
extruded=extruded,
colorRange=color_range or default_color_range,
pickable=pickable,
opacity=opacity,
**kwargs,
)
self._deck_layers = {
**self._deck_layers,
layer_id: {"type": "GridLayer", "id": layer_id},
}
add_h3_cluster_layer(self, data, name=None, get_hexagons='hexagons', get_fill_color=None, get_line_color=None, get_line_width=1, filled=True, stroked=True, extruded=False, elevation_scale=1, pickable=True, opacity=0.8, **kwargs)
¶
Add an H3 cluster layer for grouped H3 cell visualization.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data |
Any |
Array of data objects with H3 index arrays. |
required |
name |
Optional[str] |
Layer ID. |
None |
get_hexagons |
Union[str, Callable] |
Accessor for array of H3 index strings. |
'hexagons' |
get_fill_color |
Union[List[int], str, Callable] |
Accessor for fill color [r, g, b, a]. |
None |
get_line_color |
Union[List[int], str, Callable] |
Accessor for stroke color [r, g, b, a]. |
None |
get_line_width |
Union[float, str, Callable] |
Accessor for line width. |
1 |
filled |
bool |
Whether to fill clusters. |
True |
stroked |
bool |
Whether to stroke clusters. |
True |
extruded |
bool |
Whether to render as 3D. |
False |
elevation_scale |
float |
Elevation multiplier. |
1 |
pickable |
bool |
Whether layer responds to hover/click. |
True |
opacity |
float |
Layer opacity. |
0.8 |
**kwargs |
Additional H3ClusterLayer props. |
{} |
Source code in anymap_ts/deckgl.py
def add_h3_cluster_layer(
self,
data: Any,
name: Optional[str] = None,
get_hexagons: Union[str, Callable] = "hexagons",
get_fill_color: Union[List[int], str, Callable] = None,
get_line_color: Union[List[int], str, Callable] = None,
get_line_width: Union[float, str, Callable] = 1,
filled: bool = True,
stroked: bool = True,
extruded: bool = False,
elevation_scale: float = 1,
pickable: bool = True,
opacity: float = 0.8,
**kwargs,
) -> None:
"""Add an H3 cluster layer for grouped H3 cell visualization.
Args:
data: Array of data objects with H3 index arrays.
name: Layer ID.
get_hexagons: Accessor for array of H3 index strings.
get_fill_color: Accessor for fill color [r, g, b, a].
get_line_color: Accessor for stroke color [r, g, b, a].
get_line_width: Accessor for line width.
filled: Whether to fill clusters.
stroked: Whether to stroke clusters.
extruded: Whether to render as 3D.
elevation_scale: Elevation multiplier.
pickable: Whether layer responds to hover/click.
opacity: Layer opacity.
**kwargs: Additional H3ClusterLayer props.
"""
layer_id = name or f"h3cluster-{len(self._deck_layers)}"
processed_data = self._process_deck_data(data)
self.call_js_method(
"addH3ClusterLayer",
id=layer_id,
data=processed_data,
getHexagons=get_hexagons,
getFillColor=get_fill_color or [51, 136, 255, 128],
getLineColor=get_line_color or [0, 0, 0, 255],
getLineWidth=get_line_width,
filled=filled,
stroked=stroked,
extruded=extruded,
elevationScale=elevation_scale,
pickable=pickable,
opacity=opacity,
**kwargs,
)
self._deck_layers = {
**self._deck_layers,
layer_id: {"type": "H3ClusterLayer", "id": layer_id},
}
add_h3_hexagon_layer(self, data, name=None, get_hexagon='hexagon', get_fill_color=None, get_line_color=None, get_elevation=0, filled=True, stroked=True, extruded=False, wireframe=False, elevation_scale=1, coverage=1, high_precision=False, pickable=True, opacity=0.8, **kwargs)
¶
Add an H3 hexagon layer for H3 spatial index visualization.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data |
Any |
Array of data objects with H3 index. |
required |
name |
Optional[str] |
Layer ID. |
None |
get_hexagon |
Union[str, Callable] |
Accessor for H3 index string. |
'hexagon' |
get_fill_color |
Union[List[int], str, Callable] |
Accessor for fill color [r, g, b, a]. |
None |
get_line_color |
Union[List[int], str, Callable] |
Accessor for stroke color [r, g, b, a]. |
None |
get_elevation |
Union[float, str, Callable] |
Accessor for 3D extrusion height. |
0 |
filled |
bool |
Whether to fill hexagons. |
True |
stroked |
bool |
Whether to stroke hexagons. |
True |
extruded |
bool |
Whether to render as 3D hexagons. |
False |
wireframe |
bool |
Whether to render wireframe. |
False |
elevation_scale |
float |
Elevation multiplier. |
1 |
coverage |
float |
Hexagon coverage (0-1). |
1 |
high_precision |
bool |
Use high precision rendering. |
False |
pickable |
bool |
Whether layer responds to hover/click. |
True |
opacity |
float |
Layer opacity. |
0.8 |
**kwargs |
Additional H3HexagonLayer props. |
{} |
Source code in anymap_ts/deckgl.py
def add_h3_hexagon_layer(
self,
data: Any,
name: Optional[str] = None,
get_hexagon: Union[str, Callable] = "hexagon",
get_fill_color: Union[List[int], str, Callable] = None,
get_line_color: Union[List[int], str, Callable] = None,
get_elevation: Union[float, str, Callable] = 0,
filled: bool = True,
stroked: bool = True,
extruded: bool = False,
wireframe: bool = False,
elevation_scale: float = 1,
coverage: float = 1,
high_precision: bool = False,
pickable: bool = True,
opacity: float = 0.8,
**kwargs,
) -> None:
"""Add an H3 hexagon layer for H3 spatial index visualization.
Args:
data: Array of data objects with H3 index.
name: Layer ID.
get_hexagon: Accessor for H3 index string.
get_fill_color: Accessor for fill color [r, g, b, a].
get_line_color: Accessor for stroke color [r, g, b, a].
get_elevation: Accessor for 3D extrusion height.
filled: Whether to fill hexagons.
stroked: Whether to stroke hexagons.
extruded: Whether to render as 3D hexagons.
wireframe: Whether to render wireframe.
elevation_scale: Elevation multiplier.
coverage: Hexagon coverage (0-1).
high_precision: Use high precision rendering.
pickable: Whether layer responds to hover/click.
opacity: Layer opacity.
**kwargs: Additional H3HexagonLayer props.
"""
layer_id = name or f"h3hexagon-{len(self._deck_layers)}"
processed_data = self._process_deck_data(data)
self.call_js_method(
"addH3HexagonLayer",
id=layer_id,
data=processed_data,
getHexagon=get_hexagon,
getFillColor=get_fill_color or [51, 136, 255, 128],
getLineColor=get_line_color or [0, 0, 0, 255],
getElevation=get_elevation,
filled=filled,
stroked=stroked,
extruded=extruded,
wireframe=wireframe,
elevationScale=elevation_scale,
coverage=coverage,
highPrecision=high_precision,
pickable=pickable,
opacity=opacity,
**kwargs,
)
self._deck_layers = {
**self._deck_layers,
layer_id: {"type": "H3HexagonLayer", "id": layer_id},
}
add_heatmap_layer(self, data, name=None, get_position='coordinates', get_weight=1, radius_pixels=30, intensity=1, threshold=0.05, color_range=None, opacity=1, **kwargs)
¶
Add a heatmap layer for density visualization.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data |
Any |
Array of data objects with position coordinates. |
required |
name |
Optional[str] |
Layer ID. |
None |
get_position |
Union[str, Callable] |
Accessor for point position [lng, lat]. |
'coordinates' |
get_weight |
Union[float, str, Callable] |
Accessor for point weight. |
1 |
radius_pixels |
float |
Influence radius in pixels. |
30 |
intensity |
float |
Intensity multiplier. |
1 |
threshold |
float |
Minimum density threshold. |
0.05 |
color_range |
Optional[List[List[int]]] |
Color gradient [[r, g, b, a], ...]. |
None |
opacity |
float |
Layer opacity. |
1 |
**kwargs |
Additional layer props. |
{} |
Source code in anymap_ts/deckgl.py
def add_heatmap_layer(
self,
data: Any,
name: Optional[str] = None,
get_position: Union[str, Callable] = "coordinates",
get_weight: Union[float, str, Callable] = 1,
radius_pixels: float = 30,
intensity: float = 1,
threshold: float = 0.05,
color_range: Optional[List[List[int]]] = None,
opacity: float = 1,
**kwargs,
) -> None:
"""Add a heatmap layer for density visualization.
Args:
data: Array of data objects with position coordinates.
name: Layer ID.
get_position: Accessor for point position [lng, lat].
get_weight: Accessor for point weight.
radius_pixels: Influence radius in pixels.
intensity: Intensity multiplier.
threshold: Minimum density threshold.
color_range: Color gradient [[r, g, b, a], ...].
opacity: Layer opacity.
**kwargs: Additional layer props.
"""
layer_id = name or f"heatmap-{len(self._deck_layers)}"
processed_data = self._process_deck_data(data)
default_color_range = [
[255, 255, 178, 25],
[254, 217, 118, 85],
[254, 178, 76, 127],
[253, 141, 60, 170],
[240, 59, 32, 212],
[189, 0, 38, 255],
]
self.call_js_method(
"addHeatmapLayer",
id=layer_id,
data=processed_data,
getPosition=get_position,
getWeight=get_weight,
radiusPixels=radius_pixels,
intensity=intensity,
threshold=threshold,
colorRange=color_range or default_color_range,
opacity=opacity,
**kwargs,
)
self._deck_layers = {
**self._deck_layers,
layer_id: {"type": "HeatmapLayer", "id": layer_id},
}
add_hexagon_layer(self, data, name=None, get_position='coordinates', radius=1000, elevation_scale=4, extruded=True, color_range=None, pickable=True, opacity=0.8, **kwargs)
¶
Add a hexagon layer for hexbin aggregation visualization.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data |
Any |
Array of data objects with position coordinates. |
required |
name |
Optional[str] |
Layer ID. |
None |
get_position |
Union[str, Callable] |
Accessor for point position [lng, lat]. |
'coordinates' |
radius |
float |
Hexagon radius in meters. |
1000 |
elevation_scale |
float |
Elevation multiplier for 3D. |
4 |
extruded |
bool |
Whether to render as 3D hexagons. |
True |
color_range |
Optional[List[List[int]]] |
Color gradient for aggregation [[r, g, b], ...]. |
None |
pickable |
bool |
Whether layer responds to hover/click. |
True |
opacity |
float |
Layer opacity. |
0.8 |
**kwargs |
Additional layer props. |
{} |
Source code in anymap_ts/deckgl.py
def add_hexagon_layer(
self,
data: Any,
name: Optional[str] = None,
get_position: Union[str, Callable] = "coordinates",
radius: float = 1000,
elevation_scale: float = 4,
extruded: bool = True,
color_range: Optional[List[List[int]]] = None,
pickable: bool = True,
opacity: float = 0.8,
**kwargs,
) -> None:
"""Add a hexagon layer for hexbin aggregation visualization.
Args:
data: Array of data objects with position coordinates.
name: Layer ID.
get_position: Accessor for point position [lng, lat].
radius: Hexagon radius in meters.
elevation_scale: Elevation multiplier for 3D.
extruded: Whether to render as 3D hexagons.
color_range: Color gradient for aggregation [[r, g, b], ...].
pickable: Whether layer responds to hover/click.
opacity: Layer opacity.
**kwargs: Additional layer props.
"""
layer_id = name or f"hexagon-{len(self._deck_layers)}"
processed_data = self._process_deck_data(data)
default_color_range = [
[1, 152, 189],
[73, 227, 206],
[216, 254, 181],
[254, 237, 177],
[254, 173, 84],
[209, 55, 78],
]
self.call_js_method(
"addHexagonLayer",
id=layer_id,
data=processed_data,
getPosition=get_position,
radius=radius,
elevationScale=elevation_scale,
extruded=extruded,
colorRange=color_range or default_color_range,
pickable=pickable,
opacity=opacity,
**kwargs,
)
self._deck_layers = {
**self._deck_layers,
layer_id: {"type": "HexagonLayer", "id": layer_id},
}
add_icon_layer(self, data, name=None, get_position='coordinates', get_icon='icon', get_size=20, get_color=None, icon_atlas=None, icon_mapping=None, pickable=True, opacity=1, **kwargs)
¶
Add an icon layer for custom marker visualization.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data |
Any |
Array of data objects with position coordinates. |
required |
name |
Optional[str] |
Layer ID. |
None |
get_position |
Union[str, Callable] |
Accessor for icon position [lng, lat]. |
'coordinates' |
get_icon |
Union[str, Callable] |
Accessor for icon name in icon_mapping. |
'icon' |
get_size |
Union[float, str, Callable] |
Accessor for icon size. |
20 |
get_color |
Union[List[int], str, Callable] |
Accessor for icon tint color [r, g, b, a]. |
None |
icon_atlas |
Optional[str] |
URL to icon atlas image. |
None |
icon_mapping |
Optional[Dict] |
Dict mapping icon names to atlas coordinates. |
None |
pickable |
bool |
Whether layer responds to hover/click. |
True |
opacity |
float |
Layer opacity. |
1 |
**kwargs |
Additional layer props. |
{} |
Source code in anymap_ts/deckgl.py
def add_icon_layer(
self,
data: Any,
name: Optional[str] = None,
get_position: Union[str, Callable] = "coordinates",
get_icon: Union[str, Callable] = "icon",
get_size: Union[float, str, Callable] = 20,
get_color: Union[List[int], str, Callable] = None,
icon_atlas: Optional[str] = None,
icon_mapping: Optional[Dict] = None,
pickable: bool = True,
opacity: float = 1,
**kwargs,
) -> None:
"""Add an icon layer for custom marker visualization.
Args:
data: Array of data objects with position coordinates.
name: Layer ID.
get_position: Accessor for icon position [lng, lat].
get_icon: Accessor for icon name in icon_mapping.
get_size: Accessor for icon size.
get_color: Accessor for icon tint color [r, g, b, a].
icon_atlas: URL to icon atlas image.
icon_mapping: Dict mapping icon names to atlas coordinates.
pickable: Whether layer responds to hover/click.
opacity: Layer opacity.
**kwargs: Additional layer props.
"""
layer_id = name or f"icon-{len(self._deck_layers)}"
processed_data = self._process_deck_data(data)
self.call_js_method(
"addIconLayer",
id=layer_id,
data=processed_data,
getPosition=get_position,
getIcon=get_icon,
getSize=get_size,
getColor=get_color or [255, 255, 255, 255],
iconAtlas=icon_atlas,
iconMapping=icon_mapping,
pickable=pickable,
opacity=opacity,
**kwargs,
)
self._deck_layers = {
**self._deck_layers,
layer_id: {"type": "IconLayer", "id": layer_id},
}
add_line_layer(self, data, name=None, get_source_position='sourcePosition', get_target_position='targetPosition', get_color=None, get_width=1, width_min_pixels=1, pickable=True, opacity=0.8, **kwargs)
¶
Add a line layer for simple line segment visualization.
The LineLayer renders straight line segments between source and target positions. Unlike ArcLayer, lines are drawn directly without curvature.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data |
Any |
Array of line objects with source/target positions. |
required |
name |
Optional[str] |
Layer ID. If None, auto-generated. |
None |
get_source_position |
Union[str, Callable] |
Accessor for source position [lng, lat]. |
'sourcePosition' |
get_target_position |
Union[str, Callable] |
Accessor for target position [lng, lat]. |
'targetPosition' |
get_color |
Union[List[int], str, Callable] |
Accessor for line color [r, g, b] or [r, g, b, a]. |
None |
get_width |
Union[float, str, Callable] |
Accessor for line width. |
1 |
width_min_pixels |
float |
Minimum line width in pixels. |
1 |
pickable |
bool |
Whether layer responds to hover/click. |
True |
opacity |
float |
Layer opacity (0-1). |
0.8 |
**kwargs |
Additional LineLayer props. |
{} |
Examples:
>>> m = DeckGLMap()
>>> lines = [
... {"sourcePosition": [-122.4, 37.8], "targetPosition": [-122.5, 37.7]},
... {"sourcePosition": [-122.5, 37.7], "targetPosition": [-122.6, 37.8]},
... ]
>>> m.add_line_layer(
... data=lines,
... get_color=[0, 128, 255],
... get_width=2,
... )
Source code in anymap_ts/deckgl.py
def add_line_layer(
self,
data: Any,
name: Optional[str] = None,
get_source_position: Union[str, Callable] = "sourcePosition",
get_target_position: Union[str, Callable] = "targetPosition",
get_color: Union[List[int], str, Callable] = None,
get_width: Union[float, str, Callable] = 1,
width_min_pixels: float = 1,
pickable: bool = True,
opacity: float = 0.8,
**kwargs,
) -> None:
"""Add a line layer for simple line segment visualization.
The LineLayer renders straight line segments between source and
target positions. Unlike ArcLayer, lines are drawn directly
without curvature.
Args:
data: Array of line objects with source/target positions.
name: Layer ID. If None, auto-generated.
get_source_position: Accessor for source position [lng, lat].
get_target_position: Accessor for target position [lng, lat].
get_color: Accessor for line color [r, g, b] or [r, g, b, a].
get_width: Accessor for line width.
width_min_pixels: Minimum line width in pixels.
pickable: Whether layer responds to hover/click.
opacity: Layer opacity (0-1).
**kwargs: Additional LineLayer props.
Example:
>>> m = DeckGLMap()
>>> lines = [
... {"sourcePosition": [-122.4, 37.8], "targetPosition": [-122.5, 37.7]},
... {"sourcePosition": [-122.5, 37.7], "targetPosition": [-122.6, 37.8]},
... ]
>>> m.add_line_layer(
... data=lines,
... get_color=[0, 128, 255],
... get_width=2,
... )
"""
layer_id = name or f"line-{len(self._deck_layers)}"
processed_data = self._process_deck_data(data)
self.call_js_method(
"addLineLayer",
id=layer_id,
data=processed_data,
getSourcePosition=get_source_position,
getTargetPosition=get_target_position,
getColor=get_color or [51, 136, 255, 200],
getWidth=get_width,
widthMinPixels=width_min_pixels,
pickable=pickable,
opacity=opacity,
**kwargs,
)
self._deck_layers = {
**self._deck_layers,
layer_id: {"type": "LineLayer", "id": layer_id},
}
add_mvt_layer(self, data, name=None, min_zoom=0, max_zoom=14, binary=True, get_fill_color=None, get_line_color=None, get_line_width=1, get_point_radius=5, line_width_min_pixels=1, point_radius_min_pixels=2, pickable=True, visible=True, opacity=1.0, **kwargs)
¶
Add a Mapbox Vector Tile (MVT) layer.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data |
Union[str, List[str]] |
MVT tile URL template with {z}/{x}/{y} placeholders. |
required |
name |
Optional[str] |
Layer ID. |
None |
min_zoom |
int |
Minimum zoom level. |
0 |
max_zoom |
int |
Maximum zoom level. |
14 |
binary |
bool |
Whether to use binary format. |
True |
get_fill_color |
Union[List[int], str, Callable] |
Accessor for fill color [r, g, b, a]. |
None |
get_line_color |
Union[List[int], str, Callable] |
Accessor for stroke color [r, g, b, a]. |
None |
get_line_width |
Union[float, str, Callable] |
Accessor for line width. |
1 |
get_point_radius |
Union[float, str, Callable] |
Accessor for point radius. |
5 |
line_width_min_pixels |
float |
Minimum line width in pixels. |
1 |
point_radius_min_pixels |
float |
Minimum point radius in pixels. |
2 |
pickable |
bool |
Whether layer responds to hover/click. |
True |
visible |
bool |
Whether layer is visible. |
True |
opacity |
float |
Layer opacity (0-1). |
1.0 |
**kwargs |
Additional MVTLayer props. |
{} |
Source code in anymap_ts/deckgl.py
def add_mvt_layer(
self,
data: Union[str, List[str]],
name: Optional[str] = None,
min_zoom: int = 0,
max_zoom: int = 14,
binary: bool = True,
get_fill_color: Union[List[int], str, Callable] = None,
get_line_color: Union[List[int], str, Callable] = None,
get_line_width: Union[float, str, Callable] = 1,
get_point_radius: Union[float, str, Callable] = 5,
line_width_min_pixels: float = 1,
point_radius_min_pixels: float = 2,
pickable: bool = True,
visible: bool = True,
opacity: float = 1.0,
**kwargs,
) -> None:
"""Add a Mapbox Vector Tile (MVT) layer.
Args:
data: MVT tile URL template with {z}/{x}/{y} placeholders.
name: Layer ID.
min_zoom: Minimum zoom level.
max_zoom: Maximum zoom level.
binary: Whether to use binary format.
get_fill_color: Accessor for fill color [r, g, b, a].
get_line_color: Accessor for stroke color [r, g, b, a].
get_line_width: Accessor for line width.
get_point_radius: Accessor for point radius.
line_width_min_pixels: Minimum line width in pixels.
point_radius_min_pixels: Minimum point radius in pixels.
pickable: Whether layer responds to hover/click.
visible: Whether layer is visible.
opacity: Layer opacity (0-1).
**kwargs: Additional MVTLayer props.
"""
layer_id = name or f"mvt-{len(self._deck_layers)}"
self.call_js_method(
"addMVTLayer",
id=layer_id,
data=data,
minZoom=min_zoom,
maxZoom=max_zoom,
binary=binary,
getFillColor=get_fill_color or [51, 136, 255, 128],
getLineColor=get_line_color or [0, 0, 0, 255],
getLineWidth=get_line_width,
getPointRadius=get_point_radius,
lineWidthMinPixels=line_width_min_pixels,
pointRadiusMinPixels=point_radius_min_pixels,
pickable=pickable,
visible=visible,
opacity=opacity,
**kwargs,
)
self._deck_layers = {
**self._deck_layers,
layer_id: {"type": "MVTLayer", "id": layer_id},
}
add_path_layer(self, data, name=None, get_path='path', get_color=None, get_width=1, width_scale=1, width_min_pixels=1, pickable=True, opacity=0.8, **kwargs)
¶
Add a path layer for polyline visualization.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data |
Any |
Array of data objects with path coordinates. |
required |
name |
Optional[str] |
Layer ID. |
None |
get_path |
Union[str, Callable] |
Accessor for path coordinates [[lng, lat], ...]. |
'path' |
get_color |
Union[List[int], str, Callable] |
Accessor for path color [r, g, b, a]. |
None |
get_width |
Union[float, str, Callable] |
Accessor for path width. |
1 |
width_scale |
float |
Global width multiplier. |
1 |
width_min_pixels |
float |
Minimum width in pixels. |
1 |
pickable |
bool |
Whether layer responds to hover/click. |
True |
opacity |
float |
Layer opacity. |
0.8 |
**kwargs |
Additional layer props. |
{} |
Source code in anymap_ts/deckgl.py
def add_path_layer(
self,
data: Any,
name: Optional[str] = None,
get_path: Union[str, Callable] = "path",
get_color: Union[List[int], str, Callable] = None,
get_width: Union[float, str, Callable] = 1,
width_scale: float = 1,
width_min_pixels: float = 1,
pickable: bool = True,
opacity: float = 0.8,
**kwargs,
) -> None:
"""Add a path layer for polyline visualization.
Args:
data: Array of data objects with path coordinates.
name: Layer ID.
get_path: Accessor for path coordinates [[lng, lat], ...].
get_color: Accessor for path color [r, g, b, a].
get_width: Accessor for path width.
width_scale: Global width multiplier.
width_min_pixels: Minimum width in pixels.
pickable: Whether layer responds to hover/click.
opacity: Layer opacity.
**kwargs: Additional layer props.
"""
layer_id = name or f"path-{len(self._deck_layers)}"
processed_data = self._process_deck_data(data)
self.call_js_method(
"addPathLayer",
id=layer_id,
data=processed_data,
getPath=get_path,
getColor=get_color or [51, 136, 255, 200],
getWidth=get_width,
widthScale=width_scale,
widthMinPixels=width_min_pixels,
pickable=pickable,
opacity=opacity,
**kwargs,
)
self._deck_layers = {
**self._deck_layers,
layer_id: {"type": "PathLayer", "id": layer_id},
}
add_point_cloud_layer(self, data, name=None, get_position='position', get_color=None, get_normal=None, point_size=2, size_units='pixels', coordinate_system=None, coordinate_origin=None, pickable=True, opacity=1.0, **kwargs)
¶
Add a point cloud layer for 3D point visualization.
Renders large point cloud datasets typically from LiDAR or 3D scanning. Supports both 2D and 3D coordinates with optional normal vectors for lighting effects.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data |
Any |
Array of point data with position [x, y, z] coordinates. |
required |
name |
Optional[str] |
Layer ID. If None, auto-generated. |
None |
get_position |
Union[str, Callable] |
Accessor for point position [x, y, z]. |
'position' |
get_color |
Union[List[int], str, Callable] |
Accessor for point color [r, g, b, a]. |
None |
get_normal |
Union[str, Callable] |
Accessor for point normal [nx, ny, nz] for lighting. |
None |
point_size |
float |
Point size in size_units. |
2 |
size_units |
str |
Units for point_size ('pixels' or 'meters'). |
'pixels' |
coordinate_system |
Optional[str] |
Coordinate system ('CARTESIAN', 'METER_OFFSETS', 'LNGLAT', 'LNGLAT_OFFSETS'). |
None |
coordinate_origin |
Optional[List[float]] |
Origin for offset coordinate systems [lng, lat, z]. |
None |
pickable |
bool |
Whether layer responds to hover/click. |
True |
opacity |
float |
Layer opacity (0-1). |
1.0 |
**kwargs |
Additional PointCloudLayer props. |
{} |
Examples:
>>> m = DeckGLMap()
>>> points = [
... {"position": [-122.4, 37.8, 100], "color": [255, 0, 0]},
... {"position": [-122.5, 37.7, 200], "color": [0, 255, 0]},
... ]
>>> m.add_point_cloud_layer(
... data=points,
... point_size=5,
... get_color="color"
... )
Source code in anymap_ts/deckgl.py
def add_point_cloud_layer(
self,
data: Any,
name: Optional[str] = None,
get_position: Union[str, Callable] = "position",
get_color: Union[List[int], str, Callable] = None,
get_normal: Union[str, Callable] = None,
point_size: float = 2,
size_units: str = "pixels",
coordinate_system: Optional[str] = None,
coordinate_origin: Optional[List[float]] = None,
pickable: bool = True,
opacity: float = 1.0,
**kwargs,
) -> None:
"""Add a point cloud layer for 3D point visualization.
Renders large point cloud datasets typically from LiDAR or 3D scanning.
Supports both 2D and 3D coordinates with optional normal vectors for
lighting effects.
Args:
data: Array of point data with position [x, y, z] coordinates.
name: Layer ID. If None, auto-generated.
get_position: Accessor for point position [x, y, z].
get_color: Accessor for point color [r, g, b, a].
get_normal: Accessor for point normal [nx, ny, nz] for lighting.
point_size: Point size in size_units.
size_units: Units for point_size ('pixels' or 'meters').
coordinate_system: Coordinate system ('CARTESIAN', 'METER_OFFSETS',
'LNGLAT', 'LNGLAT_OFFSETS').
coordinate_origin: Origin for offset coordinate systems [lng, lat, z].
pickable: Whether layer responds to hover/click.
opacity: Layer opacity (0-1).
**kwargs: Additional PointCloudLayer props.
Example:
>>> m = DeckGLMap()
>>> points = [
... {"position": [-122.4, 37.8, 100], "color": [255, 0, 0]},
... {"position": [-122.5, 37.7, 200], "color": [0, 255, 0]},
... ]
>>> m.add_point_cloud_layer(
... data=points,
... point_size=5,
... get_color="color"
... )
"""
layer_id = name or f"pointcloud-{len(self._deck_layers)}"
processed_data = self._process_deck_data(data)
layer_kwargs = {
"id": layer_id,
"data": processed_data,
"getPosition": get_position,
"getColor": get_color or [255, 255, 255, 255],
"pointSize": point_size,
"sizeUnits": size_units,
"pickable": pickable,
"opacity": opacity,
}
if get_normal is not None:
layer_kwargs["getNormal"] = get_normal
if coordinate_system is not None:
layer_kwargs["coordinateSystem"] = coordinate_system
if coordinate_origin is not None:
layer_kwargs["coordinateOrigin"] = coordinate_origin
layer_kwargs.update(kwargs)
self.call_js_method("addPointCloudLayer", **layer_kwargs)
self._deck_layers = {
**self._deck_layers,
layer_id: {"type": "PointCloudLayer", "id": layer_id},
}
add_polygon_layer(self, data, name=None, get_polygon='polygon', get_fill_color=None, get_line_color=None, get_line_width=1, get_elevation=0, extruded=False, wireframe=False, filled=True, stroked=True, line_width_min_pixels=1, pickable=True, opacity=0.5, **kwargs)
¶
Add a polygon layer for filled polygon visualization.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data |
Any |
Array of data objects with polygon coordinates. |
required |
name |
Optional[str] |
Layer ID. |
None |
get_polygon |
Union[str, Callable] |
Accessor for polygon coordinates. |
'polygon' |
get_fill_color |
Union[List[int], str, Callable] |
Accessor for fill color [r, g, b, a]. |
None |
get_line_color |
Union[List[int], str, Callable] |
Accessor for stroke color [r, g, b, a]. |
None |
get_line_width |
Union[float, str, Callable] |
Accessor for stroke width. |
1 |
get_elevation |
Union[float, str, Callable] |
Accessor for 3D extrusion height. |
0 |
extruded |
bool |
Whether to render as 3D polygons. |
False |
wireframe |
bool |
Whether to render wireframe (extruded only). |
False |
filled |
bool |
Whether to fill polygons. |
True |
stroked |
bool |
Whether to draw stroke. |
True |
line_width_min_pixels |
float |
Minimum stroke width. |
1 |
pickable |
bool |
Whether layer responds to hover/click. |
True |
opacity |
float |
Layer opacity. |
0.5 |
**kwargs |
Additional layer props. |
{} |
Source code in anymap_ts/deckgl.py
def add_polygon_layer(
self,
data: Any,
name: Optional[str] = None,
get_polygon: Union[str, Callable] = "polygon",
get_fill_color: Union[List[int], str, Callable] = None,
get_line_color: Union[List[int], str, Callable] = None,
get_line_width: Union[float, str, Callable] = 1,
get_elevation: Union[float, str, Callable] = 0,
extruded: bool = False,
wireframe: bool = False,
filled: bool = True,
stroked: bool = True,
line_width_min_pixels: float = 1,
pickable: bool = True,
opacity: float = 0.5,
**kwargs,
) -> None:
"""Add a polygon layer for filled polygon visualization.
Args:
data: Array of data objects with polygon coordinates.
name: Layer ID.
get_polygon: Accessor for polygon coordinates.
get_fill_color: Accessor for fill color [r, g, b, a].
get_line_color: Accessor for stroke color [r, g, b, a].
get_line_width: Accessor for stroke width.
get_elevation: Accessor for 3D extrusion height.
extruded: Whether to render as 3D polygons.
wireframe: Whether to render wireframe (extruded only).
filled: Whether to fill polygons.
stroked: Whether to draw stroke.
line_width_min_pixels: Minimum stroke width.
pickable: Whether layer responds to hover/click.
opacity: Layer opacity.
**kwargs: Additional layer props.
"""
layer_id = name or f"polygon-{len(self._deck_layers)}"
processed_data = self._process_deck_data(data)
self.call_js_method(
"addPolygonLayer",
id=layer_id,
data=processed_data,
getPolygon=get_polygon,
getFillColor=get_fill_color or [51, 136, 255, 128],
getLineColor=get_line_color or [0, 0, 255, 255],
getLineWidth=get_line_width,
getElevation=get_elevation,
extruded=extruded,
wireframe=wireframe,
filled=filled,
stroked=stroked,
lineWidthMinPixels=line_width_min_pixels,
pickable=pickable,
opacity=opacity,
**kwargs,
)
self._deck_layers = {
**self._deck_layers,
layer_id: {"type": "PolygonLayer", "id": layer_id},
}
add_quadkey_layer(self, data, name=None, get_quadkey='quadkey', get_fill_color=None, get_line_color=None, get_line_width=1, get_elevation=0, filled=True, stroked=True, extruded=False, wireframe=False, elevation_scale=1, pickable=True, opacity=0.8, **kwargs)
¶
Add a Quadkey layer for Bing Maps tile index visualization.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data |
Any |
Array of data objects with quadkey. |
required |
name |
Optional[str] |
Layer ID. |
None |
get_quadkey |
Union[str, Callable] |
Accessor for quadkey string. |
'quadkey' |
get_fill_color |
Union[List[int], str, Callable] |
Accessor for fill color [r, g, b, a]. |
None |
get_line_color |
Union[List[int], str, Callable] |
Accessor for stroke color [r, g, b, a]. |
None |
get_line_width |
Union[float, str, Callable] |
Accessor for line width. |
1 |
get_elevation |
Union[float, str, Callable] |
Accessor for 3D extrusion height. |
0 |
filled |
bool |
Whether to fill cells. |
True |
stroked |
bool |
Whether to stroke cells. |
True |
extruded |
bool |
Whether to render as 3D. |
False |
wireframe |
bool |
Whether to render wireframe. |
False |
elevation_scale |
float |
Elevation multiplier. |
1 |
pickable |
bool |
Whether layer responds to hover/click. |
True |
opacity |
float |
Layer opacity. |
0.8 |
**kwargs |
Additional QuadkeyLayer props. |
{} |
Source code in anymap_ts/deckgl.py
def add_quadkey_layer(
self,
data: Any,
name: Optional[str] = None,
get_quadkey: Union[str, Callable] = "quadkey",
get_fill_color: Union[List[int], str, Callable] = None,
get_line_color: Union[List[int], str, Callable] = None,
get_line_width: Union[float, str, Callable] = 1,
get_elevation: Union[float, str, Callable] = 0,
filled: bool = True,
stroked: bool = True,
extruded: bool = False,
wireframe: bool = False,
elevation_scale: float = 1,
pickable: bool = True,
opacity: float = 0.8,
**kwargs,
) -> None:
"""Add a Quadkey layer for Bing Maps tile index visualization.
Args:
data: Array of data objects with quadkey.
name: Layer ID.
get_quadkey: Accessor for quadkey string.
get_fill_color: Accessor for fill color [r, g, b, a].
get_line_color: Accessor for stroke color [r, g, b, a].
get_line_width: Accessor for line width.
get_elevation: Accessor for 3D extrusion height.
filled: Whether to fill cells.
stroked: Whether to stroke cells.
extruded: Whether to render as 3D.
wireframe: Whether to render wireframe.
elevation_scale: Elevation multiplier.
pickable: Whether layer responds to hover/click.
opacity: Layer opacity.
**kwargs: Additional QuadkeyLayer props.
"""
layer_id = name or f"quadkey-{len(self._deck_layers)}"
processed_data = self._process_deck_data(data)
self.call_js_method(
"addQuadkeyLayer",
id=layer_id,
data=processed_data,
getQuadkey=get_quadkey,
getFillColor=get_fill_color or [51, 136, 255, 128],
getLineColor=get_line_color or [0, 0, 0, 255],
getLineWidth=get_line_width,
getElevation=get_elevation,
filled=filled,
stroked=stroked,
extruded=extruded,
wireframe=wireframe,
elevationScale=elevation_scale,
pickable=pickable,
opacity=opacity,
**kwargs,
)
self._deck_layers = {
**self._deck_layers,
layer_id: {"type": "QuadkeyLayer", "id": layer_id},
}
add_s2_layer(self, data, name=None, get_s2_token='s2Token', get_fill_color=None, get_line_color=None, get_line_width=1, get_elevation=0, filled=True, stroked=True, extruded=False, wireframe=False, elevation_scale=1, pickable=True, opacity=0.8, **kwargs)
¶
Add an S2 layer for S2 geometry cell visualization.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data |
Any |
Array of data objects with S2 token. |
required |
name |
Optional[str] |
Layer ID. |
None |
get_s2_token |
Union[str, Callable] |
Accessor for S2 token string. |
's2Token' |
get_fill_color |
Union[List[int], str, Callable] |
Accessor for fill color [r, g, b, a]. |
None |
get_line_color |
Union[List[int], str, Callable] |
Accessor for stroke color [r, g, b, a]. |
None |
get_line_width |
Union[float, str, Callable] |
Accessor for line width. |
1 |
get_elevation |
Union[float, str, Callable] |
Accessor for 3D extrusion height. |
0 |
filled |
bool |
Whether to fill cells. |
True |
stroked |
bool |
Whether to stroke cells. |
True |
extruded |
bool |
Whether to render as 3D. |
False |
wireframe |
bool |
Whether to render wireframe. |
False |
elevation_scale |
float |
Elevation multiplier. |
1 |
pickable |
bool |
Whether layer responds to hover/click. |
True |
opacity |
float |
Layer opacity. |
0.8 |
**kwargs |
Additional S2Layer props. |
{} |
Source code in anymap_ts/deckgl.py
def add_s2_layer(
self,
data: Any,
name: Optional[str] = None,
get_s2_token: Union[str, Callable] = "s2Token",
get_fill_color: Union[List[int], str, Callable] = None,
get_line_color: Union[List[int], str, Callable] = None,
get_line_width: Union[float, str, Callable] = 1,
get_elevation: Union[float, str, Callable] = 0,
filled: bool = True,
stroked: bool = True,
extruded: bool = False,
wireframe: bool = False,
elevation_scale: float = 1,
pickable: bool = True,
opacity: float = 0.8,
**kwargs,
) -> None:
"""Add an S2 layer for S2 geometry cell visualization.
Args:
data: Array of data objects with S2 token.
name: Layer ID.
get_s2_token: Accessor for S2 token string.
get_fill_color: Accessor for fill color [r, g, b, a].
get_line_color: Accessor for stroke color [r, g, b, a].
get_line_width: Accessor for line width.
get_elevation: Accessor for 3D extrusion height.
filled: Whether to fill cells.
stroked: Whether to stroke cells.
extruded: Whether to render as 3D.
wireframe: Whether to render wireframe.
elevation_scale: Elevation multiplier.
pickable: Whether layer responds to hover/click.
opacity: Layer opacity.
**kwargs: Additional S2Layer props.
"""
layer_id = name or f"s2-{len(self._deck_layers)}"
processed_data = self._process_deck_data(data)
self.call_js_method(
"addS2Layer",
id=layer_id,
data=processed_data,
getS2Token=get_s2_token,
getFillColor=get_fill_color or [51, 136, 255, 128],
getLineColor=get_line_color or [0, 0, 0, 255],
getLineWidth=get_line_width,
getElevation=get_elevation,
filled=filled,
stroked=stroked,
extruded=extruded,
wireframe=wireframe,
elevationScale=elevation_scale,
pickable=pickable,
opacity=opacity,
**kwargs,
)
self._deck_layers = {
**self._deck_layers,
layer_id: {"type": "S2Layer", "id": layer_id},
}
add_scatterplot_layer(self, data, name=None, get_position='coordinates', get_radius=5, get_fill_color=None, get_line_color=None, radius_scale=1, radius_min_pixels=1, radius_max_pixels=100, line_width_min_pixels=1, stroked=True, filled=True, pickable=True, opacity=0.8, **kwargs)
¶
Add a scatterplot layer for point visualization.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data |
Any |
Array of data objects or GeoJSON. |
required |
name |
Optional[str] |
Layer ID. |
None |
get_position |
Union[str, Callable] |
Accessor for point position [lng, lat]. |
'coordinates' |
get_radius |
Union[float, str, Callable] |
Accessor for point radius. |
5 |
get_fill_color |
Union[List[int], str, Callable] |
Accessor for fill color [r, g, b, a]. |
None |
get_line_color |
Union[List[int], str, Callable] |
Accessor for stroke color [r, g, b, a]. |
None |
radius_scale |
float |
Global radius multiplier. |
1 |
radius_min_pixels |
float |
Minimum radius in pixels. |
1 |
radius_max_pixels |
float |
Maximum radius in pixels. |
100 |
line_width_min_pixels |
float |
Minimum stroke width. |
1 |
stroked |
bool |
Whether to draw stroke. |
True |
filled |
bool |
Whether to fill points. |
True |
pickable |
bool |
Whether layer responds to hover/click. |
True |
opacity |
float |
Layer opacity. |
0.8 |
**kwargs |
Additional layer props. |
{} |
Source code in anymap_ts/deckgl.py
def add_scatterplot_layer(
self,
data: Any,
name: Optional[str] = None,
get_position: Union[str, Callable] = "coordinates",
get_radius: Union[float, str, Callable] = 5,
get_fill_color: Union[List[int], str, Callable] = None,
get_line_color: Union[List[int], str, Callable] = None,
radius_scale: float = 1,
radius_min_pixels: float = 1,
radius_max_pixels: float = 100,
line_width_min_pixels: float = 1,
stroked: bool = True,
filled: bool = True,
pickable: bool = True,
opacity: float = 0.8,
**kwargs,
) -> None:
"""Add a scatterplot layer for point visualization.
Args:
data: Array of data objects or GeoJSON.
name: Layer ID.
get_position: Accessor for point position [lng, lat].
get_radius: Accessor for point radius.
get_fill_color: Accessor for fill color [r, g, b, a].
get_line_color: Accessor for stroke color [r, g, b, a].
radius_scale: Global radius multiplier.
radius_min_pixels: Minimum radius in pixels.
radius_max_pixels: Maximum radius in pixels.
line_width_min_pixels: Minimum stroke width.
stroked: Whether to draw stroke.
filled: Whether to fill points.
pickable: Whether layer responds to hover/click.
opacity: Layer opacity.
**kwargs: Additional layer props.
"""
layer_id = name or f"scatterplot-{len(self._deck_layers)}"
processed_data = self._process_deck_data(data)
self.call_js_method(
"addScatterplotLayer",
id=layer_id,
data=processed_data,
getPosition=get_position,
getRadius=get_radius,
getFillColor=get_fill_color or [51, 136, 255, 200],
getLineColor=get_line_color or [255, 255, 255, 255],
radiusScale=radius_scale,
radiusMinPixels=radius_min_pixels,
radiusMaxPixels=radius_max_pixels,
lineWidthMinPixels=line_width_min_pixels,
stroked=stroked,
filled=filled,
pickable=pickable,
opacity=opacity,
**kwargs,
)
self._deck_layers = {
**self._deck_layers,
layer_id: {"type": "ScatterplotLayer", "id": layer_id},
}
add_scenegraph_layer(self, data, scenegraph, name=None, get_position='coordinates', get_color=None, get_orientation=None, get_scale=None, get_translation=None, size_scale=1, size_min_pixels=0, size_max_pixels=10000, pickable=True, opacity=1.0, **kwargs)
¶
Add a scenegraph layer for glTF model visualization.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data |
Any |
Array of data objects with position coordinates. |
required |
scenegraph |
str |
URL to glTF/GLB model file. |
required |
name |
Optional[str] |
Layer ID. |
None |
get_position |
Union[str, Callable] |
Accessor for model position [lng, lat, z]. |
'coordinates' |
get_color |
Union[List[int], str, Callable] |
Accessor for model tint color [r, g, b, a]. |
None |
get_orientation |
Union[str, Callable] |
Accessor for model orientation [pitch, yaw, roll]. |
None |
get_scale |
Union[str, Callable] |
Accessor for model scale [x, y, z]. |
None |
get_translation |
Union[str, Callable] |
Accessor for model translation [x, y, z]. |
None |
size_scale |
float |
Global size multiplier. |
1 |
size_min_pixels |
float |
Minimum model size in pixels. |
0 |
size_max_pixels |
float |
Maximum model size in pixels. |
10000 |
pickable |
bool |
Whether layer responds to hover/click. |
True |
opacity |
float |
Layer opacity (0-1). |
1.0 |
**kwargs |
Additional ScenegraphLayer props. |
{} |
Source code in anymap_ts/deckgl.py
def add_scenegraph_layer(
self,
data: Any,
scenegraph: str,
name: Optional[str] = None,
get_position: Union[str, Callable] = "coordinates",
get_color: Union[List[int], str, Callable] = None,
get_orientation: Union[str, Callable] = None,
get_scale: Union[str, Callable] = None,
get_translation: Union[str, Callable] = None,
size_scale: float = 1,
size_min_pixels: float = 0,
size_max_pixels: float = 10000,
pickable: bool = True,
opacity: float = 1.0,
**kwargs,
) -> None:
"""Add a scenegraph layer for glTF model visualization.
Args:
data: Array of data objects with position coordinates.
scenegraph: URL to glTF/GLB model file.
name: Layer ID.
get_position: Accessor for model position [lng, lat, z].
get_color: Accessor for model tint color [r, g, b, a].
get_orientation: Accessor for model orientation [pitch, yaw, roll].
get_scale: Accessor for model scale [x, y, z].
get_translation: Accessor for model translation [x, y, z].
size_scale: Global size multiplier.
size_min_pixels: Minimum model size in pixels.
size_max_pixels: Maximum model size in pixels.
pickable: Whether layer responds to hover/click.
opacity: Layer opacity (0-1).
**kwargs: Additional ScenegraphLayer props.
"""
layer_id = name or f"scenegraph-{len(self._deck_layers)}"
processed_data = self._process_deck_data(data)
layer_kwargs = {
"id": layer_id,
"data": processed_data,
"scenegraph": scenegraph,
"getPosition": get_position,
"getColor": get_color or [255, 255, 255, 255],
"sizeScale": size_scale,
"sizeMinPixels": size_min_pixels,
"sizeMaxPixels": size_max_pixels,
"pickable": pickable,
"opacity": opacity,
}
if get_orientation:
layer_kwargs["getOrientation"] = get_orientation
if get_scale:
layer_kwargs["getScale"] = get_scale
if get_translation:
layer_kwargs["getTranslation"] = get_translation
layer_kwargs.update(kwargs)
self.call_js_method("addScenegraphLayer", **layer_kwargs)
self._deck_layers = {
**self._deck_layers,
layer_id: {"type": "ScenegraphLayer", "id": layer_id},
}
add_screen_grid_layer(self, data, name=None, get_position='coordinates', get_weight=1, cell_size_pixels=50, color_range=None, pickable=True, opacity=0.8, **kwargs)
¶
Add a screen grid layer for screen-space grid aggregation.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data |
Any |
Array of data objects with position coordinates. |
required |
name |
Optional[str] |
Layer ID. |
None |
get_position |
Union[str, Callable] |
Accessor for point position [lng, lat]. |
'coordinates' |
get_weight |
Union[float, str, Callable] |
Accessor for point weight. |
1 |
cell_size_pixels |
float |
Grid cell size in pixels. |
50 |
color_range |
Optional[List[List[int]]] |
Color gradient [[r, g, b, a], ...]. |
None |
pickable |
bool |
Whether layer responds to hover/click. |
True |
opacity |
float |
Layer opacity. |
0.8 |
**kwargs |
Additional layer props. |
{} |
Source code in anymap_ts/deckgl.py
def add_screen_grid_layer(
self,
data: Any,
name: Optional[str] = None,
get_position: Union[str, Callable] = "coordinates",
get_weight: Union[float, str, Callable] = 1,
cell_size_pixels: float = 50,
color_range: Optional[List[List[int]]] = None,
pickable: bool = True,
opacity: float = 0.8,
**kwargs,
) -> None:
"""Add a screen grid layer for screen-space grid aggregation.
Args:
data: Array of data objects with position coordinates.
name: Layer ID.
get_position: Accessor for point position [lng, lat].
get_weight: Accessor for point weight.
cell_size_pixels: Grid cell size in pixels.
color_range: Color gradient [[r, g, b, a], ...].
pickable: Whether layer responds to hover/click.
opacity: Layer opacity.
**kwargs: Additional layer props.
"""
layer_id = name or f"screengrid-{len(self._deck_layers)}"
processed_data = self._process_deck_data(data)
default_color_range = [
[255, 255, 178, 25],
[254, 217, 118, 85],
[254, 178, 76, 127],
[253, 141, 60, 170],
[240, 59, 32, 212],
[189, 0, 38, 255],
]
self.call_js_method(
"addScreenGridLayer",
id=layer_id,
data=processed_data,
getPosition=get_position,
getWeight=get_weight,
cellSizePixels=cell_size_pixels,
colorRange=color_range or default_color_range,
pickable=pickable,
opacity=opacity,
**kwargs,
)
self._deck_layers = {
**self._deck_layers,
layer_id: {"type": "ScreenGridLayer", "id": layer_id},
}
add_simple_mesh_layer(self, data, mesh, name=None, texture=None, get_position='coordinates', get_color=None, get_orientation=None, get_scale=None, get_translation=None, size_scale=1, wireframe=False, pickable=True, opacity=1.0, **kwargs)
¶
Add a simple mesh layer for 3D mesh visualization.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data |
Any |
Array of data objects with position coordinates. |
required |
mesh |
str |
URL to OBJ/glTF mesh file. |
required |
name |
Optional[str] |
Layer ID. |
None |
texture |
Optional[str] |
URL to texture image. |
None |
get_position |
Union[str, Callable] |
Accessor for mesh position [lng, lat, z]. |
'coordinates' |
get_color |
Union[List[int], str, Callable] |
Accessor for mesh color [r, g, b, a]. |
None |
get_orientation |
Union[str, Callable] |
Accessor for mesh orientation [pitch, yaw, roll]. |
None |
get_scale |
Union[str, Callable] |
Accessor for mesh scale [x, y, z]. |
None |
get_translation |
Union[str, Callable] |
Accessor for mesh translation [x, y, z]. |
None |
size_scale |
float |
Global size multiplier. |
1 |
wireframe |
bool |
Whether to render as wireframe. |
False |
pickable |
bool |
Whether layer responds to hover/click. |
True |
opacity |
float |
Layer opacity (0-1). |
1.0 |
**kwargs |
Additional SimpleMeshLayer props. |
{} |
Source code in anymap_ts/deckgl.py
def add_simple_mesh_layer(
self,
data: Any,
mesh: str,
name: Optional[str] = None,
texture: Optional[str] = None,
get_position: Union[str, Callable] = "coordinates",
get_color: Union[List[int], str, Callable] = None,
get_orientation: Union[str, Callable] = None,
get_scale: Union[str, Callable] = None,
get_translation: Union[str, Callable] = None,
size_scale: float = 1,
wireframe: bool = False,
pickable: bool = True,
opacity: float = 1.0,
**kwargs,
) -> None:
"""Add a simple mesh layer for 3D mesh visualization.
Args:
data: Array of data objects with position coordinates.
mesh: URL to OBJ/glTF mesh file.
name: Layer ID.
texture: URL to texture image.
get_position: Accessor for mesh position [lng, lat, z].
get_color: Accessor for mesh color [r, g, b, a].
get_orientation: Accessor for mesh orientation [pitch, yaw, roll].
get_scale: Accessor for mesh scale [x, y, z].
get_translation: Accessor for mesh translation [x, y, z].
size_scale: Global size multiplier.
wireframe: Whether to render as wireframe.
pickable: Whether layer responds to hover/click.
opacity: Layer opacity (0-1).
**kwargs: Additional SimpleMeshLayer props.
"""
layer_id = name or f"simplemesh-{len(self._deck_layers)}"
processed_data = self._process_deck_data(data)
layer_kwargs = {
"id": layer_id,
"data": processed_data,
"mesh": mesh,
"getPosition": get_position,
"getColor": get_color or [255, 255, 255, 255],
"sizeScale": size_scale,
"wireframe": wireframe,
"pickable": pickable,
"opacity": opacity,
}
if texture:
layer_kwargs["texture"] = texture
if get_orientation:
layer_kwargs["getOrientation"] = get_orientation
if get_scale:
layer_kwargs["getScale"] = get_scale
if get_translation:
layer_kwargs["getTranslation"] = get_translation
layer_kwargs.update(kwargs)
self.call_js_method("addSimpleMeshLayer", **layer_kwargs)
self._deck_layers = {
**self._deck_layers,
layer_id: {"type": "SimpleMeshLayer", "id": layer_id},
}
add_solid_polygon_layer(self, data, name=None, get_polygon='polygon', get_fill_color=None, get_line_color=None, get_elevation=0, filled=True, extruded=False, wireframe=False, elevation_scale=1, pickable=True, opacity=0.8, **kwargs)
¶
Add a solid polygon layer for filled polygon visualization.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data |
Any |
Array of data objects with polygon coordinates. |
required |
name |
Optional[str] |
Layer ID. |
None |
get_polygon |
Union[str, Callable] |
Accessor for polygon coordinates. |
'polygon' |
get_fill_color |
Union[List[int], str, Callable] |
Accessor for fill color [r, g, b, a]. |
None |
get_line_color |
Union[List[int], str, Callable] |
Accessor for stroke color [r, g, b, a]. |
None |
get_elevation |
Union[float, str, Callable] |
Accessor for 3D extrusion height. |
0 |
filled |
bool |
Whether to fill polygons. |
True |
extruded |
bool |
Whether to render as 3D polygons. |
False |
wireframe |
bool |
Whether to render wireframe. |
False |
elevation_scale |
float |
Elevation multiplier. |
1 |
pickable |
bool |
Whether layer responds to hover/click. |
True |
opacity |
float |
Layer opacity. |
0.8 |
**kwargs |
Additional SolidPolygonLayer props. |
{} |
Source code in anymap_ts/deckgl.py
def add_solid_polygon_layer(
self,
data: Any,
name: Optional[str] = None,
get_polygon: Union[str, Callable] = "polygon",
get_fill_color: Union[List[int], str, Callable] = None,
get_line_color: Union[List[int], str, Callable] = None,
get_elevation: Union[float, str, Callable] = 0,
filled: bool = True,
extruded: bool = False,
wireframe: bool = False,
elevation_scale: float = 1,
pickable: bool = True,
opacity: float = 0.8,
**kwargs,
) -> None:
"""Add a solid polygon layer for filled polygon visualization.
Args:
data: Array of data objects with polygon coordinates.
name: Layer ID.
get_polygon: Accessor for polygon coordinates.
get_fill_color: Accessor for fill color [r, g, b, a].
get_line_color: Accessor for stroke color [r, g, b, a].
get_elevation: Accessor for 3D extrusion height.
filled: Whether to fill polygons.
extruded: Whether to render as 3D polygons.
wireframe: Whether to render wireframe.
elevation_scale: Elevation multiplier.
pickable: Whether layer responds to hover/click.
opacity: Layer opacity.
**kwargs: Additional SolidPolygonLayer props.
"""
layer_id = name or f"solidpolygon-{len(self._deck_layers)}"
processed_data = self._process_deck_data(data)
self.call_js_method(
"addSolidPolygonLayer",
id=layer_id,
data=processed_data,
getPolygon=get_polygon,
getFillColor=get_fill_color or [51, 136, 255, 128],
getLineColor=get_line_color or [0, 0, 0, 255],
getElevation=get_elevation,
filled=filled,
extruded=extruded,
wireframe=wireframe,
elevationScale=elevation_scale,
pickable=pickable,
opacity=opacity,
**kwargs,
)
self._deck_layers = {
**self._deck_layers,
layer_id: {"type": "SolidPolygonLayer", "id": layer_id},
}
add_terrain_layer(self, elevation_data, name=None, texture=None, mesh_max_error=4.0, bounds=None, elevation_decoder=None, pickable=False, visible=True, opacity=1.0, wireframe=False, **kwargs)
¶
Add a terrain layer for 3D terrain visualization.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
elevation_data |
Union[str, List[str]] |
URL to elevation tiles (e.g., Mapbox terrain). |
required |
name |
Optional[str] |
Layer ID. |
None |
texture |
Optional[str] |
URL to texture tiles for terrain surface. |
None |
mesh_max_error |
float |
Maximum mesh error in meters. |
4.0 |
bounds |
Optional[List[float]] |
Bounding box [west, south, east, north]. |
None |
elevation_decoder |
Optional[Dict] |
Decoder for elevation data format. |
None |
pickable |
bool |
Whether layer responds to hover/click. |
False |
visible |
bool |
Whether layer is visible. |
True |
opacity |
float |
Layer opacity (0-1). |
1.0 |
wireframe |
bool |
Whether to render as wireframe. |
False |
**kwargs |
Additional TerrainLayer props. |
{} |
Source code in anymap_ts/deckgl.py
def add_terrain_layer(
self,
elevation_data: Union[str, List[str]],
name: Optional[str] = None,
texture: Optional[str] = None,
mesh_max_error: float = 4.0,
bounds: Optional[List[float]] = None,
elevation_decoder: Optional[Dict] = None,
pickable: bool = False,
visible: bool = True,
opacity: float = 1.0,
wireframe: bool = False,
**kwargs,
) -> None:
"""Add a terrain layer for 3D terrain visualization.
Args:
elevation_data: URL to elevation tiles (e.g., Mapbox terrain).
name: Layer ID.
texture: URL to texture tiles for terrain surface.
mesh_max_error: Maximum mesh error in meters.
bounds: Bounding box [west, south, east, north].
elevation_decoder: Decoder for elevation data format.
pickable: Whether layer responds to hover/click.
visible: Whether layer is visible.
opacity: Layer opacity (0-1).
wireframe: Whether to render as wireframe.
**kwargs: Additional TerrainLayer props.
"""
layer_id = name or f"terrain-{len(self._deck_layers)}"
default_decoder = {
"rScaler": 256,
"gScaler": 1,
"bScaler": 1 / 256,
"offset": -32768,
}
self.call_js_method(
"addTerrainLayer",
id=layer_id,
elevationData=elevation_data,
texture=texture,
meshMaxError=mesh_max_error,
bounds=bounds,
elevationDecoder=elevation_decoder or default_decoder,
pickable=pickable,
visible=visible,
opacity=opacity,
wireframe=wireframe,
**kwargs,
)
self._deck_layers = {
**self._deck_layers,
layer_id: {"type": "TerrainLayer", "id": layer_id},
}
add_text_layer(self, data, name=None, get_position='coordinates', get_text='text', get_size=12, get_color=None, get_angle=0, text_anchor='middle', alignment_baseline='center', pickable=True, opacity=1, **kwargs)
¶
Add a text layer for label visualization.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data |
Any |
Array of data objects with position and text. |
required |
name |
Optional[str] |
Layer ID. |
None |
get_position |
Union[str, Callable] |
Accessor for text position [lng, lat]. |
'coordinates' |
get_text |
Union[str, Callable] |
Accessor for text content. |
'text' |
get_size |
Union[float, str, Callable] |
Accessor for text size. |
12 |
get_color |
Union[List[int], str, Callable] |
Accessor for text color [r, g, b, a]. |
None |
get_angle |
Union[float, str, Callable] |
Accessor for text rotation in degrees. |
0 |
text_anchor |
str |
Horizontal alignment ('start', 'middle', 'end'). |
'middle' |
alignment_baseline |
str |
Vertical alignment ('top', 'center', 'bottom'). |
'center' |
pickable |
bool |
Whether layer responds to hover/click. |
True |
opacity |
float |
Layer opacity. |
1 |
**kwargs |
Additional layer props. |
{} |
Source code in anymap_ts/deckgl.py
def add_text_layer(
self,
data: Any,
name: Optional[str] = None,
get_position: Union[str, Callable] = "coordinates",
get_text: Union[str, Callable] = "text",
get_size: Union[float, str, Callable] = 12,
get_color: Union[List[int], str, Callable] = None,
get_angle: Union[float, str, Callable] = 0,
text_anchor: str = "middle",
alignment_baseline: str = "center",
pickable: bool = True,
opacity: float = 1,
**kwargs,
) -> None:
"""Add a text layer for label visualization.
Args:
data: Array of data objects with position and text.
name: Layer ID.
get_position: Accessor for text position [lng, lat].
get_text: Accessor for text content.
get_size: Accessor for text size.
get_color: Accessor for text color [r, g, b, a].
get_angle: Accessor for text rotation in degrees.
text_anchor: Horizontal alignment ('start', 'middle', 'end').
alignment_baseline: Vertical alignment ('top', 'center', 'bottom').
pickable: Whether layer responds to hover/click.
opacity: Layer opacity.
**kwargs: Additional layer props.
"""
layer_id = name or f"text-{len(self._deck_layers)}"
processed_data = self._process_deck_data(data)
self.call_js_method(
"addTextLayer",
id=layer_id,
data=processed_data,
getPosition=get_position,
getText=get_text,
getSize=get_size,
getColor=get_color or [0, 0, 0, 255],
getAngle=get_angle,
getTextAnchor=text_anchor,
getAlignmentBaseline=alignment_baseline,
pickable=pickable,
opacity=opacity,
**kwargs,
)
self._deck_layers = {
**self._deck_layers,
layer_id: {"type": "TextLayer", "id": layer_id},
}
add_tile3d_layer(self, data, name=None, point_size=1, pickable=True, visible=True, opacity=1.0, load_options=None, **kwargs)
¶
Add a 3D Tiles layer for 3D building/terrain visualization.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data |
str |
URL to tileset.json. |
required |
name |
Optional[str] |
Layer ID. |
None |
point_size |
float |
Point size for point cloud tiles. |
1 |
pickable |
bool |
Whether layer responds to hover/click. |
True |
visible |
bool |
Whether layer is visible. |
True |
opacity |
float |
Layer opacity (0-1). |
1.0 |
load_options |
Optional[Dict] |
Loader options for tile loading. |
None |
**kwargs |
Additional Tile3DLayer props. |
{} |
Source code in anymap_ts/deckgl.py
def add_tile3d_layer(
self,
data: str,
name: Optional[str] = None,
point_size: float = 1,
pickable: bool = True,
visible: bool = True,
opacity: float = 1.0,
load_options: Optional[Dict] = None,
**kwargs,
) -> None:
"""Add a 3D Tiles layer for 3D building/terrain visualization.
Args:
data: URL to tileset.json.
name: Layer ID.
point_size: Point size for point cloud tiles.
pickable: Whether layer responds to hover/click.
visible: Whether layer is visible.
opacity: Layer opacity (0-1).
load_options: Loader options for tile loading.
**kwargs: Additional Tile3DLayer props.
"""
layer_id = name or f"tile3d-{len(self._deck_layers)}"
self.call_js_method(
"addTile3DLayer",
id=layer_id,
data=data,
pointSize=point_size,
pickable=pickable,
visible=visible,
opacity=opacity,
loadOptions=load_options or {},
**kwargs,
)
self._deck_layers = {
**self._deck_layers,
layer_id: {"type": "Tile3DLayer", "id": layer_id},
}
add_tile_layer(self, data, name=None, min_zoom=0, max_zoom=19, tile_size=256, pickable=False, visible=True, opacity=1.0, **kwargs)
¶
Add a tile layer for raster tile visualization.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data |
Union[str, List[str]] |
Tile URL template with {z}/{x}/{y} placeholders. |
required |
name |
Optional[str] |
Layer ID. |
None |
min_zoom |
int |
Minimum zoom level. |
0 |
max_zoom |
int |
Maximum zoom level. |
19 |
tile_size |
int |
Tile size in pixels. |
256 |
pickable |
bool |
Whether layer responds to hover/click. |
False |
visible |
bool |
Whether layer is visible. |
True |
opacity |
float |
Layer opacity (0-1). |
1.0 |
**kwargs |
Additional TileLayer props. |
{} |
Source code in anymap_ts/deckgl.py
def add_tile_layer(
self,
data: Union[str, List[str]],
name: Optional[str] = None,
min_zoom: int = 0,
max_zoom: int = 19,
tile_size: int = 256,
pickable: bool = False,
visible: bool = True,
opacity: float = 1.0,
**kwargs,
) -> None:
"""Add a tile layer for raster tile visualization.
Args:
data: Tile URL template with {z}/{x}/{y} placeholders.
name: Layer ID.
min_zoom: Minimum zoom level.
max_zoom: Maximum zoom level.
tile_size: Tile size in pixels.
pickable: Whether layer responds to hover/click.
visible: Whether layer is visible.
opacity: Layer opacity (0-1).
**kwargs: Additional TileLayer props.
"""
layer_id = name or f"tile-{len(self._deck_layers)}"
self.call_js_method(
"addTileLayer",
id=layer_id,
data=data,
minZoom=min_zoom,
maxZoom=max_zoom,
tileSize=tile_size,
pickable=pickable,
visible=visible,
opacity=opacity,
**kwargs,
)
self._deck_layers = {
**self._deck_layers,
layer_id: {"type": "TileLayer", "id": layer_id},
}
add_trips_layer(self, data, name=None, get_path='waypoints', get_timestamps='timestamps', get_color=None, width_min_pixels=2, trail_length=180, current_time=0, pickable=True, opacity=0.8, **kwargs)
¶
Add a trips layer for animated path visualization.
The TripsLayer renders animated paths showing movement over time, ideal for visualizing vehicle routes, migration patterns, or any time-based trajectory data.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data |
Any |
Array of trip objects with waypoints and timestamps. |
required |
name |
Optional[str] |
Layer ID. If None, auto-generated. |
None |
get_path |
Union[str, Callable] |
Accessor for waypoint coordinates [[lng, lat], ...]. |
'waypoints' |
get_timestamps |
Union[str, Callable] |
Accessor for timestamps at each waypoint. |
'timestamps' |
get_color |
Union[List[int], str, Callable] |
Accessor for trip color [r, g, b] or [r, g, b, a]. |
None |
width_min_pixels |
float |
Minimum trail width in pixels. |
2 |
trail_length |
float |
Trail length in timestamp units. |
180 |
current_time |
float |
Current animation time. |
0 |
pickable |
bool |
Whether layer responds to hover/click. |
True |
opacity |
float |
Layer opacity (0-1). |
0.8 |
**kwargs |
Additional TripsLayer props. |
{} |
Examples:
>>> m = DeckGLMap()
>>> trips = [
... {
... "waypoints": [[-122.4, 37.8], [-122.5, 37.7], [-122.6, 37.8]],
... "timestamps": [0, 50, 100]
... }
... ]
>>> m.add_trips_layer(
... data=trips,
... trail_length=180,
... current_time=50,
... )
Source code in anymap_ts/deckgl.py
def add_trips_layer(
self,
data: Any,
name: Optional[str] = None,
get_path: Union[str, Callable] = "waypoints",
get_timestamps: Union[str, Callable] = "timestamps",
get_color: Union[List[int], str, Callable] = None,
width_min_pixels: float = 2,
trail_length: float = 180,
current_time: float = 0,
pickable: bool = True,
opacity: float = 0.8,
**kwargs,
) -> None:
"""Add a trips layer for animated path visualization.
The TripsLayer renders animated paths showing movement over time,
ideal for visualizing vehicle routes, migration patterns, or any
time-based trajectory data.
Args:
data: Array of trip objects with waypoints and timestamps.
name: Layer ID. If None, auto-generated.
get_path: Accessor for waypoint coordinates [[lng, lat], ...].
get_timestamps: Accessor for timestamps at each waypoint.
get_color: Accessor for trip color [r, g, b] or [r, g, b, a].
width_min_pixels: Minimum trail width in pixels.
trail_length: Trail length in timestamp units.
current_time: Current animation time.
pickable: Whether layer responds to hover/click.
opacity: Layer opacity (0-1).
**kwargs: Additional TripsLayer props.
Example:
>>> m = DeckGLMap()
>>> trips = [
... {
... "waypoints": [[-122.4, 37.8], [-122.5, 37.7], [-122.6, 37.8]],
... "timestamps": [0, 50, 100]
... }
... ]
>>> m.add_trips_layer(
... data=trips,
... trail_length=180,
... current_time=50,
... )
"""
layer_id = name or f"trips-{len(self._deck_layers)}"
processed_data = self._process_deck_data(data)
self.call_js_method(
"addTripsLayer",
id=layer_id,
data=processed_data,
getPath=get_path,
getTimestamps=get_timestamps,
getColor=get_color or [253, 128, 93],
widthMinPixels=width_min_pixels,
trailLength=trail_length,
currentTime=current_time,
pickable=pickable,
opacity=opacity,
**kwargs,
)
self._deck_layers = {
**self._deck_layers,
layer_id: {"type": "TripsLayer", "id": layer_id},
}
add_wms_layer(self, data, name=None, service_type='wms', layers=None, srs=None, pickable=False, visible=True, opacity=1.0, **kwargs)
¶
Add a WMS layer for OGC Web Map Service visualization.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data |
str |
WMS base URL. |
required |
name |
Optional[str] |
Layer ID. |
None |
service_type |
str |
Service type ('wms' or 'template'). |
'wms' |
layers |
Optional[List[str]] |
WMS layer names to request. |
None |
srs |
Optional[str] |
Spatial reference system (e.g., 'EPSG:4326'). |
None |
pickable |
bool |
Whether layer responds to hover/click. |
False |
visible |
bool |
Whether layer is visible. |
True |
opacity |
float |
Layer opacity (0-1). |
1.0 |
**kwargs |
Additional WMSLayer props. |
{} |
Source code in anymap_ts/deckgl.py
def add_wms_layer(
self,
data: str,
name: Optional[str] = None,
service_type: str = "wms",
layers: Optional[List[str]] = None,
srs: Optional[str] = None,
pickable: bool = False,
visible: bool = True,
opacity: float = 1.0,
**kwargs,
) -> None:
"""Add a WMS layer for OGC Web Map Service visualization.
Args:
data: WMS base URL.
name: Layer ID.
service_type: Service type ('wms' or 'template').
layers: WMS layer names to request.
srs: Spatial reference system (e.g., 'EPSG:4326').
pickable: Whether layer responds to hover/click.
visible: Whether layer is visible.
opacity: Layer opacity (0-1).
**kwargs: Additional WMSLayer props.
"""
layer_id = name or f"wms-{len(self._deck_layers)}"
self.call_js_method(
"addWMSLayer",
id=layer_id,
data=data,
serviceType=service_type,
layers=layers,
srs=srs,
pickable=pickable,
visible=visible,
opacity=opacity,
**kwargs,
)
self._deck_layers = {
**self._deck_layers,
layer_id: {"type": "WMSLayer", "id": layer_id},
}
remove_cog_layer(self, layer_id)
¶
Remove a COG layer.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
layer_id |
str |
Layer identifier to remove. |
required |
Source code in anymap_ts/deckgl.py
def remove_cog_layer(self, layer_id: str) -> None:
"""Remove a COG layer.
Args:
layer_id: Layer identifier to remove.
"""
self.remove_deck_layer(layer_id)
remove_deck_layer(self, layer_id)
¶
Remove a deck.gl layer.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
layer_id |
str |
Layer identifier to remove. |
required |
Source code in anymap_ts/deckgl.py
def remove_deck_layer(self, layer_id: str) -> None:
"""Remove a deck.gl layer.
Args:
layer_id: Layer identifier to remove.
"""
if layer_id in self._deck_layers:
layers = dict(self._deck_layers)
del layers[layer_id]
self._deck_layers = layers
self.call_js_method("removeDeckLayer", layer_id)
set_deck_layer_visibility(self, layer_id, visible)
¶
Set deck.gl layer visibility.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
layer_id |
str |
Layer identifier. |
required |
visible |
bool |
Whether layer should be visible. |
required |
Source code in anymap_ts/deckgl.py
def set_deck_layer_visibility(self, layer_id: str, visible: bool) -> None:
"""Set deck.gl layer visibility.
Args:
layer_id: Layer identifier.
visible: Whether layer should be visible.
"""
self.call_js_method("setDeckLayerVisibility", layer_id, visible)
keplergl
¶
KeplerGL map widget implementation.
KeplerGL is loaded via CDN since it's React-based and requires complex setup. This implementation provides a Python wrapper with data management capabilities.
KeplerGLMap (MapWidget)
¶
Interactive map widget using KeplerGL.
KeplerGL is a powerful data visualization tool built on top of deck.gl. This class provides a Python interface for adding data and configuring the KeplerGL visualization.
Note: KeplerGL is loaded from CDN due to its React-based architecture.
Examples:
>>> from anymap_ts import KeplerGLMap
>>> import pandas as pd
>>> m = KeplerGLMap()
>>> df = pd.DataFrame({
... 'lat': [37.7749, 37.8044],
... 'lng': [-122.4194, -122.2712],
... 'value': [100, 200]
... })
>>> m.add_data(df, name='points')
>>> m
Source code in anymap_ts/keplergl.py
class KeplerGLMap(MapWidget):
"""Interactive map widget using KeplerGL.
KeplerGL is a powerful data visualization tool built on top of deck.gl.
This class provides a Python interface for adding data and configuring
the KeplerGL visualization.
Note: KeplerGL is loaded from CDN due to its React-based architecture.
Example:
>>> from anymap_ts import KeplerGLMap
>>> import pandas as pd
>>> m = KeplerGLMap()
>>> df = pd.DataFrame({
... 'lat': [37.7749, 37.8044],
... 'lng': [-122.4194, -122.2712],
... 'value': [100, 200]
... })
>>> m.add_data(df, name='points')
>>> m
"""
# ESM module for frontend
_esm = STATIC_DIR / "keplergl.js"
# KeplerGL-specific traits
config = traitlets.Dict({}).tag(sync=True)
datasets = traitlets.Dict({}).tag(sync=True)
read_only = traitlets.Bool(False).tag(sync=True)
show_data_table = traitlets.Bool(True).tag(sync=True)
# Mapbox token for basemaps
mapbox_token = traitlets.Unicode("").tag(sync=True)
def __init__(
self,
center: Tuple[float, float] = (-122.4, 37.8),
zoom: float = 10.0,
width: str = "100%",
height: str = "600px",
config: Optional[Dict] = None,
read_only: bool = False,
show_data_table: bool = True,
mapbox_token: Optional[str] = None,
**kwargs,
):
"""Initialize a KeplerGL map.
Args:
center: Map center as (longitude, latitude).
zoom: Initial zoom level.
width: Widget width as CSS string.
height: Widget height as CSS string.
config: KeplerGL configuration dict.
read_only: Whether the UI is read-only.
show_data_table: Whether to show the data table panel.
mapbox_token: Mapbox access token for basemaps.
**kwargs: Additional widget arguments.
"""
import os
if mapbox_token is None:
mapbox_token = os.environ.get("MAPBOX_TOKEN", "")
super().__init__(
center=list(center),
zoom=zoom,
width=width,
height=height,
config=config or {},
read_only=read_only,
show_data_table=show_data_table,
mapbox_token=mapbox_token,
**kwargs,
)
self.datasets = {}
# -------------------------------------------------------------------------
# Data Methods
# -------------------------------------------------------------------------
def add_data(
self,
data: Any,
name: Optional[str] = None,
) -> None:
"""Add data to the map.
Args:
data: Data to add (DataFrame, GeoDataFrame, dict, or file path).
name: Dataset name/label.
"""
dataset_id = name or f"data_{uuid.uuid4().hex[:8]}"
processed_data = self._process_data(data)
self.datasets = {
**self.datasets,
dataset_id: {
"info": {
"id": dataset_id,
"label": dataset_id,
},
"data": processed_data,
},
}
self.call_js_method(
"addData",
dataId=dataset_id,
data=processed_data,
)
def _process_data(self, data: Any) -> Dict:
"""Process data into KeplerGL format.
Args:
data: Input data.
Returns:
Processed data dict with fields and rows.
"""
# Handle DataFrame
if hasattr(data, "to_dict"):
# Check if it's a GeoDataFrame
if hasattr(data, "geometry"):
# Convert to GeoJSON for geometry columns
geojson = json.loads(data.to_json())
return {
"type": "geojson",
"data": geojson,
}
else:
# Regular DataFrame
fields = []
for col in data.columns:
dtype = str(data[col].dtype)
if "int" in dtype:
field_type = "integer"
elif "float" in dtype:
field_type = "real"
elif "datetime" in dtype:
field_type = "timestamp"
elif "bool" in dtype:
field_type = "boolean"
else:
field_type = "string"
fields.append({"name": col, "type": field_type})
# Convert to list of lists
rows = data.values.tolist()
return {
"fields": fields,
"rows": rows,
}
# Handle dict (assume it's already GeoJSON or processed)
if isinstance(data, dict):
if "type" in data and data["type"] in [
"FeatureCollection",
"Feature",
"Point",
"LineString",
"Polygon",
"MultiPoint",
"MultiLineString",
"MultiPolygon",
]:
return {"type": "geojson", "data": data}
return data
# Handle file path
if isinstance(data, (str, Path)):
path = Path(data)
if path.exists():
if path.suffix.lower() in [".geojson", ".json"]:
with open(path) as f:
geojson = json.load(f)
return {"type": "geojson", "data": geojson}
elif path.suffix.lower() == ".csv":
try:
import pandas as pd
df = pd.read_csv(path)
return self._process_data(df)
except ImportError:
raise ImportError(
"pandas is required to load CSV files. "
"Install with: pip install pandas"
)
return data
def remove_data(self, name: str) -> None:
"""Remove a dataset.
Args:
name: Dataset name to remove.
"""
if name in self.datasets:
datasets = dict(self.datasets)
del datasets[name]
self.datasets = datasets
self.call_js_method("removeData", dataId=name)
# -------------------------------------------------------------------------
# Configuration Methods
# -------------------------------------------------------------------------
def set_config(self, config: Dict) -> None:
"""Set the KeplerGL configuration.
Args:
config: Configuration dict.
"""
self.config = config
self.call_js_method("setConfig", config=config)
def get_config(self) -> Dict:
"""Get the current KeplerGL configuration.
Returns:
Configuration dict.
"""
return self.config
def save_config(self, filepath: Union[str, Path]) -> None:
"""Save configuration to a JSON file.
Args:
filepath: Path to save the configuration.
"""
with open(filepath, "w") as f:
json.dump(self.config, f, indent=2)
def load_config(self, filepath: Union[str, Path]) -> None:
"""Load configuration from a JSON file.
Args:
filepath: Path to the configuration file.
"""
with open(filepath) as f:
config = json.load(f)
self.set_config(config)
# -------------------------------------------------------------------------
# Filter Methods
# -------------------------------------------------------------------------
def add_filter(
self,
data_id: str,
field: str,
filter_type: str = "range",
value: Optional[Any] = None,
) -> None:
"""Add a filter to the visualization.
Args:
data_id: Dataset ID to filter.
field: Field name to filter on.
filter_type: Type of filter ('range', 'select', 'time').
value: Filter value(s).
"""
filter_config = {
"dataId": [data_id],
"name": [field],
"type": filter_type,
}
if value is not None:
filter_config["value"] = value
self.call_js_method("addFilter", filter=filter_config)
# -------------------------------------------------------------------------
# Layer Methods
# -------------------------------------------------------------------------
def add_layer(
self,
layer_type: str,
data_id: str,
columns: Dict[str, str],
label: Optional[str] = None,
color: Optional[List[int]] = None,
vis_config: Optional[Dict] = None,
**kwargs,
) -> None:
"""Add a layer to the visualization.
Args:
layer_type: Layer type ('point', 'arc', 'line', 'hexagon', 'heatmap', etc.).
data_id: Dataset ID for the layer.
columns: Column mapping (e.g., {'lat': 'latitude', 'lng': 'longitude'}).
label: Layer label.
color: Layer color as [r, g, b].
vis_config: Visual configuration.
**kwargs: Additional layer options.
"""
layer_config = {
"type": layer_type,
"config": {
"dataId": data_id,
"label": label or f"{layer_type}_layer",
"columns": columns,
"isVisible": True,
},
}
if color:
layer_config["config"]["color"] = color
if vis_config:
layer_config["config"]["visConfig"] = vis_config
layer_config["config"].update(kwargs)
self.call_js_method("addLayer", layer=layer_config)
# -------------------------------------------------------------------------
# View Methods
# -------------------------------------------------------------------------
def fly_to(
self,
lng: float,
lat: float,
zoom: Optional[float] = None,
) -> None:
"""Fly to a location.
Args:
lng: Target longitude.
lat: Target latitude.
zoom: Target zoom level.
"""
self.center = [lng, lat]
if zoom is not None:
self.zoom = zoom
self.call_js_method("flyTo", lng=lng, lat=lat, zoom=zoom or self.zoom)
# -------------------------------------------------------------------------
# HTML Export
# -------------------------------------------------------------------------
def _generate_html_template(self) -> str:
"""Generate standalone HTML for KeplerGL."""
template_path = Path(__file__).parent / "templates" / "keplergl.html"
if template_path.exists():
template = template_path.read_text(encoding="utf-8")
else:
template = self._get_default_template()
state = {
"center": self.center,
"zoom": self.zoom,
"config": self.config,
"datasets": self.datasets,
"read_only": self.read_only,
"mapbox_token": self.mapbox_token,
"width": self.width,
"height": self.height,
"js_calls": self._js_calls,
}
template = template.replace("{{state}}", json.dumps(state, indent=2))
return template
def _get_default_template(self) -> str:
"""Get default HTML template."""
return """<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>KeplerGL Map</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
html, body { height: 100%; }
#app { position: absolute; top: 0; bottom: 0; width: 100%; }
</style>
</head>
<body>
<div id="app"></div>
<script>
const state = {{state}};
// KeplerGL requires React/Redux setup - simplified placeholder
document.getElementById('app').innerHTML = '<p>KeplerGL visualization requires full React setup. Use Jupyter widget for interactive visualization.</p>';
</script>
</body>
</html>"""
def _repr_html_(self) -> str:
"""Return HTML representation for Jupyter (uses iframe with CDN KeplerGL)."""
state = {
"center": self.center,
"zoom": self.zoom,
"config": self.config,
"datasets": self.datasets,
"mapbox_token": self.mapbox_token,
}
html = f"""
<iframe
srcdoc='
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<script src="https://unpkg.com/react@16.8.4/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom@16.8.4/umd/react-dom.production.min.js"></script>
<script src="https://unpkg.com/kepler.gl@3.0.0/umd/keplergl.min.js"></script>
<link href="https://unpkg.com/kepler.gl@3.0.0/umd/keplergl.min.css" rel="stylesheet" />
<style>
body {{ margin: 0; padding: 0; overflow: hidden; }}
#app {{ width: 100vw; height: 100vh; }}
</style>
</head>
<body>
<div id="app"></div>
<script>
const state = {json.dumps(state)};
// KeplerGL requires complex React setup
document.getElementById("app").innerHTML = "KeplerGL widget - use anywidget interface for full interactivity";
</script>
</body>
</html>
'
width="{self.width}"
height="{self.height}"
frameborder="0"
></iframe>
"""
return html
__init__(self, center=(-122.4, 37.8), zoom=10.0, width='100%', height='600px', config=None, read_only=False, show_data_table=True, mapbox_token=None, **kwargs)
special
¶
Initialize a KeplerGL map.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
center |
Tuple[float, float] |
Map center as (longitude, latitude). |
(-122.4, 37.8) |
zoom |
float |
Initial zoom level. |
10.0 |
width |
str |
Widget width as CSS string. |
'100%' |
height |
str |
Widget height as CSS string. |
'600px' |
config |
Optional[Dict] |
KeplerGL configuration dict. |
None |
read_only |
bool |
Whether the UI is read-only. |
False |
show_data_table |
bool |
Whether to show the data table panel. |
True |
mapbox_token |
Optional[str] |
Mapbox access token for basemaps. |
None |
**kwargs |
Additional widget arguments. |
{} |
Source code in anymap_ts/keplergl.py
def __init__(
self,
center: Tuple[float, float] = (-122.4, 37.8),
zoom: float = 10.0,
width: str = "100%",
height: str = "600px",
config: Optional[Dict] = None,
read_only: bool = False,
show_data_table: bool = True,
mapbox_token: Optional[str] = None,
**kwargs,
):
"""Initialize a KeplerGL map.
Args:
center: Map center as (longitude, latitude).
zoom: Initial zoom level.
width: Widget width as CSS string.
height: Widget height as CSS string.
config: KeplerGL configuration dict.
read_only: Whether the UI is read-only.
show_data_table: Whether to show the data table panel.
mapbox_token: Mapbox access token for basemaps.
**kwargs: Additional widget arguments.
"""
import os
if mapbox_token is None:
mapbox_token = os.environ.get("MAPBOX_TOKEN", "")
super().__init__(
center=list(center),
zoom=zoom,
width=width,
height=height,
config=config or {},
read_only=read_only,
show_data_table=show_data_table,
mapbox_token=mapbox_token,
**kwargs,
)
self.datasets = {}
add_data(self, data, name=None)
¶
Add data to the map.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data |
Any |
Data to add (DataFrame, GeoDataFrame, dict, or file path). |
required |
name |
Optional[str] |
Dataset name/label. |
None |
Source code in anymap_ts/keplergl.py
def add_data(
self,
data: Any,
name: Optional[str] = None,
) -> None:
"""Add data to the map.
Args:
data: Data to add (DataFrame, GeoDataFrame, dict, or file path).
name: Dataset name/label.
"""
dataset_id = name or f"data_{uuid.uuid4().hex[:8]}"
processed_data = self._process_data(data)
self.datasets = {
**self.datasets,
dataset_id: {
"info": {
"id": dataset_id,
"label": dataset_id,
},
"data": processed_data,
},
}
self.call_js_method(
"addData",
dataId=dataset_id,
data=processed_data,
)
add_filter(self, data_id, field, filter_type='range', value=None)
¶
Add a filter to the visualization.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data_id |
str |
Dataset ID to filter. |
required |
field |
str |
Field name to filter on. |
required |
filter_type |
str |
Type of filter ('range', 'select', 'time'). |
'range' |
value |
Optional[Any] |
Filter value(s). |
None |
Source code in anymap_ts/keplergl.py
def add_filter(
self,
data_id: str,
field: str,
filter_type: str = "range",
value: Optional[Any] = None,
) -> None:
"""Add a filter to the visualization.
Args:
data_id: Dataset ID to filter.
field: Field name to filter on.
filter_type: Type of filter ('range', 'select', 'time').
value: Filter value(s).
"""
filter_config = {
"dataId": [data_id],
"name": [field],
"type": filter_type,
}
if value is not None:
filter_config["value"] = value
self.call_js_method("addFilter", filter=filter_config)
add_layer(self, layer_type, data_id, columns, label=None, color=None, vis_config=None, **kwargs)
¶
Add a layer to the visualization.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
layer_type |
str |
Layer type ('point', 'arc', 'line', 'hexagon', 'heatmap', etc.). |
required |
data_id |
str |
Dataset ID for the layer. |
required |
columns |
Dict[str, str] |
Column mapping (e.g., {'lat': 'latitude', 'lng': 'longitude'}). |
required |
label |
Optional[str] |
Layer label. |
None |
color |
Optional[List[int]] |
Layer color as [r, g, b]. |
None |
vis_config |
Optional[Dict] |
Visual configuration. |
None |
**kwargs |
Additional layer options. |
{} |
Source code in anymap_ts/keplergl.py
def add_layer(
self,
layer_type: str,
data_id: str,
columns: Dict[str, str],
label: Optional[str] = None,
color: Optional[List[int]] = None,
vis_config: Optional[Dict] = None,
**kwargs,
) -> None:
"""Add a layer to the visualization.
Args:
layer_type: Layer type ('point', 'arc', 'line', 'hexagon', 'heatmap', etc.).
data_id: Dataset ID for the layer.
columns: Column mapping (e.g., {'lat': 'latitude', 'lng': 'longitude'}).
label: Layer label.
color: Layer color as [r, g, b].
vis_config: Visual configuration.
**kwargs: Additional layer options.
"""
layer_config = {
"type": layer_type,
"config": {
"dataId": data_id,
"label": label or f"{layer_type}_layer",
"columns": columns,
"isVisible": True,
},
}
if color:
layer_config["config"]["color"] = color
if vis_config:
layer_config["config"]["visConfig"] = vis_config
layer_config["config"].update(kwargs)
self.call_js_method("addLayer", layer=layer_config)
fly_to(self, lng, lat, zoom=None)
¶
Fly to a location.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
lng |
float |
Target longitude. |
required |
lat |
float |
Target latitude. |
required |
zoom |
Optional[float] |
Target zoom level. |
None |
Source code in anymap_ts/keplergl.py
def fly_to(
self,
lng: float,
lat: float,
zoom: Optional[float] = None,
) -> None:
"""Fly to a location.
Args:
lng: Target longitude.
lat: Target latitude.
zoom: Target zoom level.
"""
self.center = [lng, lat]
if zoom is not None:
self.zoom = zoom
self.call_js_method("flyTo", lng=lng, lat=lat, zoom=zoom or self.zoom)
get_config(self)
¶
Get the current KeplerGL configuration.
Returns:
| Type | Description |
|---|---|
Dict |
Configuration dict. |
Source code in anymap_ts/keplergl.py
def get_config(self) -> Dict:
"""Get the current KeplerGL configuration.
Returns:
Configuration dict.
"""
return self.config
load_config(self, filepath)
¶
Load configuration from a JSON file.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
filepath |
Union[str, Path] |
Path to the configuration file. |
required |
Source code in anymap_ts/keplergl.py
def load_config(self, filepath: Union[str, Path]) -> None:
"""Load configuration from a JSON file.
Args:
filepath: Path to the configuration file.
"""
with open(filepath) as f:
config = json.load(f)
self.set_config(config)
remove_data(self, name)
¶
Remove a dataset.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
name |
str |
Dataset name to remove. |
required |
Source code in anymap_ts/keplergl.py
def remove_data(self, name: str) -> None:
"""Remove a dataset.
Args:
name: Dataset name to remove.
"""
if name in self.datasets:
datasets = dict(self.datasets)
del datasets[name]
self.datasets = datasets
self.call_js_method("removeData", dataId=name)
save_config(self, filepath)
¶
Save configuration to a JSON file.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
filepath |
Union[str, Path] |
Path to save the configuration. |
required |
Source code in anymap_ts/keplergl.py
def save_config(self, filepath: Union[str, Path]) -> None:
"""Save configuration to a JSON file.
Args:
filepath: Path to save the configuration.
"""
with open(filepath, "w") as f:
json.dump(self.config, f, indent=2)
set_config(self, config)
¶
Set the KeplerGL configuration.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
config |
Dict |
Configuration dict. |
required |
Source code in anymap_ts/keplergl.py
def set_config(self, config: Dict) -> None:
"""Set the KeplerGL configuration.
Args:
config: Configuration dict.
"""
self.config = config
self.call_js_method("setConfig", config=config)
leaflet
¶
Leaflet map widget implementation.
LeafletMap (MapWidget)
¶
Interactive map widget using Leaflet.
This class provides a Python interface to Leaflet maps with full bidirectional communication through anywidget.
Note
Leaflet uses [lat, lng] order internally, but this class accepts [lng, lat] for consistency with other map libraries.
Examples:
>>> from anymap_ts import LeafletMap
>>> m = LeafletMap(center=[-122.4, 37.8], zoom=10)
>>> m.add_basemap("OpenStreetMap")
>>> m
Source code in anymap_ts/leaflet.py
class LeafletMap(MapWidget):
"""Interactive map widget using Leaflet.
This class provides a Python interface to Leaflet maps with
full bidirectional communication through anywidget.
Note:
Leaflet uses [lat, lng] order internally, but this class
accepts [lng, lat] for consistency with other map libraries.
Example:
>>> from anymap_ts import LeafletMap
>>> m = LeafletMap(center=[-122.4, 37.8], zoom=10)
>>> m.add_basemap("OpenStreetMap")
>>> m
"""
# ESM module for frontend
_esm = STATIC_DIR / "leaflet.js"
_css = STATIC_DIR / "leaflet.css"
# Layer tracking
_layer_dict = traitlets.Dict({}).tag(sync=True)
def __init__(
self,
center: Tuple[float, float] = (0.0, 0.0),
zoom: float = 2.0,
width: str = "100%",
height: str = "600px",
controls: Optional[Dict[str, Any]] = None,
**kwargs,
):
"""Initialize a Leaflet map.
Args:
center: Map center as (longitude, latitude).
zoom: Initial zoom level.
width: Map width as CSS string.
height: Map height as CSS string.
controls: Dict of controls to add (e.g., {"zoom": True}).
**kwargs: Additional widget arguments.
"""
super().__init__(
center=list(center),
zoom=zoom,
width=width,
height=height,
style="", # Leaflet doesn't use style URLs
**kwargs,
)
# Initialize layer dictionary
self._layer_dict = {"Background": []}
# Add default controls
if controls is None:
controls = {"zoom": True, "scale": True}
for control_name, config in controls.items():
if config:
self.add_control(
control_name, **(config if isinstance(config, dict) else {})
)
# -------------------------------------------------------------------------
# Basemap Methods
# -------------------------------------------------------------------------
def add_basemap(
self,
basemap: str = "OpenStreetMap",
attribution: Optional[str] = None,
**kwargs,
) -> None:
"""Add a basemap layer.
Args:
basemap: Name of basemap provider (e.g., "OpenStreetMap", "CartoDB.Positron").
attribution: Custom attribution text.
**kwargs: Additional options.
"""
try:
url, default_attribution = get_basemap_url(basemap)
except (ValueError, KeyError):
url = basemap
default_attribution = ""
self.call_js_method(
"addBasemap",
url,
attribution=attribution or default_attribution,
name=basemap,
**kwargs,
)
# Track in layer dict
basemaps = self._layer_dict.get("Basemaps", [])
if basemap not in basemaps:
self._layer_dict = {
**self._layer_dict,
"Basemaps": basemaps + [basemap],
}
# -------------------------------------------------------------------------
# Vector Data Methods
# -------------------------------------------------------------------------
def add_vector(
self,
data: Any,
style: Optional[Dict] = None,
name: Optional[str] = None,
fit_bounds: bool = True,
**kwargs,
) -> None:
"""Add vector data to the map.
Supports GeoJSON, GeoDataFrame, or file paths to vector formats.
Args:
data: GeoJSON dict, GeoDataFrame, or path to vector file.
style: Leaflet style properties.
name: Layer name.
fit_bounds: Whether to fit map to data bounds.
**kwargs: Additional layer options.
"""
geojson = to_geojson(data)
# Handle URL data
if geojson.get("type") == "url":
self.add_geojson(
geojson["url"],
style=style,
name=name,
fit_bounds=fit_bounds,
**kwargs,
)
return
layer_id = name or f"vector-{len(self._layers)}"
# Get default style if not provided
if style is None:
layer_type = _infer_leaflet_type(geojson)
style = _get_default_style(layer_type)
# Get bounds
bounds = get_bounds(data) if fit_bounds else None
# Call JavaScript
self.call_js_method(
"addGeoJSON",
data=geojson,
name=layer_id,
style=style,
fitBounds=fit_bounds,
bounds=bounds,
**kwargs,
)
# Track layer
self._layers = {
**self._layers,
layer_id: {
"id": layer_id,
"type": "geojson",
"style": style,
},
}
def add_geojson(
self,
data: Union[str, Dict],
style: Optional[Dict] = None,
name: Optional[str] = None,
fit_bounds: bool = True,
**kwargs,
) -> None:
"""Add GeoJSON data to the map.
Args:
data: GeoJSON dict or URL to GeoJSON file.
style: Leaflet style properties.
name: Layer name.
fit_bounds: Whether to fit map to data bounds.
**kwargs: Additional layer options.
"""
self.add_vector(
data,
style=style,
name=name,
fit_bounds=fit_bounds,
**kwargs,
)
# -------------------------------------------------------------------------
# Raster Data Methods
# -------------------------------------------------------------------------
def add_tile_layer(
self,
url: str,
name: Optional[str] = None,
attribution: str = "",
min_zoom: int = 0,
max_zoom: int = 22,
opacity: float = 1.0,
**kwargs,
) -> None:
"""Add an XYZ tile layer.
Args:
url: Tile URL template with {x}, {y}, {z} placeholders.
name: Layer name.
attribution: Attribution text.
min_zoom: Minimum zoom level.
max_zoom: Maximum zoom level.
opacity: Layer opacity.
**kwargs: Additional options.
"""
layer_id = name or f"tiles-{len(self._layers)}"
self.call_js_method(
"addTileLayer",
url,
name=layer_id,
attribution=attribution,
minZoom=min_zoom,
maxZoom=max_zoom,
opacity=opacity,
**kwargs,
)
# Track layer
self._layers = {
**self._layers,
layer_id: {
"id": layer_id,
"type": "tile",
},
}
# -------------------------------------------------------------------------
# Layer Management
# -------------------------------------------------------------------------
def remove_layer(self, layer_id: str) -> None:
"""Remove a layer from the map.
Args:
layer_id: Layer identifier to remove.
"""
if layer_id in self._layers:
layers = dict(self._layers)
del layers[layer_id]
self._layers = layers
self.call_js_method("removeLayer", layer_id)
def set_visibility(self, layer_id: str, visible: bool) -> None:
"""Set layer visibility.
Args:
layer_id: Layer identifier.
visible: Whether layer should be visible.
"""
self.call_js_method("setVisibility", layer_id, visible)
def set_opacity(self, layer_id: str, opacity: float) -> None:
"""Set layer opacity.
Args:
layer_id: Layer identifier.
opacity: Opacity value between 0 and 1.
"""
self.call_js_method("setOpacity", layer_id, opacity)
# -------------------------------------------------------------------------
# Controls
# -------------------------------------------------------------------------
def add_control(
self,
control_type: str,
position: str = "topright",
**kwargs,
) -> None:
"""Add a map control.
Args:
control_type: Type of control ('zoom', 'scale', 'attribution', 'layers').
position: Control position ('topleft', 'topright', 'bottomleft', 'bottomright').
**kwargs: Control-specific options.
"""
# Convert position format
position_map = {
"top-left": "topleft",
"top-right": "topright",
"bottom-left": "bottomleft",
"bottom-right": "bottomright",
}
pos = position_map.get(position, position)
self.call_js_method("addControl", control_type, position=pos, **kwargs)
self._controls = {
**self._controls,
control_type: {"type": control_type, "position": pos, **kwargs},
}
def remove_control(self, control_type: str) -> None:
"""Remove a map control.
Args:
control_type: Type of control to remove.
"""
self.call_js_method("removeControl", control_type)
if control_type in self._controls:
controls = dict(self._controls)
del controls[control_type]
self._controls = controls
def add_layer_control(
self,
position: str = "topright",
collapsed: bool = True,
) -> None:
"""Add a layer control for toggling layer visibility.
Args:
position: Control position.
collapsed: Whether control starts collapsed.
"""
self.add_control("layers", position=position, collapsed=collapsed)
# -------------------------------------------------------------------------
# Markers
# -------------------------------------------------------------------------
def add_marker(
self,
lng: float,
lat: float,
popup: Optional[str] = None,
marker_id: Optional[str] = None,
) -> None:
"""Add a marker to the map.
Args:
lng: Longitude.
lat: Latitude.
popup: HTML content for popup.
marker_id: Unique marker ID.
"""
self.call_js_method("addMarker", lng, lat, popup=popup, id=marker_id)
def remove_marker(self, marker_id: str) -> None:
"""Remove a marker from the map.
Args:
marker_id: Marker ID to remove.
"""
self.call_js_method("removeMarker", marker_id)
# -------------------------------------------------------------------------
# HTML Export
# -------------------------------------------------------------------------
def _generate_html_template(self) -> str:
"""Generate standalone HTML for the map."""
template_path = Path(__file__).parent / "templates" / "leaflet.html"
if template_path.exists():
template = template_path.read_text(encoding="utf-8")
else:
template = self._get_default_template()
# Serialize state
state = {
"center": self.center,
"zoom": self.zoom,
"width": self.width,
"height": self.height,
"layers": self._layers,
"controls": self._controls,
"js_calls": self._js_calls,
}
template = template.replace("{{state}}", json.dumps(state, indent=2))
return template
def _get_default_template(self) -> str:
"""Get default HTML template."""
return """<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>{{title}}</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" />
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
<style>
body { margin: 0; padding: 0; }
#map { position: absolute; top: 0; bottom: 0; width: 100%; }
</style>
</head>
<body>
<div id="map"></div>
<script>
const state = {{state}};
// Note: Leaflet uses [lat, lng], but we store [lng, lat]
const map = L.map('map').setView([state.center[1], state.center[0]], state.zoom);
// Replay JS calls
for (const call of state.js_calls || []) {
try {
executeMethod(call.method, call.args, call.kwargs);
} catch (e) {
console.error('Error executing', call.method, e);
}
}
function executeMethod(method, args, kwargs) {
switch (method) {
case 'addBasemap':
case 'addTileLayer':
const url = args[0];
L.tileLayer(url, {
attribution: kwargs.attribution || '',
maxZoom: kwargs.maxZoom || 22,
minZoom: kwargs.minZoom || 0,
opacity: kwargs.opacity || 1
}).addTo(map);
break;
case 'addGeoJSON':
const geojson = kwargs.data;
const style = kwargs.style || {
color: '#3388ff',
weight: 2,
opacity: 0.8,
fillOpacity: 0.5
};
const layer = L.geoJSON(geojson, {
style: style,
pointToLayer: (feature, latlng) => L.circleMarker(latlng, style)
}).addTo(map);
if (kwargs.fitBounds) {
map.fitBounds(layer.getBounds(), { padding: [50, 50] });
}
break;
case 'addControl':
const controlType = args[0];
const position = kwargs.position || 'topright';
if (controlType === 'zoom' || controlType === 'navigation') {
L.control.zoom({ position }).addTo(map);
} else if (controlType === 'scale') {
L.control.scale({ position, imperial: false }).addTo(map);
}
break;
case 'addMarker':
const [lng, lat] = args;
const marker = L.marker([lat, lng]).addTo(map);
if (kwargs.popup) {
marker.bindPopup(kwargs.popup);
}
break;
case 'flyTo':
map.flyTo([args[1], args[0]], kwargs.zoom || map.getZoom(), {
duration: (kwargs.duration || 2000) / 1000
});
break;
case 'fitBounds':
const bounds = args[0];
map.fitBounds([
[bounds[1], bounds[0]],
[bounds[3], bounds[2]]
], { padding: [kwargs.padding || 50, kwargs.padding || 50] });
break;
default:
console.log('Unknown method:', method);
}
}
</script>
</body>
</html>"""
__init__(self, center=(0.0, 0.0), zoom=2.0, width='100%', height='600px', controls=None, **kwargs)
special
¶
Initialize a Leaflet map.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
center |
Tuple[float, float] |
Map center as (longitude, latitude). |
(0.0, 0.0) |
zoom |
float |
Initial zoom level. |
2.0 |
width |
str |
Map width as CSS string. |
'100%' |
height |
str |
Map height as CSS string. |
'600px' |
controls |
Optional[Dict[str, Any]] |
Dict of controls to add (e.g., {"zoom": True}). |
None |
**kwargs |
Additional widget arguments. |
{} |
Source code in anymap_ts/leaflet.py
def __init__(
self,
center: Tuple[float, float] = (0.0, 0.0),
zoom: float = 2.0,
width: str = "100%",
height: str = "600px",
controls: Optional[Dict[str, Any]] = None,
**kwargs,
):
"""Initialize a Leaflet map.
Args:
center: Map center as (longitude, latitude).
zoom: Initial zoom level.
width: Map width as CSS string.
height: Map height as CSS string.
controls: Dict of controls to add (e.g., {"zoom": True}).
**kwargs: Additional widget arguments.
"""
super().__init__(
center=list(center),
zoom=zoom,
width=width,
height=height,
style="", # Leaflet doesn't use style URLs
**kwargs,
)
# Initialize layer dictionary
self._layer_dict = {"Background": []}
# Add default controls
if controls is None:
controls = {"zoom": True, "scale": True}
for control_name, config in controls.items():
if config:
self.add_control(
control_name, **(config if isinstance(config, dict) else {})
)
add_basemap(self, basemap='OpenStreetMap', attribution=None, **kwargs)
¶
Add a basemap layer.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
basemap |
str |
Name of basemap provider (e.g., "OpenStreetMap", "CartoDB.Positron"). |
'OpenStreetMap' |
attribution |
Optional[str] |
Custom attribution text. |
None |
**kwargs |
Additional options. |
{} |
Source code in anymap_ts/leaflet.py
def add_basemap(
self,
basemap: str = "OpenStreetMap",
attribution: Optional[str] = None,
**kwargs,
) -> None:
"""Add a basemap layer.
Args:
basemap: Name of basemap provider (e.g., "OpenStreetMap", "CartoDB.Positron").
attribution: Custom attribution text.
**kwargs: Additional options.
"""
try:
url, default_attribution = get_basemap_url(basemap)
except (ValueError, KeyError):
url = basemap
default_attribution = ""
self.call_js_method(
"addBasemap",
url,
attribution=attribution or default_attribution,
name=basemap,
**kwargs,
)
# Track in layer dict
basemaps = self._layer_dict.get("Basemaps", [])
if basemap not in basemaps:
self._layer_dict = {
**self._layer_dict,
"Basemaps": basemaps + [basemap],
}
add_control(self, control_type, position='topright', **kwargs)
¶
Add a map control.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
control_type |
str |
Type of control ('zoom', 'scale', 'attribution', 'layers'). |
required |
position |
str |
Control position ('topleft', 'topright', 'bottomleft', 'bottomright'). |
'topright' |
**kwargs |
Control-specific options. |
{} |
Source code in anymap_ts/leaflet.py
def add_control(
self,
control_type: str,
position: str = "topright",
**kwargs,
) -> None:
"""Add a map control.
Args:
control_type: Type of control ('zoom', 'scale', 'attribution', 'layers').
position: Control position ('topleft', 'topright', 'bottomleft', 'bottomright').
**kwargs: Control-specific options.
"""
# Convert position format
position_map = {
"top-left": "topleft",
"top-right": "topright",
"bottom-left": "bottomleft",
"bottom-right": "bottomright",
}
pos = position_map.get(position, position)
self.call_js_method("addControl", control_type, position=pos, **kwargs)
self._controls = {
**self._controls,
control_type: {"type": control_type, "position": pos, **kwargs},
}
add_geojson(self, data, style=None, name=None, fit_bounds=True, **kwargs)
¶
Add GeoJSON data to the map.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data |
Union[str, Dict] |
GeoJSON dict or URL to GeoJSON file. |
required |
style |
Optional[Dict] |
Leaflet style properties. |
None |
name |
Optional[str] |
Layer name. |
None |
fit_bounds |
bool |
Whether to fit map to data bounds. |
True |
**kwargs |
Additional layer options. |
{} |
Source code in anymap_ts/leaflet.py
def add_geojson(
self,
data: Union[str, Dict],
style: Optional[Dict] = None,
name: Optional[str] = None,
fit_bounds: bool = True,
**kwargs,
) -> None:
"""Add GeoJSON data to the map.
Args:
data: GeoJSON dict or URL to GeoJSON file.
style: Leaflet style properties.
name: Layer name.
fit_bounds: Whether to fit map to data bounds.
**kwargs: Additional layer options.
"""
self.add_vector(
data,
style=style,
name=name,
fit_bounds=fit_bounds,
**kwargs,
)
add_layer_control(self, position='topright', collapsed=True)
¶
Add a layer control for toggling layer visibility.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
position |
str |
Control position. |
'topright' |
collapsed |
bool |
Whether control starts collapsed. |
True |
Source code in anymap_ts/leaflet.py
def add_layer_control(
self,
position: str = "topright",
collapsed: bool = True,
) -> None:
"""Add a layer control for toggling layer visibility.
Args:
position: Control position.
collapsed: Whether control starts collapsed.
"""
self.add_control("layers", position=position, collapsed=collapsed)
add_marker(self, lng, lat, popup=None, marker_id=None)
¶
Add a marker to the map.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
lng |
float |
Longitude. |
required |
lat |
float |
Latitude. |
required |
popup |
Optional[str] |
HTML content for popup. |
None |
marker_id |
Optional[str] |
Unique marker ID. |
None |
Source code in anymap_ts/leaflet.py
def add_marker(
self,
lng: float,
lat: float,
popup: Optional[str] = None,
marker_id: Optional[str] = None,
) -> None:
"""Add a marker to the map.
Args:
lng: Longitude.
lat: Latitude.
popup: HTML content for popup.
marker_id: Unique marker ID.
"""
self.call_js_method("addMarker", lng, lat, popup=popup, id=marker_id)
add_tile_layer(self, url, name=None, attribution='', min_zoom=0, max_zoom=22, opacity=1.0, **kwargs)
¶
Add an XYZ tile layer.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
url |
str |
Tile URL template with {x}, {y}, {z} placeholders. |
required |
name |
Optional[str] |
Layer name. |
None |
attribution |
str |
Attribution text. |
'' |
min_zoom |
int |
Minimum zoom level. |
0 |
max_zoom |
int |
Maximum zoom level. |
22 |
opacity |
float |
Layer opacity. |
1.0 |
**kwargs |
Additional options. |
{} |
Source code in anymap_ts/leaflet.py
def add_tile_layer(
self,
url: str,
name: Optional[str] = None,
attribution: str = "",
min_zoom: int = 0,
max_zoom: int = 22,
opacity: float = 1.0,
**kwargs,
) -> None:
"""Add an XYZ tile layer.
Args:
url: Tile URL template with {x}, {y}, {z} placeholders.
name: Layer name.
attribution: Attribution text.
min_zoom: Minimum zoom level.
max_zoom: Maximum zoom level.
opacity: Layer opacity.
**kwargs: Additional options.
"""
layer_id = name or f"tiles-{len(self._layers)}"
self.call_js_method(
"addTileLayer",
url,
name=layer_id,
attribution=attribution,
minZoom=min_zoom,
maxZoom=max_zoom,
opacity=opacity,
**kwargs,
)
# Track layer
self._layers = {
**self._layers,
layer_id: {
"id": layer_id,
"type": "tile",
},
}
add_vector(self, data, style=None, name=None, fit_bounds=True, **kwargs)
¶
Add vector data to the map.
Supports GeoJSON, GeoDataFrame, or file paths to vector formats.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data |
Any |
GeoJSON dict, GeoDataFrame, or path to vector file. |
required |
style |
Optional[Dict] |
Leaflet style properties. |
None |
name |
Optional[str] |
Layer name. |
None |
fit_bounds |
bool |
Whether to fit map to data bounds. |
True |
**kwargs |
Additional layer options. |
{} |
Source code in anymap_ts/leaflet.py
def add_vector(
self,
data: Any,
style: Optional[Dict] = None,
name: Optional[str] = None,
fit_bounds: bool = True,
**kwargs,
) -> None:
"""Add vector data to the map.
Supports GeoJSON, GeoDataFrame, or file paths to vector formats.
Args:
data: GeoJSON dict, GeoDataFrame, or path to vector file.
style: Leaflet style properties.
name: Layer name.
fit_bounds: Whether to fit map to data bounds.
**kwargs: Additional layer options.
"""
geojson = to_geojson(data)
# Handle URL data
if geojson.get("type") == "url":
self.add_geojson(
geojson["url"],
style=style,
name=name,
fit_bounds=fit_bounds,
**kwargs,
)
return
layer_id = name or f"vector-{len(self._layers)}"
# Get default style if not provided
if style is None:
layer_type = _infer_leaflet_type(geojson)
style = _get_default_style(layer_type)
# Get bounds
bounds = get_bounds(data) if fit_bounds else None
# Call JavaScript
self.call_js_method(
"addGeoJSON",
data=geojson,
name=layer_id,
style=style,
fitBounds=fit_bounds,
bounds=bounds,
**kwargs,
)
# Track layer
self._layers = {
**self._layers,
layer_id: {
"id": layer_id,
"type": "geojson",
"style": style,
},
}
remove_control(self, control_type)
¶
Remove a map control.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
control_type |
str |
Type of control to remove. |
required |
Source code in anymap_ts/leaflet.py
def remove_control(self, control_type: str) -> None:
"""Remove a map control.
Args:
control_type: Type of control to remove.
"""
self.call_js_method("removeControl", control_type)
if control_type in self._controls:
controls = dict(self._controls)
del controls[control_type]
self._controls = controls
remove_layer(self, layer_id)
¶
Remove a layer from the map.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
layer_id |
str |
Layer identifier to remove. |
required |
Source code in anymap_ts/leaflet.py
def remove_layer(self, layer_id: str) -> None:
"""Remove a layer from the map.
Args:
layer_id: Layer identifier to remove.
"""
if layer_id in self._layers:
layers = dict(self._layers)
del layers[layer_id]
self._layers = layers
self.call_js_method("removeLayer", layer_id)
remove_marker(self, marker_id)
¶
Remove a marker from the map.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
marker_id |
str |
Marker ID to remove. |
required |
Source code in anymap_ts/leaflet.py
def remove_marker(self, marker_id: str) -> None:
"""Remove a marker from the map.
Args:
marker_id: Marker ID to remove.
"""
self.call_js_method("removeMarker", marker_id)
set_opacity(self, layer_id, opacity)
¶
Set layer opacity.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
layer_id |
str |
Layer identifier. |
required |
opacity |
float |
Opacity value between 0 and 1. |
required |
Source code in anymap_ts/leaflet.py
def set_opacity(self, layer_id: str, opacity: float) -> None:
"""Set layer opacity.
Args:
layer_id: Layer identifier.
opacity: Opacity value between 0 and 1.
"""
self.call_js_method("setOpacity", layer_id, opacity)
set_visibility(self, layer_id, visible)
¶
Set layer visibility.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
layer_id |
str |
Layer identifier. |
required |
visible |
bool |
Whether layer should be visible. |
required |
Source code in anymap_ts/leaflet.py
def set_visibility(self, layer_id: str, visible: bool) -> None:
"""Set layer visibility.
Args:
layer_id: Layer identifier.
visible: Whether layer should be visible.
"""
self.call_js_method("setVisibility", layer_id, visible)
mapbox
¶
Mapbox GL JS map widget implementation.
MapboxMap (MapWidget)
¶
Interactive map widget using Mapbox GL JS.
This class provides a Python interface to Mapbox GL JS maps with full bidirectional communication through anywidget.
Note
Requires a Mapbox access token. Set via MAPBOX_TOKEN environment variable or pass directly to the constructor.
Examples:
>>> from anymap_ts import MapboxMap
>>> m = MapboxMap(center=[-122.4, 37.8], zoom=10)
>>> m.add_basemap("mapbox://styles/mapbox/streets-v12")
>>> m
Source code in anymap_ts/mapbox.py
class MapboxMap(MapWidget):
"""Interactive map widget using Mapbox GL JS.
This class provides a Python interface to Mapbox GL JS maps with
full bidirectional communication through anywidget.
Note:
Requires a Mapbox access token. Set via MAPBOX_TOKEN environment
variable or pass directly to the constructor.
Example:
>>> from anymap_ts import MapboxMap
>>> m = MapboxMap(center=[-122.4, 37.8], zoom=10)
>>> m.add_basemap("mapbox://styles/mapbox/streets-v12")
>>> m
"""
# ESM module for frontend
_esm = STATIC_DIR / "mapbox.js"
_css = STATIC_DIR / "mapbox.css"
# Mapbox-specific traits
access_token = traitlets.Unicode("").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)
double_click_zoom = traitlets.Bool(True).tag(sync=True)
# Layer tracking
_layer_dict = traitlets.Dict({}).tag(sync=True)
def __init__(
self,
center: Tuple[float, float] = (0.0, 0.0),
zoom: float = 2.0,
width: str = "100%",
height: str = "600px",
style: str = "mapbox://styles/mapbox/streets-v12",
bearing: float = 0.0,
pitch: float = 0.0,
access_token: Optional[str] = None,
controls: Optional[Dict[str, Any]] = None,
**kwargs,
):
"""Initialize a Mapbox map.
Args:
center: Map center as (longitude, latitude).
zoom: Initial zoom level.
width: Map width as CSS string.
height: Map height as CSS string.
style: Mapbox style URL (e.g., "mapbox://styles/mapbox/streets-v12").
bearing: Map bearing in degrees.
pitch: Map pitch in degrees.
access_token: Mapbox access token. If None, reads from MAPBOX_TOKEN env var.
controls: Dict of controls to add (e.g., {"navigation": True}).
**kwargs: Additional widget arguments.
"""
# Get access token
token = access_token or get_mapbox_token()
if not token:
print(
"Warning: No Mapbox access token provided. "
"Set MAPBOX_TOKEN environment variable or pass access_token parameter."
)
super().__init__(
center=list(center),
zoom=zoom,
width=width,
height=height,
style=style,
bearing=bearing,
pitch=pitch,
access_token=token,
**kwargs,
)
# Initialize layer dictionary
self._layer_dict = {"Background": []}
# Add default controls
if controls is None:
controls = {"navigation": True, "fullscreen": True}
for control_name, config in controls.items():
if config:
self.add_control(
control_name, **(config if isinstance(config, dict) else {})
)
def set_access_token(self, token: str) -> None:
"""Set the Mapbox access token.
Args:
token: Mapbox access token.
"""
self.access_token = token
# -------------------------------------------------------------------------
# Basemap Methods
# -------------------------------------------------------------------------
def add_basemap(
self,
basemap: str = "mapbox://styles/mapbox/streets-v12",
attribution: Optional[str] = None,
**kwargs,
) -> None:
"""Add a basemap layer.
For Mapbox styles, use the style URL format:
- "mapbox://styles/mapbox/streets-v12"
- "mapbox://styles/mapbox/satellite-v9"
- "mapbox://styles/mapbox/satellite-streets-v12"
- "mapbox://styles/mapbox/light-v11"
- "mapbox://styles/mapbox/dark-v11"
- "mapbox://styles/mapbox/outdoors-v12"
Or use XYZ tile URLs for custom basemaps.
Args:
basemap: Mapbox style URL or XYZ tile URL.
attribution: Custom attribution text.
**kwargs: Additional options.
"""
# If it's a Mapbox style URL, set it as the map style
if basemap.startswith("mapbox://"):
self.style = basemap
return
# Otherwise, treat as XYZ tile URL
try:
url, default_attribution = get_basemap_url(basemap)
except (ValueError, KeyError):
url = basemap
default_attribution = ""
self.call_js_method(
"addBasemap",
url,
attribution=attribution or default_attribution,
name=basemap,
**kwargs,
)
# Track in layer dict
basemaps = self._layer_dict.get("Basemaps", [])
if basemap not in basemaps:
self._layer_dict = {
**self._layer_dict,
"Basemaps": basemaps + [basemap],
}
# -------------------------------------------------------------------------
# Vector Data Methods
# -------------------------------------------------------------------------
def add_vector(
self,
data: Any,
layer_type: Optional[str] = None,
paint: Optional[Dict] = None,
name: Optional[str] = None,
fit_bounds: bool = True,
**kwargs,
) -> None:
"""Add vector data to the map.
Supports GeoJSON, GeoDataFrame, or file paths to vector formats.
Args:
data: GeoJSON dict, GeoDataFrame, or path to vector file.
layer_type: Mapbox layer type ('circle', 'line', 'fill', 'symbol').
paint: Mapbox paint properties.
name: Layer name.
fit_bounds: Whether to fit map to data bounds.
**kwargs: Additional layer options.
"""
geojson = to_geojson(data)
# Handle URL data
if geojson.get("type") == "url":
self.add_geojson(
geojson["url"],
layer_type=layer_type,
paint=paint,
name=name,
fit_bounds=fit_bounds,
**kwargs,
)
return
layer_id = name or f"vector-{len(self._layers)}"
# Infer layer type if not specified
if layer_type is None:
layer_type = infer_layer_type(geojson)
# Get default paint if not provided
if paint is None:
paint = get_default_paint(layer_type)
# Get bounds
bounds = get_bounds(data) if fit_bounds else None
# Call JavaScript
self.call_js_method(
"addGeoJSON",
data=geojson,
name=layer_id,
layerType=layer_type,
paint=paint,
fitBounds=fit_bounds,
bounds=bounds,
**kwargs,
)
# Track layer
self._layers = {
**self._layers,
layer_id: {
"id": layer_id,
"type": layer_type,
"source": f"{layer_id}-source",
"paint": paint,
},
}
def add_geojson(
self,
data: Union[str, Dict],
layer_type: Optional[str] = None,
paint: Optional[Dict] = None,
name: Optional[str] = None,
fit_bounds: bool = True,
**kwargs,
) -> None:
"""Add GeoJSON data to the map.
Args:
data: GeoJSON dict or URL to GeoJSON file.
layer_type: Mapbox layer type.
paint: Mapbox paint properties.
name: Layer name.
fit_bounds: Whether to fit map to data bounds.
**kwargs: Additional layer options.
"""
self.add_vector(
data,
layer_type=layer_type,
paint=paint,
name=name,
fit_bounds=fit_bounds,
**kwargs,
)
# -------------------------------------------------------------------------
# Raster Data Methods
# -------------------------------------------------------------------------
def add_tile_layer(
self,
url: str,
name: Optional[str] = None,
attribution: str = "",
min_zoom: int = 0,
max_zoom: int = 22,
**kwargs,
) -> None:
"""Add an XYZ tile layer.
Args:
url: Tile URL template with {x}, {y}, {z} placeholders.
name: Layer name.
attribution: Attribution text.
min_zoom: Minimum zoom level.
max_zoom: Maximum zoom level.
**kwargs: Additional options.
"""
layer_id = name or f"tiles-{len(self._layers)}"
self.call_js_method(
"addTileLayer",
url,
name=layer_id,
attribution=attribution,
minZoom=min_zoom,
maxZoom=max_zoom,
**kwargs,
)
# Track layer
self._layers = {
**self._layers,
layer_id: {
"id": layer_id,
"type": "raster",
"source": f"{layer_id}-source",
},
}
# -------------------------------------------------------------------------
# COG Layer (deck.gl)
# -------------------------------------------------------------------------
def add_cog_layer(
self,
url: str,
name: Optional[str] = None,
opacity: float = 1.0,
visible: bool = True,
debug: bool = False,
debug_opacity: float = 0.25,
max_error: float = 0.125,
fit_bounds: bool = True,
before_id: Optional[str] = None,
**kwargs,
) -> None:
"""Add a Cloud Optimized GeoTIFF (COG) layer using deck.gl-raster.
This method renders COG files directly in the browser using GPU-accelerated
deck.gl rendering with automatic reprojection support.
Args:
url: URL to the Cloud Optimized GeoTIFF file.
name: Layer ID. If None, auto-generated.
opacity: Layer opacity (0-1).
visible: Whether layer is visible.
debug: Show reprojection mesh for debugging.
debug_opacity: Opacity of debug mesh (0-1).
max_error: Maximum reprojection error in pixels. Lower values
create denser mesh for better accuracy.
fit_bounds: Whether to fit map to COG bounds after loading.
before_id: ID of layer to insert before.
**kwargs: Additional COGLayer props.
Example:
>>> from anymap_ts import MapboxMap
>>> m = MapboxMap()
>>> m.add_cog_layer(
... "https://example.com/landcover.tif",
... name="landcover",
... opacity=0.8
... )
"""
layer_id = name or f"cog-{len(self._layers)}"
self.call_js_method(
"addCOGLayer",
id=layer_id,
geotiff=url,
opacity=opacity,
visible=visible,
debug=debug,
debugOpacity=debug_opacity,
maxError=max_error,
fitBounds=fit_bounds,
beforeId=before_id,
**kwargs,
)
self._layers = {
**self._layers,
layer_id: {
"id": layer_id,
"type": "cog",
"url": url,
},
}
def remove_cog_layer(self, layer_id: str) -> None:
"""Remove a COG layer.
Args:
layer_id: Layer identifier to remove.
"""
if layer_id in self._layers:
layers = dict(self._layers)
del layers[layer_id]
self._layers = layers
self.call_js_method("removeCOGLayer", layer_id)
# -------------------------------------------------------------------------
# Arc Layer (deck.gl)
# -------------------------------------------------------------------------
def add_arc_layer(
self,
data: Any,
name: Optional[str] = None,
get_source_position: Union[str, Any] = "source",
get_target_position: Union[str, Any] = "target",
get_source_color: Optional[List[int]] = None,
get_target_color: Optional[List[int]] = None,
get_width: Union[float, str] = 1,
get_height: float = 1,
great_circle: bool = False,
pickable: bool = True,
opacity: float = 0.8,
**kwargs,
) -> None:
"""Add an arc layer for origin-destination visualization using deck.gl.
Arc layers are ideal for visualizing connections between locations,
such as flight routes, migration patterns, or network flows.
Args:
data: Array of data objects with source/target coordinates.
Each object should have source and target positions.
name: Layer ID. If None, auto-generated.
get_source_position: Accessor for source position [lng, lat].
Can be a string (property name) or a value.
get_target_position: Accessor for target position [lng, lat].
Can be a string (property name) or a value.
get_source_color: Source end color as [r, g, b, a].
Default: [51, 136, 255, 255] (blue).
get_target_color: Target end color as [r, g, b, a].
Default: [255, 136, 51, 255] (orange).
get_width: Arc width in pixels. Can be a number or accessor.
get_height: Arc height multiplier. Higher values create more curved arcs.
great_circle: Whether to draw arcs along great circles.
pickable: Whether layer responds to hover/click events.
opacity: Layer opacity (0-1).
**kwargs: Additional ArcLayer props.
Example:
>>> from anymap_ts import MapboxMap
>>> m = MapboxMap()
>>> arcs = [
... {"source": [-122.4, 37.8], "target": [-73.9, 40.7]},
... {"source": [-122.4, 37.8], "target": [-0.1, 51.5]},
... ]
>>> m.add_arc_layer(arcs, name="flights")
"""
layer_id = name or f"arc-{len(self._layers)}"
processed_data = self._process_deck_data(data)
self.call_js_method(
"addArcLayer",
id=layer_id,
data=processed_data,
getSourcePosition=get_source_position,
getTargetPosition=get_target_position,
getSourceColor=get_source_color or [51, 136, 255, 255],
getTargetColor=get_target_color or [255, 136, 51, 255],
getWidth=get_width,
getHeight=get_height,
greatCircle=great_circle,
pickable=pickable,
opacity=opacity,
**kwargs,
)
self._layers = {
**self._layers,
layer_id: {
"id": layer_id,
"type": "arc",
},
}
def remove_arc_layer(self, layer_id: str) -> None:
"""Remove an arc layer.
Args:
layer_id: Layer identifier to remove.
"""
if layer_id in self._layers:
layers = dict(self._layers)
del layers[layer_id]
self._layers = layers
self.call_js_method("removeArcLayer", layer_id)
# -------------------------------------------------------------------------
# PointCloud Layer (deck.gl)
# -------------------------------------------------------------------------
def add_point_cloud_layer(
self,
data: Any,
name: Optional[str] = None,
get_position: Union[str, Any] = "position",
get_color: Optional[Union[List[int], str]] = None,
get_normal: Optional[Union[str, Any]] = None,
point_size: float = 2,
size_units: str = "pixels",
pickable: bool = True,
opacity: float = 1.0,
material: bool = True,
coordinate_system: Optional[int] = None,
coordinate_origin: Optional[List[float]] = None,
**kwargs,
) -> None:
"""Add a point cloud layer for 3D point visualization using deck.gl.
Point cloud layers render large collections of 3D points, ideal for
LiDAR data, photogrammetry outputs, or any 3D point dataset.
Args:
data: Array of point data with positions. Each point should have
x, y, z coordinates (or position array).
name: Layer ID. If None, auto-generated.
get_position: Accessor for point position [x, y, z].
Can be a string (property name) or a value.
get_color: Accessor or value for point color [r, g, b, a].
Default: [255, 255, 255, 255] (white).
get_normal: Accessor for point normal [nx, ny, nz] for lighting.
Default: [0, 0, 1] (pointing up).
point_size: Point size in pixels or meters (depends on size_units).
size_units: Size units: 'pixels', 'meters', or 'common'.
pickable: Whether layer responds to hover/click events.
opacity: Layer opacity (0-1).
material: Whether to enable lighting effects.
coordinate_system: Coordinate system for positions.
coordinate_origin: Origin for coordinate system [x, y, z].
**kwargs: Additional PointCloudLayer props.
Example:
>>> from anymap_ts import MapboxMap
>>> m = MapboxMap(pitch=45)
>>> points = [
... {"position": [-122.4, 37.8, 100], "color": [255, 0, 0, 255]},
... {"position": [-122.3, 37.7, 200], "color": [0, 255, 0, 255]},
... ]
>>> m.add_point_cloud_layer(points, point_size=5)
"""
layer_id = name or f"pointcloud-{len(self._layers)}"
processed_data = self._process_deck_data(data)
self.call_js_method(
"addPointCloudLayer",
id=layer_id,
data=processed_data,
getPosition=get_position,
getColor=get_color or [255, 255, 255, 255],
getNormal=get_normal,
pointSize=point_size,
sizeUnits=size_units,
pickable=pickable,
opacity=opacity,
material=material,
coordinateSystem=coordinate_system,
coordinateOrigin=coordinate_origin,
**kwargs,
)
self._layers = {
**self._layers,
layer_id: {
"id": layer_id,
"type": "pointcloud",
},
}
def remove_point_cloud_layer(self, layer_id: str) -> None:
"""Remove a point cloud layer.
Args:
layer_id: Layer identifier to remove.
"""
if layer_id in self._layers:
layers = dict(self._layers)
del layers[layer_id]
self._layers = layers
self.call_js_method("removePointCloudLayer", layer_id)
def _process_deck_data(self, data: Any) -> Any:
"""Process data for deck.gl layers.
Handles GeoDataFrame, file paths, GeoJSON, and list of dicts.
Args:
data: Input data in various formats.
Returns:
Processed data suitable for deck.gl layers.
"""
# Handle GeoDataFrame
if hasattr(data, "__geo_interface__"):
return data.__geo_interface__
# Handle file paths
if isinstance(data, (str, Path)):
path = Path(data)
if path.exists():
try:
import geopandas as gpd
gdf = gpd.read_file(path)
return gdf.__geo_interface__
except ImportError:
pass
# Return as-is for lists, dicts, etc.
return data
# -------------------------------------------------------------------------
# Terrain Methods (Mapbox-specific)
# -------------------------------------------------------------------------
def add_terrain(
self, exaggeration: float = 1.0, source: str = "mapbox-dem"
) -> None:
"""Add 3D terrain to the map.
Args:
exaggeration: Terrain exaggeration factor.
source: Terrain source ID.
"""
self.call_js_method("addTerrain", source=source, exaggeration=exaggeration)
def remove_terrain(self) -> None:
"""Remove 3D terrain from the map."""
self.call_js_method("removeTerrain")
# -------------------------------------------------------------------------
# Layer Management
# -------------------------------------------------------------------------
def add_layer(
self,
layer_id: str,
layer_type: str,
source: Union[str, Dict],
paint: Optional[Dict] = None,
layout: Optional[Dict] = None,
before_id: Optional[str] = None,
**kwargs,
) -> None:
"""Add a generic layer to the map.
Args:
layer_id: Unique layer identifier.
layer_type: Mapbox layer type.
source: Source ID or source configuration dict.
paint: Paint properties.
layout: Layout properties.
before_id: ID of layer to insert before.
**kwargs: Additional layer options.
"""
layer_config = {
"id": layer_id,
"type": layer_type,
"paint": paint or {},
"layout": layout or {},
**kwargs,
}
if isinstance(source, str):
layer_config["source"] = source
else:
source_id = f"{layer_id}-source"
self._sources = {**self._sources, source_id: source}
self.call_js_method("addSource", source_id, **source)
layer_config["source"] = source_id
self._layers = {**self._layers, layer_id: layer_config}
self.call_js_method("addLayer", beforeId=before_id, **layer_config)
def remove_layer(self, layer_id: str) -> None:
"""Remove a layer from the map.
Args:
layer_id: Layer identifier to remove.
"""
if layer_id in self._layers:
layers = dict(self._layers)
del layers[layer_id]
self._layers = layers
self.call_js_method("removeLayer", layer_id)
def set_visibility(self, layer_id: str, visible: bool) -> None:
"""Set layer visibility.
Args:
layer_id: Layer identifier.
visible: Whether layer should be visible.
"""
self.call_js_method("setVisibility", layer_id, visible)
def set_opacity(self, layer_id: str, opacity: float) -> None:
"""Set layer opacity.
Args:
layer_id: Layer identifier.
opacity: Opacity value between 0 and 1.
"""
self.call_js_method("setOpacity", layer_id, opacity)
# -------------------------------------------------------------------------
# Controls
# -------------------------------------------------------------------------
def add_control(
self,
control_type: str,
position: str = "top-right",
**kwargs,
) -> None:
"""Add a map control.
Args:
control_type: Type of control ('navigation', 'scale', 'fullscreen', etc.).
position: Control position.
**kwargs: Control-specific options.
"""
self.call_js_method("addControl", control_type, position=position, **kwargs)
self._controls = {
**self._controls,
control_type: {"type": control_type, "position": position, **kwargs},
}
def remove_control(self, control_type: str) -> None:
"""Remove a map control.
Args:
control_type: Type of control to remove.
"""
self.call_js_method("removeControl", control_type)
if control_type in self._controls:
controls = dict(self._controls)
del controls[control_type]
self._controls = controls
# -------------------------------------------------------------------------
# Markers
# -------------------------------------------------------------------------
def add_marker(
self,
lng: float,
lat: float,
color: str = "#3388ff",
popup: Optional[str] = None,
marker_id: Optional[str] = None,
) -> None:
"""Add a marker to the map.
Args:
lng: Longitude.
lat: Latitude.
color: Marker color.
popup: HTML content for popup.
marker_id: Unique marker ID.
"""
self.call_js_method(
"addMarker", lng, lat, color=color, popup=popup, id=marker_id
)
def remove_marker(self, marker_id: str) -> None:
"""Remove a marker from the map.
Args:
marker_id: Marker ID to remove.
"""
self.call_js_method("removeMarker", marker_id)
# -------------------------------------------------------------------------
# HTML Export
# -------------------------------------------------------------------------
def _generate_html_template(self) -> str:
"""Generate standalone HTML for the map."""
template_path = Path(__file__).parent / "templates" / "mapbox.html"
if template_path.exists():
template = template_path.read_text(encoding="utf-8")
else:
template = self._get_default_template()
# Serialize state
state = {
"center": self.center,
"zoom": self.zoom,
"style": self.style,
"bearing": self.bearing,
"pitch": self.pitch,
"width": self.width,
"height": self.height,
"layers": self._layers,
"sources": self._sources,
"controls": self._controls,
"js_calls": self._js_calls,
"access_token": self.access_token,
}
template = template.replace("{{state}}", json.dumps(state, indent=2))
return template
def _get_default_template(self) -> str:
"""Get default HTML template."""
return """<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>{{title}}</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<script src="https://api.mapbox.com/mapbox-gl-js/v3.0.0/mapbox-gl.js"></script>
<link href="https://api.mapbox.com/mapbox-gl-js/v3.0.0/mapbox-gl.css" rel="stylesheet" />
<style>
body { margin: 0; padding: 0; }
#map { position: absolute; top: 0; bottom: 0; width: 100%; }
</style>
</head>
<body>
<div id="map"></div>
<script>
const state = {{state}};
mapboxgl.accessToken = state.access_token;
const map = new mapboxgl.Map({
container: 'map',
style: state.style,
center: state.center,
zoom: state.zoom,
bearing: state.bearing || 0,
pitch: state.pitch || 0
});
map.on('load', function() {
// Replay JS calls
for (const call of state.js_calls || []) {
try {
executeMethod(call.method, call.args, call.kwargs);
} catch (e) {
console.error('Error executing', call.method, e);
}
}
});
function executeMethod(method, args, kwargs) {
switch (method) {
case 'addBasemap':
const url = args[0];
const name = kwargs.name || 'basemap';
const sourceId = 'basemap-' + name;
if (!map.getSource(sourceId)) {
map.addSource(sourceId, {
type: 'raster',
tiles: [url],
tileSize: 256,
attribution: kwargs.attribution || ''
});
}
if (!map.getLayer(sourceId)) {
map.addLayer({
id: sourceId,
type: 'raster',
source: sourceId
});
}
break;
case 'addGeoJSON':
const layerName = kwargs.name;
const sourceIdGeo = layerName + '-source';
if (!map.getSource(sourceIdGeo)) {
map.addSource(sourceIdGeo, {
type: 'geojson',
data: kwargs.data
});
}
if (!map.getLayer(layerName)) {
map.addLayer({
id: layerName,
type: kwargs.layerType || 'circle',
source: sourceIdGeo,
paint: kwargs.paint || {}
});
}
if (kwargs.fitBounds && kwargs.bounds) {
map.fitBounds([
[kwargs.bounds[0], kwargs.bounds[1]],
[kwargs.bounds[2], kwargs.bounds[3]]
], { padding: 50 });
}
break;
case 'addTileLayer':
const tileUrl = args[0];
const tileName = kwargs.name;
const tileSourceId = tileName + '-source';
if (!map.getSource(tileSourceId)) {
map.addSource(tileSourceId, {
type: 'raster',
tiles: [tileUrl],
tileSize: 256,
attribution: kwargs.attribution || ''
});
}
if (!map.getLayer(tileName)) {
map.addLayer({
id: tileName,
type: 'raster',
source: tileSourceId
});
}
break;
case 'addControl':
const controlType = args[0];
const position = kwargs.position || 'top-right';
let control;
switch (controlType) {
case 'navigation':
control = new mapboxgl.NavigationControl();
break;
case 'scale':
control = new mapboxgl.ScaleControl();
break;
case 'fullscreen':
control = new mapboxgl.FullscreenControl();
break;
}
if (control) {
map.addControl(control, position);
}
break;
case 'addTerrain':
const terrainSource = kwargs.source || 'mapbox-dem';
if (!map.getSource(terrainSource)) {
map.addSource(terrainSource, {
type: 'raster-dem',
url: 'mapbox://mapbox.mapbox-terrain-dem-v1',
tileSize: 512,
maxzoom: 14
});
}
map.setTerrain({ source: terrainSource, exaggeration: kwargs.exaggeration || 1 });
break;
case 'removeTerrain':
map.setTerrain(null);
break;
case 'flyTo':
map.flyTo({
center: [args[0], args[1]],
zoom: kwargs.zoom,
duration: kwargs.duration || 2000
});
break;
case 'fitBounds':
const bounds = args[0];
map.fitBounds([
[bounds[0], bounds[1]],
[bounds[2], bounds[3]]
], {
padding: kwargs.padding || 50,
duration: kwargs.duration || 1000
});
break;
case 'addMarker':
new mapboxgl.Marker({ color: kwargs.color || '#3388ff' })
.setLngLat([args[0], args[1]])
.addTo(map);
break;
default:
console.log('Unknown method:', method);
}
}
</script>
</body>
</html>"""
__init__(self, center=(0.0, 0.0), zoom=2.0, width='100%', height='600px', style='mapbox://styles/mapbox/streets-v12', bearing=0.0, pitch=0.0, access_token=None, controls=None, **kwargs)
special
¶
Initialize a Mapbox map.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
center |
Tuple[float, float] |
Map center as (longitude, latitude). |
(0.0, 0.0) |
zoom |
float |
Initial zoom level. |
2.0 |
width |
str |
Map width as CSS string. |
'100%' |
height |
str |
Map height as CSS string. |
'600px' |
style |
str |
Mapbox style URL (e.g., "mapbox://styles/mapbox/streets-v12"). |
'mapbox://styles/mapbox/streets-v12' |
bearing |
float |
Map bearing in degrees. |
0.0 |
pitch |
float |
Map pitch in degrees. |
0.0 |
access_token |
Optional[str] |
Mapbox access token. If None, reads from MAPBOX_TOKEN env var. |
None |
controls |
Optional[Dict[str, Any]] |
Dict of controls to add (e.g., {"navigation": True}). |
None |
**kwargs |
Additional widget arguments. |
{} |
Source code in anymap_ts/mapbox.py
def __init__(
self,
center: Tuple[float, float] = (0.0, 0.0),
zoom: float = 2.0,
width: str = "100%",
height: str = "600px",
style: str = "mapbox://styles/mapbox/streets-v12",
bearing: float = 0.0,
pitch: float = 0.0,
access_token: Optional[str] = None,
controls: Optional[Dict[str, Any]] = None,
**kwargs,
):
"""Initialize a Mapbox map.
Args:
center: Map center as (longitude, latitude).
zoom: Initial zoom level.
width: Map width as CSS string.
height: Map height as CSS string.
style: Mapbox style URL (e.g., "mapbox://styles/mapbox/streets-v12").
bearing: Map bearing in degrees.
pitch: Map pitch in degrees.
access_token: Mapbox access token. If None, reads from MAPBOX_TOKEN env var.
controls: Dict of controls to add (e.g., {"navigation": True}).
**kwargs: Additional widget arguments.
"""
# Get access token
token = access_token or get_mapbox_token()
if not token:
print(
"Warning: No Mapbox access token provided. "
"Set MAPBOX_TOKEN environment variable or pass access_token parameter."
)
super().__init__(
center=list(center),
zoom=zoom,
width=width,
height=height,
style=style,
bearing=bearing,
pitch=pitch,
access_token=token,
**kwargs,
)
# Initialize layer dictionary
self._layer_dict = {"Background": []}
# Add default controls
if controls is None:
controls = {"navigation": True, "fullscreen": True}
for control_name, config in controls.items():
if config:
self.add_control(
control_name, **(config if isinstance(config, dict) else {})
)
add_arc_layer(self, data, name=None, get_source_position='source', get_target_position='target', get_source_color=None, get_target_color=None, get_width=1, get_height=1, great_circle=False, pickable=True, opacity=0.8, **kwargs)
¶
Add an arc layer for origin-destination visualization using deck.gl.
Arc layers are ideal for visualizing connections between locations, such as flight routes, migration patterns, or network flows.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data |
Any |
Array of data objects with source/target coordinates. Each object should have source and target positions. |
required |
name |
Optional[str] |
Layer ID. If None, auto-generated. |
None |
get_source_position |
Union[str, Any] |
Accessor for source position [lng, lat]. Can be a string (property name) or a value. |
'source' |
get_target_position |
Union[str, Any] |
Accessor for target position [lng, lat]. Can be a string (property name) or a value. |
'target' |
get_source_color |
Optional[List[int]] |
Source end color as [r, g, b, a]. Default: [51, 136, 255, 255] (blue). |
None |
get_target_color |
Optional[List[int]] |
Target end color as [r, g, b, a]. Default: [255, 136, 51, 255] (orange). |
None |
get_width |
Union[float, str] |
Arc width in pixels. Can be a number or accessor. |
1 |
get_height |
float |
Arc height multiplier. Higher values create more curved arcs. |
1 |
great_circle |
bool |
Whether to draw arcs along great circles. |
False |
pickable |
bool |
Whether layer responds to hover/click events. |
True |
opacity |
float |
Layer opacity (0-1). |
0.8 |
**kwargs |
Additional ArcLayer props. |
{} |
Examples:
>>> from anymap_ts import MapboxMap
>>> m = MapboxMap()
>>> arcs = [
... {"source": [-122.4, 37.8], "target": [-73.9, 40.7]},
... {"source": [-122.4, 37.8], "target": [-0.1, 51.5]},
... ]
>>> m.add_arc_layer(arcs, name="flights")
Source code in anymap_ts/mapbox.py
def add_arc_layer(
self,
data: Any,
name: Optional[str] = None,
get_source_position: Union[str, Any] = "source",
get_target_position: Union[str, Any] = "target",
get_source_color: Optional[List[int]] = None,
get_target_color: Optional[List[int]] = None,
get_width: Union[float, str] = 1,
get_height: float = 1,
great_circle: bool = False,
pickable: bool = True,
opacity: float = 0.8,
**kwargs,
) -> None:
"""Add an arc layer for origin-destination visualization using deck.gl.
Arc layers are ideal for visualizing connections between locations,
such as flight routes, migration patterns, or network flows.
Args:
data: Array of data objects with source/target coordinates.
Each object should have source and target positions.
name: Layer ID. If None, auto-generated.
get_source_position: Accessor for source position [lng, lat].
Can be a string (property name) or a value.
get_target_position: Accessor for target position [lng, lat].
Can be a string (property name) or a value.
get_source_color: Source end color as [r, g, b, a].
Default: [51, 136, 255, 255] (blue).
get_target_color: Target end color as [r, g, b, a].
Default: [255, 136, 51, 255] (orange).
get_width: Arc width in pixels. Can be a number or accessor.
get_height: Arc height multiplier. Higher values create more curved arcs.
great_circle: Whether to draw arcs along great circles.
pickable: Whether layer responds to hover/click events.
opacity: Layer opacity (0-1).
**kwargs: Additional ArcLayer props.
Example:
>>> from anymap_ts import MapboxMap
>>> m = MapboxMap()
>>> arcs = [
... {"source": [-122.4, 37.8], "target": [-73.9, 40.7]},
... {"source": [-122.4, 37.8], "target": [-0.1, 51.5]},
... ]
>>> m.add_arc_layer(arcs, name="flights")
"""
layer_id = name or f"arc-{len(self._layers)}"
processed_data = self._process_deck_data(data)
self.call_js_method(
"addArcLayer",
id=layer_id,
data=processed_data,
getSourcePosition=get_source_position,
getTargetPosition=get_target_position,
getSourceColor=get_source_color or [51, 136, 255, 255],
getTargetColor=get_target_color or [255, 136, 51, 255],
getWidth=get_width,
getHeight=get_height,
greatCircle=great_circle,
pickable=pickable,
opacity=opacity,
**kwargs,
)
self._layers = {
**self._layers,
layer_id: {
"id": layer_id,
"type": "arc",
},
}
add_basemap(self, basemap='mapbox://styles/mapbox/streets-v12', attribution=None, **kwargs)
¶
Add a basemap layer.
For Mapbox styles, use the style URL format: - "mapbox://styles/mapbox/streets-v12" - "mapbox://styles/mapbox/satellite-v9" - "mapbox://styles/mapbox/satellite-streets-v12" - "mapbox://styles/mapbox/light-v11" - "mapbox://styles/mapbox/dark-v11" - "mapbox://styles/mapbox/outdoors-v12"
Or use XYZ tile URLs for custom basemaps.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
basemap |
str |
Mapbox style URL or XYZ tile URL. |
'mapbox://styles/mapbox/streets-v12' |
attribution |
Optional[str] |
Custom attribution text. |
None |
**kwargs |
Additional options. |
{} |
Source code in anymap_ts/mapbox.py
def add_basemap(
self,
basemap: str = "mapbox://styles/mapbox/streets-v12",
attribution: Optional[str] = None,
**kwargs,
) -> None:
"""Add a basemap layer.
For Mapbox styles, use the style URL format:
- "mapbox://styles/mapbox/streets-v12"
- "mapbox://styles/mapbox/satellite-v9"
- "mapbox://styles/mapbox/satellite-streets-v12"
- "mapbox://styles/mapbox/light-v11"
- "mapbox://styles/mapbox/dark-v11"
- "mapbox://styles/mapbox/outdoors-v12"
Or use XYZ tile URLs for custom basemaps.
Args:
basemap: Mapbox style URL or XYZ tile URL.
attribution: Custom attribution text.
**kwargs: Additional options.
"""
# If it's a Mapbox style URL, set it as the map style
if basemap.startswith("mapbox://"):
self.style = basemap
return
# Otherwise, treat as XYZ tile URL
try:
url, default_attribution = get_basemap_url(basemap)
except (ValueError, KeyError):
url = basemap
default_attribution = ""
self.call_js_method(
"addBasemap",
url,
attribution=attribution or default_attribution,
name=basemap,
**kwargs,
)
# Track in layer dict
basemaps = self._layer_dict.get("Basemaps", [])
if basemap not in basemaps:
self._layer_dict = {
**self._layer_dict,
"Basemaps": basemaps + [basemap],
}
add_cog_layer(self, url, name=None, opacity=1.0, visible=True, debug=False, debug_opacity=0.25, max_error=0.125, fit_bounds=True, before_id=None, **kwargs)
¶
Add a Cloud Optimized GeoTIFF (COG) layer using deck.gl-raster.
This method renders COG files directly in the browser using GPU-accelerated deck.gl rendering with automatic reprojection support.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
url |
str |
URL to the Cloud Optimized GeoTIFF file. |
required |
name |
Optional[str] |
Layer ID. If None, auto-generated. |
None |
opacity |
float |
Layer opacity (0-1). |
1.0 |
visible |
bool |
Whether layer is visible. |
True |
debug |
bool |
Show reprojection mesh for debugging. |
False |
debug_opacity |
float |
Opacity of debug mesh (0-1). |
0.25 |
max_error |
float |
Maximum reprojection error in pixels. Lower values create denser mesh for better accuracy. |
0.125 |
fit_bounds |
bool |
Whether to fit map to COG bounds after loading. |
True |
before_id |
Optional[str] |
ID of layer to insert before. |
None |
**kwargs |
Additional COGLayer props. |
{} |
Examples:
>>> from anymap_ts import MapboxMap
>>> m = MapboxMap()
>>> m.add_cog_layer(
... "https://example.com/landcover.tif",
... name="landcover",
... opacity=0.8
... )
Source code in anymap_ts/mapbox.py
def add_cog_layer(
self,
url: str,
name: Optional[str] = None,
opacity: float = 1.0,
visible: bool = True,
debug: bool = False,
debug_opacity: float = 0.25,
max_error: float = 0.125,
fit_bounds: bool = True,
before_id: Optional[str] = None,
**kwargs,
) -> None:
"""Add a Cloud Optimized GeoTIFF (COG) layer using deck.gl-raster.
This method renders COG files directly in the browser using GPU-accelerated
deck.gl rendering with automatic reprojection support.
Args:
url: URL to the Cloud Optimized GeoTIFF file.
name: Layer ID. If None, auto-generated.
opacity: Layer opacity (0-1).
visible: Whether layer is visible.
debug: Show reprojection mesh for debugging.
debug_opacity: Opacity of debug mesh (0-1).
max_error: Maximum reprojection error in pixels. Lower values
create denser mesh for better accuracy.
fit_bounds: Whether to fit map to COG bounds after loading.
before_id: ID of layer to insert before.
**kwargs: Additional COGLayer props.
Example:
>>> from anymap_ts import MapboxMap
>>> m = MapboxMap()
>>> m.add_cog_layer(
... "https://example.com/landcover.tif",
... name="landcover",
... opacity=0.8
... )
"""
layer_id = name or f"cog-{len(self._layers)}"
self.call_js_method(
"addCOGLayer",
id=layer_id,
geotiff=url,
opacity=opacity,
visible=visible,
debug=debug,
debugOpacity=debug_opacity,
maxError=max_error,
fitBounds=fit_bounds,
beforeId=before_id,
**kwargs,
)
self._layers = {
**self._layers,
layer_id: {
"id": layer_id,
"type": "cog",
"url": url,
},
}
add_control(self, control_type, position='top-right', **kwargs)
¶
Add a map control.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
control_type |
str |
Type of control ('navigation', 'scale', 'fullscreen', etc.). |
required |
position |
str |
Control position. |
'top-right' |
**kwargs |
Control-specific options. |
{} |
Source code in anymap_ts/mapbox.py
def add_control(
self,
control_type: str,
position: str = "top-right",
**kwargs,
) -> None:
"""Add a map control.
Args:
control_type: Type of control ('navigation', 'scale', 'fullscreen', etc.).
position: Control position.
**kwargs: Control-specific options.
"""
self.call_js_method("addControl", control_type, position=position, **kwargs)
self._controls = {
**self._controls,
control_type: {"type": control_type, "position": position, **kwargs},
}
add_geojson(self, data, layer_type=None, paint=None, name=None, fit_bounds=True, **kwargs)
¶
Add GeoJSON data to the map.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data |
Union[str, Dict] |
GeoJSON dict or URL to GeoJSON file. |
required |
layer_type |
Optional[str] |
Mapbox layer type. |
None |
paint |
Optional[Dict] |
Mapbox paint properties. |
None |
name |
Optional[str] |
Layer name. |
None |
fit_bounds |
bool |
Whether to fit map to data bounds. |
True |
**kwargs |
Additional layer options. |
{} |
Source code in anymap_ts/mapbox.py
def add_geojson(
self,
data: Union[str, Dict],
layer_type: Optional[str] = None,
paint: Optional[Dict] = None,
name: Optional[str] = None,
fit_bounds: bool = True,
**kwargs,
) -> None:
"""Add GeoJSON data to the map.
Args:
data: GeoJSON dict or URL to GeoJSON file.
layer_type: Mapbox layer type.
paint: Mapbox paint properties.
name: Layer name.
fit_bounds: Whether to fit map to data bounds.
**kwargs: Additional layer options.
"""
self.add_vector(
data,
layer_type=layer_type,
paint=paint,
name=name,
fit_bounds=fit_bounds,
**kwargs,
)
add_layer(self, layer_id, layer_type, source, paint=None, layout=None, before_id=None, **kwargs)
¶
Add a generic layer to the map.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
layer_id |
str |
Unique layer identifier. |
required |
layer_type |
str |
Mapbox layer type. |
required |
source |
Union[str, Dict] |
Source ID or source configuration dict. |
required |
paint |
Optional[Dict] |
Paint properties. |
None |
layout |
Optional[Dict] |
Layout properties. |
None |
before_id |
Optional[str] |
ID of layer to insert before. |
None |
**kwargs |
Additional layer options. |
{} |
Source code in anymap_ts/mapbox.py
def add_layer(
self,
layer_id: str,
layer_type: str,
source: Union[str, Dict],
paint: Optional[Dict] = None,
layout: Optional[Dict] = None,
before_id: Optional[str] = None,
**kwargs,
) -> None:
"""Add a generic layer to the map.
Args:
layer_id: Unique layer identifier.
layer_type: Mapbox layer type.
source: Source ID or source configuration dict.
paint: Paint properties.
layout: Layout properties.
before_id: ID of layer to insert before.
**kwargs: Additional layer options.
"""
layer_config = {
"id": layer_id,
"type": layer_type,
"paint": paint or {},
"layout": layout or {},
**kwargs,
}
if isinstance(source, str):
layer_config["source"] = source
else:
source_id = f"{layer_id}-source"
self._sources = {**self._sources, source_id: source}
self.call_js_method("addSource", source_id, **source)
layer_config["source"] = source_id
self._layers = {**self._layers, layer_id: layer_config}
self.call_js_method("addLayer", beforeId=before_id, **layer_config)
add_marker(self, lng, lat, color='#3388ff', popup=None, marker_id=None)
¶
Add a marker to the map.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
lng |
float |
Longitude. |
required |
lat |
float |
Latitude. |
required |
color |
str |
Marker color. |
'#3388ff' |
popup |
Optional[str] |
HTML content for popup. |
None |
marker_id |
Optional[str] |
Unique marker ID. |
None |
Source code in anymap_ts/mapbox.py
def add_marker(
self,
lng: float,
lat: float,
color: str = "#3388ff",
popup: Optional[str] = None,
marker_id: Optional[str] = None,
) -> None:
"""Add a marker to the map.
Args:
lng: Longitude.
lat: Latitude.
color: Marker color.
popup: HTML content for popup.
marker_id: Unique marker ID.
"""
self.call_js_method(
"addMarker", lng, lat, color=color, popup=popup, id=marker_id
)
add_point_cloud_layer(self, data, name=None, get_position='position', get_color=None, get_normal=None, point_size=2, size_units='pixels', pickable=True, opacity=1.0, material=True, coordinate_system=None, coordinate_origin=None, **kwargs)
¶
Add a point cloud layer for 3D point visualization using deck.gl.
Point cloud layers render large collections of 3D points, ideal for LiDAR data, photogrammetry outputs, or any 3D point dataset.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data |
Any |
Array of point data with positions. Each point should have x, y, z coordinates (or position array). |
required |
name |
Optional[str] |
Layer ID. If None, auto-generated. |
None |
get_position |
Union[str, Any] |
Accessor for point position [x, y, z]. Can be a string (property name) or a value. |
'position' |
get_color |
Optional[Union[List[int], str]] |
Accessor or value for point color [r, g, b, a]. Default: [255, 255, 255, 255] (white). |
None |
get_normal |
Optional[Union[str, Any]] |
Accessor for point normal [nx, ny, nz] for lighting. Default: [0, 0, 1] (pointing up). |
None |
point_size |
float |
Point size in pixels or meters (depends on size_units). |
2 |
size_units |
str |
Size units: 'pixels', 'meters', or 'common'. |
'pixels' |
pickable |
bool |
Whether layer responds to hover/click events. |
True |
opacity |
float |
Layer opacity (0-1). |
1.0 |
material |
bool |
Whether to enable lighting effects. |
True |
coordinate_system |
Optional[int] |
Coordinate system for positions. |
None |
coordinate_origin |
Optional[List[float]] |
Origin for coordinate system [x, y, z]. |
None |
**kwargs |
Additional PointCloudLayer props. |
{} |
Examples:
>>> from anymap_ts import MapboxMap
>>> m = MapboxMap(pitch=45)
>>> points = [
... {"position": [-122.4, 37.8, 100], "color": [255, 0, 0, 255]},
... {"position": [-122.3, 37.7, 200], "color": [0, 255, 0, 255]},
... ]
>>> m.add_point_cloud_layer(points, point_size=5)
Source code in anymap_ts/mapbox.py
def add_point_cloud_layer(
self,
data: Any,
name: Optional[str] = None,
get_position: Union[str, Any] = "position",
get_color: Optional[Union[List[int], str]] = None,
get_normal: Optional[Union[str, Any]] = None,
point_size: float = 2,
size_units: str = "pixels",
pickable: bool = True,
opacity: float = 1.0,
material: bool = True,
coordinate_system: Optional[int] = None,
coordinate_origin: Optional[List[float]] = None,
**kwargs,
) -> None:
"""Add a point cloud layer for 3D point visualization using deck.gl.
Point cloud layers render large collections of 3D points, ideal for
LiDAR data, photogrammetry outputs, or any 3D point dataset.
Args:
data: Array of point data with positions. Each point should have
x, y, z coordinates (or position array).
name: Layer ID. If None, auto-generated.
get_position: Accessor for point position [x, y, z].
Can be a string (property name) or a value.
get_color: Accessor or value for point color [r, g, b, a].
Default: [255, 255, 255, 255] (white).
get_normal: Accessor for point normal [nx, ny, nz] for lighting.
Default: [0, 0, 1] (pointing up).
point_size: Point size in pixels or meters (depends on size_units).
size_units: Size units: 'pixels', 'meters', or 'common'.
pickable: Whether layer responds to hover/click events.
opacity: Layer opacity (0-1).
material: Whether to enable lighting effects.
coordinate_system: Coordinate system for positions.
coordinate_origin: Origin for coordinate system [x, y, z].
**kwargs: Additional PointCloudLayer props.
Example:
>>> from anymap_ts import MapboxMap
>>> m = MapboxMap(pitch=45)
>>> points = [
... {"position": [-122.4, 37.8, 100], "color": [255, 0, 0, 255]},
... {"position": [-122.3, 37.7, 200], "color": [0, 255, 0, 255]},
... ]
>>> m.add_point_cloud_layer(points, point_size=5)
"""
layer_id = name or f"pointcloud-{len(self._layers)}"
processed_data = self._process_deck_data(data)
self.call_js_method(
"addPointCloudLayer",
id=layer_id,
data=processed_data,
getPosition=get_position,
getColor=get_color or [255, 255, 255, 255],
getNormal=get_normal,
pointSize=point_size,
sizeUnits=size_units,
pickable=pickable,
opacity=opacity,
material=material,
coordinateSystem=coordinate_system,
coordinateOrigin=coordinate_origin,
**kwargs,
)
self._layers = {
**self._layers,
layer_id: {
"id": layer_id,
"type": "pointcloud",
},
}
add_terrain(self, exaggeration=1.0, source='mapbox-dem')
¶
Add 3D terrain to the map.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
exaggeration |
float |
Terrain exaggeration factor. |
1.0 |
source |
str |
Terrain source ID. |
'mapbox-dem' |
Source code in anymap_ts/mapbox.py
def add_terrain(
self, exaggeration: float = 1.0, source: str = "mapbox-dem"
) -> None:
"""Add 3D terrain to the map.
Args:
exaggeration: Terrain exaggeration factor.
source: Terrain source ID.
"""
self.call_js_method("addTerrain", source=source, exaggeration=exaggeration)
add_tile_layer(self, url, name=None, attribution='', min_zoom=0, max_zoom=22, **kwargs)
¶
Add an XYZ tile layer.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
url |
str |
Tile URL template with {x}, {y}, {z} placeholders. |
required |
name |
Optional[str] |
Layer name. |
None |
attribution |
str |
Attribution text. |
'' |
min_zoom |
int |
Minimum zoom level. |
0 |
max_zoom |
int |
Maximum zoom level. |
22 |
**kwargs |
Additional options. |
{} |
Source code in anymap_ts/mapbox.py
def add_tile_layer(
self,
url: str,
name: Optional[str] = None,
attribution: str = "",
min_zoom: int = 0,
max_zoom: int = 22,
**kwargs,
) -> None:
"""Add an XYZ tile layer.
Args:
url: Tile URL template with {x}, {y}, {z} placeholders.
name: Layer name.
attribution: Attribution text.
min_zoom: Minimum zoom level.
max_zoom: Maximum zoom level.
**kwargs: Additional options.
"""
layer_id = name or f"tiles-{len(self._layers)}"
self.call_js_method(
"addTileLayer",
url,
name=layer_id,
attribution=attribution,
minZoom=min_zoom,
maxZoom=max_zoom,
**kwargs,
)
# Track layer
self._layers = {
**self._layers,
layer_id: {
"id": layer_id,
"type": "raster",
"source": f"{layer_id}-source",
},
}
add_vector(self, data, layer_type=None, paint=None, name=None, fit_bounds=True, **kwargs)
¶
Add vector data to the map.
Supports GeoJSON, GeoDataFrame, or file paths to vector formats.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data |
Any |
GeoJSON dict, GeoDataFrame, or path to vector file. |
required |
layer_type |
Optional[str] |
Mapbox layer type ('circle', 'line', 'fill', 'symbol'). |
None |
paint |
Optional[Dict] |
Mapbox paint properties. |
None |
name |
Optional[str] |
Layer name. |
None |
fit_bounds |
bool |
Whether to fit map to data bounds. |
True |
**kwargs |
Additional layer options. |
{} |
Source code in anymap_ts/mapbox.py
def add_vector(
self,
data: Any,
layer_type: Optional[str] = None,
paint: Optional[Dict] = None,
name: Optional[str] = None,
fit_bounds: bool = True,
**kwargs,
) -> None:
"""Add vector data to the map.
Supports GeoJSON, GeoDataFrame, or file paths to vector formats.
Args:
data: GeoJSON dict, GeoDataFrame, or path to vector file.
layer_type: Mapbox layer type ('circle', 'line', 'fill', 'symbol').
paint: Mapbox paint properties.
name: Layer name.
fit_bounds: Whether to fit map to data bounds.
**kwargs: Additional layer options.
"""
geojson = to_geojson(data)
# Handle URL data
if geojson.get("type") == "url":
self.add_geojson(
geojson["url"],
layer_type=layer_type,
paint=paint,
name=name,
fit_bounds=fit_bounds,
**kwargs,
)
return
layer_id = name or f"vector-{len(self._layers)}"
# Infer layer type if not specified
if layer_type is None:
layer_type = infer_layer_type(geojson)
# Get default paint if not provided
if paint is None:
paint = get_default_paint(layer_type)
# Get bounds
bounds = get_bounds(data) if fit_bounds else None
# Call JavaScript
self.call_js_method(
"addGeoJSON",
data=geojson,
name=layer_id,
layerType=layer_type,
paint=paint,
fitBounds=fit_bounds,
bounds=bounds,
**kwargs,
)
# Track layer
self._layers = {
**self._layers,
layer_id: {
"id": layer_id,
"type": layer_type,
"source": f"{layer_id}-source",
"paint": paint,
},
}
remove_arc_layer(self, layer_id)
¶
Remove an arc layer.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
layer_id |
str |
Layer identifier to remove. |
required |
Source code in anymap_ts/mapbox.py
def remove_arc_layer(self, layer_id: str) -> None:
"""Remove an arc layer.
Args:
layer_id: Layer identifier to remove.
"""
if layer_id in self._layers:
layers = dict(self._layers)
del layers[layer_id]
self._layers = layers
self.call_js_method("removeArcLayer", layer_id)
remove_cog_layer(self, layer_id)
¶
Remove a COG layer.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
layer_id |
str |
Layer identifier to remove. |
required |
Source code in anymap_ts/mapbox.py
def remove_cog_layer(self, layer_id: str) -> None:
"""Remove a COG layer.
Args:
layer_id: Layer identifier to remove.
"""
if layer_id in self._layers:
layers = dict(self._layers)
del layers[layer_id]
self._layers = layers
self.call_js_method("removeCOGLayer", layer_id)
remove_control(self, control_type)
¶
Remove a map control.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
control_type |
str |
Type of control to remove. |
required |
Source code in anymap_ts/mapbox.py
def remove_control(self, control_type: str) -> None:
"""Remove a map control.
Args:
control_type: Type of control to remove.
"""
self.call_js_method("removeControl", control_type)
if control_type in self._controls:
controls = dict(self._controls)
del controls[control_type]
self._controls = controls
remove_layer(self, layer_id)
¶
Remove a layer from the map.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
layer_id |
str |
Layer identifier to remove. |
required |
Source code in anymap_ts/mapbox.py
def remove_layer(self, layer_id: str) -> None:
"""Remove a layer from the map.
Args:
layer_id: Layer identifier to remove.
"""
if layer_id in self._layers:
layers = dict(self._layers)
del layers[layer_id]
self._layers = layers
self.call_js_method("removeLayer", layer_id)
remove_marker(self, marker_id)
¶
Remove a marker from the map.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
marker_id |
str |
Marker ID to remove. |
required |
Source code in anymap_ts/mapbox.py
def remove_marker(self, marker_id: str) -> None:
"""Remove a marker from the map.
Args:
marker_id: Marker ID to remove.
"""
self.call_js_method("removeMarker", marker_id)
remove_point_cloud_layer(self, layer_id)
¶
Remove a point cloud layer.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
layer_id |
str |
Layer identifier to remove. |
required |
Source code in anymap_ts/mapbox.py
def remove_point_cloud_layer(self, layer_id: str) -> None:
"""Remove a point cloud layer.
Args:
layer_id: Layer identifier to remove.
"""
if layer_id in self._layers:
layers = dict(self._layers)
del layers[layer_id]
self._layers = layers
self.call_js_method("removePointCloudLayer", layer_id)
remove_terrain(self)
¶
Remove 3D terrain from the map.
Source code in anymap_ts/mapbox.py
def remove_terrain(self) -> None:
"""Remove 3D terrain from the map."""
self.call_js_method("removeTerrain")
set_access_token(self, token)
¶
Set the Mapbox access token.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
token |
str |
Mapbox access token. |
required |
Source code in anymap_ts/mapbox.py
def set_access_token(self, token: str) -> None:
"""Set the Mapbox access token.
Args:
token: Mapbox access token.
"""
self.access_token = token
set_opacity(self, layer_id, opacity)
¶
Set layer opacity.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
layer_id |
str |
Layer identifier. |
required |
opacity |
float |
Opacity value between 0 and 1. |
required |
Source code in anymap_ts/mapbox.py
def set_opacity(self, layer_id: str, opacity: float) -> None:
"""Set layer opacity.
Args:
layer_id: Layer identifier.
opacity: Opacity value between 0 and 1.
"""
self.call_js_method("setOpacity", layer_id, opacity)
set_visibility(self, layer_id, visible)
¶
Set layer visibility.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
layer_id |
str |
Layer identifier. |
required |
visible |
bool |
Whether layer should be visible. |
required |
Source code in anymap_ts/mapbox.py
def set_visibility(self, layer_id: str, visible: bool) -> None:
"""Set layer visibility.
Args:
layer_id: Layer identifier.
visible: Whether layer should be visible.
"""
self.call_js_method("setVisibility", layer_id, visible)
get_mapbox_token()
¶
Get Mapbox access token from environment variable.
Returns:
| Type | Description |
|---|---|
str |
Mapbox access token string, or empty string if not set. |
Source code in anymap_ts/mapbox.py
def get_mapbox_token() -> str:
"""Get Mapbox access token from environment variable.
Returns:
Mapbox access token string, or empty string if not set.
"""
return os.environ.get("MAPBOX_TOKEN", "")
maplibre
¶
MapLibre GL JS map widget implementation.
MapLibreMap (MapWidget)
¶
Interactive map widget using MapLibre GL JS.
This class provides a Python interface to MapLibre GL JS maps with full bidirectional communication through anywidget.
Examples:
>>> from anymap_ts import Map
>>> m = Map(center=[-122.4, 37.8], zoom=10)
>>> m.add_basemap("OpenStreetMap")
>>> m
Source code in anymap_ts/maplibre.py
class MapLibreMap(MapWidget):
"""Interactive map widget using MapLibre GL JS.
This class provides a Python interface to MapLibre GL JS maps with
full bidirectional communication through anywidget.
Example:
>>> from anymap_ts import Map
>>> m = Map(center=[-122.4, 37.8], zoom=10)
>>> m.add_basemap("OpenStreetMap")
>>> m
"""
# ESM module for frontend
_esm = STATIC_DIR / "maplibre.js"
_css = STATIC_DIR / "maplibre.css"
# MapLibre-specific traits
bearing = traitlets.Float(0.0).tag(sync=True)
pitch = traitlets.Float(0.0).tag(sync=True)
antialias = traitlets.Bool(True).tag(sync=True)
double_click_zoom = traitlets.Bool(True).tag(sync=True)
# Layer tracking
_layer_dict = traitlets.Dict({}).tag(sync=True)
def __init__(
self,
center: Tuple[float, float] = (0.0, 0.0),
zoom: float = 2.0,
width: str = "100%",
height: str = "600px",
style: Union[str, Dict] = "https://demotiles.maplibre.org/style.json",
bearing: float = 0.0,
pitch: float = 0.0,
controls: Optional[Dict[str, Any]] = None,
**kwargs,
):
"""Initialize a MapLibre map.
Args:
center: Map center as (longitude, latitude)
zoom: Initial zoom level
width: Map width as CSS string
height: Map height as CSS string
style: MapLibre style URL or style object
bearing: Map bearing in degrees
pitch: Map pitch in degrees
controls: Dict of controls to add. If None, defaults to
{"navigation": True, "fullscreen": True, "globe": True, "layer-control": True}.
Use {"layer-control": {"collapsed": True}} for custom options.
**kwargs: Additional widget arguments
"""
# Handle style shortcuts
if isinstance(style, str) and not style.startswith("http"):
try:
style = get_maplibre_style(style)
except ValueError:
pass # Use as-is
super().__init__(
center=list(center),
zoom=zoom,
width=width,
height=height,
style=style,
bearing=bearing,
pitch=pitch,
**kwargs,
)
# Initialize layer dictionary
self._layer_dict = {"Background": []}
# Add default controls
if controls is None:
controls = {
"navigation": True,
"fullscreen": True,
"globe": True,
"layer-control": True,
}
for control_name, config in controls.items():
if config:
if control_name == "layer-control":
# Layer control uses a separate method
self.add_layer_control(
**(config if isinstance(config, dict) else {})
)
else:
self.add_control(
control_name, **(config if isinstance(config, dict) else {})
)
# -------------------------------------------------------------------------
# Basemap Methods
# -------------------------------------------------------------------------
def add_basemap(
self,
basemap: str = "OpenStreetMap",
attribution: Optional[str] = None,
**kwargs,
) -> None:
"""Add a basemap layer.
Args:
basemap: Name of basemap provider (e.g., "OpenStreetMap", "CartoDB.Positron")
attribution: Custom attribution text
**kwargs: Additional options
"""
url, default_attribution = get_basemap_url(basemap)
self.call_js_method(
"addBasemap",
url,
attribution=attribution or default_attribution,
name=basemap,
**kwargs,
)
# Track in layer dict
basemaps = self._layer_dict.get("Basemaps", [])
if basemap not in basemaps:
self._layer_dict = {
**self._layer_dict,
"Basemaps": basemaps + [basemap],
}
# -------------------------------------------------------------------------
# Vector Data Methods
# -------------------------------------------------------------------------
def add_vector(
self,
data: Any,
layer_type: Optional[str] = None,
paint: Optional[Dict] = None,
name: Optional[str] = None,
fit_bounds: bool = True,
**kwargs,
) -> None:
"""Add vector data to the map.
Supports GeoJSON, GeoDataFrame, or file paths to vector formats.
Args:
data: GeoJSON dict, GeoDataFrame, or path to vector file
layer_type: MapLibre layer type ('circle', 'line', 'fill', 'symbol')
paint: MapLibre paint properties
name: Layer name
fit_bounds: Whether to fit map to data bounds
**kwargs: Additional layer options
"""
geojson = to_geojson(data)
# Handle URL data
if geojson.get("type") == "url":
self.add_geojson(
geojson["url"],
layer_type=layer_type,
paint=paint,
name=name,
fit_bounds=fit_bounds,
**kwargs,
)
return
layer_id = name or f"vector-{len(self._layers)}"
# Infer layer type if not specified
if layer_type is None:
layer_type = infer_layer_type(geojson)
# Get default paint if not provided
if paint is None:
paint = get_default_paint(layer_type)
# Get bounds
bounds = get_bounds(data) if fit_bounds else None
# Call JavaScript
self.call_js_method(
"addGeoJSON",
data=geojson,
name=layer_id,
layerType=layer_type,
paint=paint,
fitBounds=fit_bounds,
bounds=bounds,
**kwargs,
)
# Track layer
self._layers = {
**self._layers,
layer_id: {
"id": layer_id,
"type": layer_type,
"source": f"{layer_id}-source",
"paint": paint,
},
}
def add_geojson(
self,
data: Union[str, Dict],
layer_type: Optional[str] = None,
paint: Optional[Dict] = None,
name: Optional[str] = None,
fit_bounds: bool = True,
**kwargs,
) -> None:
"""Add GeoJSON data to the map.
Args:
data: GeoJSON dict or URL to GeoJSON file
layer_type: MapLibre layer type
paint: MapLibre paint properties
name: Layer name
fit_bounds: Whether to fit map to data bounds
**kwargs: Additional layer options
"""
self.add_vector(
data,
layer_type=layer_type,
paint=paint,
name=name,
fit_bounds=fit_bounds,
**kwargs,
)
# -------------------------------------------------------------------------
# Raster Data Methods
# -------------------------------------------------------------------------
def add_raster(
self,
source: str,
name: Optional[str] = None,
attribution: str = "",
indexes: Optional[List[int]] = None,
colormap: Optional[str] = None,
vmin: Optional[float] = None,
vmax: Optional[float] = None,
nodata: Optional[float] = None,
fit_bounds: bool = True,
**kwargs,
) -> None:
"""Add a raster layer from a local file using localtileserver.
Args:
source: Path to local raster file
name: Layer name
attribution: Attribution text
indexes: Band indexes to use
colormap: Colormap name
vmin: Minimum value for colormap
vmax: Maximum value for colormap
nodata: NoData value
fit_bounds: Whether to fit map to raster bounds
**kwargs: Additional options
"""
try:
from localtileserver import TileClient
except ImportError:
raise ImportError(
"localtileserver is required for local raster support. "
"Install with: pip install anymap-ts[raster]"
)
client = TileClient(source)
# Build tile URL with parameters
tile_url = client.get_tile_url()
if indexes:
tile_url = client.get_tile_url(indexes=indexes)
if colormap:
tile_url = client.get_tile_url(colormap=colormap)
if vmin is not None or vmax is not None:
tile_url = client.get_tile_url(
vmin=vmin or client.min, vmax=vmax or client.max
)
if nodata is not None:
tile_url = client.get_tile_url(nodata=nodata)
layer_name = name or Path(source).stem
self.add_tile_layer(
tile_url,
name=layer_name,
attribution=attribution,
**kwargs,
)
# Fit bounds if requested
if fit_bounds:
bounds = client.bounds()
if bounds:
self.fit_bounds([bounds[0], bounds[1], bounds[2], bounds[3]])
def add_tile_layer(
self,
url: str,
name: Optional[str] = None,
attribution: str = "",
min_zoom: int = 0,
max_zoom: int = 22,
**kwargs,
) -> None:
"""Add an XYZ tile layer.
Args:
url: Tile URL template with {x}, {y}, {z} placeholders
name: Layer name
attribution: Attribution text
min_zoom: Minimum zoom level
max_zoom: Maximum zoom level
**kwargs: Additional options
"""
layer_id = name or f"tiles-{len(self._layers)}"
self.call_js_method(
"addTileLayer",
url,
name=layer_id,
attribution=attribution,
minZoom=min_zoom,
maxZoom=max_zoom,
**kwargs,
)
# Track layer
self._layers = {
**self._layers,
layer_id: {
"id": layer_id,
"type": "raster",
"source": f"{layer_id}-source",
},
}
# -------------------------------------------------------------------------
# COG Layer (deck.gl)
# -------------------------------------------------------------------------
def add_cog_layer(
self,
url: str,
name: Optional[str] = None,
opacity: float = 1.0,
visible: bool = True,
debug: bool = False,
debug_opacity: float = 0.25,
max_error: float = 0.125,
fit_bounds: bool = True,
before_id: Optional[str] = None,
**kwargs,
) -> None:
"""Add a Cloud Optimized GeoTIFF (COG) layer using @developmentseed/deck.gl-geotiff.
This method renders COG files directly in the browser using GPU-accelerated
deck.gl-geotiff rendering with automatic reprojection support.
Args:
url: URL to the Cloud Optimized GeoTIFF file.
name: Layer ID. If None, auto-generated.
opacity: Layer opacity (0-1).
visible: Whether layer is visible.
debug: Show reprojection mesh for debugging.
debug_opacity: Opacity of debug mesh (0-1).
max_error: Maximum reprojection error in pixels. Lower values
create denser mesh for better accuracy.
fit_bounds: Whether to fit map to COG bounds after loading.
before_id: ID of layer to insert before.
**kwargs: Additional COGLayer props.
Example:
>>> from anymap_ts import Map
>>> m = Map()
>>> m.add_cog_layer(
... "https://example.com/landcover.tif",
... name="landcover",
... opacity=0.8
... )
"""
layer_id = name or f"cog-{len(self._layers)}"
self.call_js_method(
"addCOGLayer",
id=layer_id,
geotiff=url,
opacity=opacity,
visible=visible,
debug=debug,
debugOpacity=debug_opacity,
maxError=max_error,
fitBounds=fit_bounds,
beforeId=before_id,
**kwargs,
)
self._layers = {
**self._layers,
layer_id: {
"id": layer_id,
"type": "cog",
"url": url,
},
}
def remove_cog_layer(self, layer_id: str) -> None:
"""Remove a COG layer.
Args:
layer_id: Layer identifier to remove.
"""
if layer_id in self._layers:
layers = dict(self._layers)
del layers[layer_id]
self._layers = layers
self.call_js_method("removeCOGLayer", layer_id)
# -------------------------------------------------------------------------
# Zarr Layer (@carbonplan/zarr-layer)
# -------------------------------------------------------------------------
def add_zarr_layer(
self,
url: str,
variable: str,
name: Optional[str] = None,
colormap: Optional[List[str]] = None,
clim: Optional[Tuple[float, float]] = None,
opacity: float = 1.0,
selector: Optional[Dict[str, Any]] = None,
minzoom: int = 0,
maxzoom: int = 22,
fill_value: Optional[float] = None,
spatial_dimensions: Optional[Dict[str, str]] = None,
zarr_version: Optional[int] = None,
bounds: Optional[List[float]] = None,
**kwargs,
) -> None:
"""Add a Zarr dataset layer for visualizing multidimensional array data.
This method renders Zarr pyramid datasets directly in the browser using
GPU-accelerated WebGL rendering via @carbonplan/zarr-layer.
Args:
url: URL to the Zarr store (pyramid format recommended).
variable: Variable name in the Zarr dataset to visualize.
name: Layer ID. If None, auto-generated.
colormap: List of hex color strings for visualization.
Example: ['#0000ff', '#ffff00', '#ff0000'] (blue-yellow-red).
Default: ['#000000', '#ffffff'] (black to white).
clim: Color range as (min, max) tuple.
Default: (0, 100).
opacity: Layer opacity (0-1).
selector: Dimension selector for multi-dimensional data.
Example: {"month": 4} to select 4th month.
minzoom: Minimum zoom level for rendering.
maxzoom: Maximum zoom level for rendering.
fill_value: No-data value (auto-detected from metadata if not set).
spatial_dimensions: Custom spatial dimension names.
Example: {"lat": "y", "lon": "x"} for non-standard names.
zarr_version: Zarr format version (2 or 3). Auto-detected if not set.
bounds: Explicit spatial bounds [xMin, yMin, xMax, yMax].
Units depend on CRS: degrees for EPSG:4326, meters for EPSG:3857.
**kwargs: Additional ZarrLayer props.
Example:
>>> from anymap_ts import Map
>>> m = Map()
>>> m.add_zarr_layer(
... "https://example.com/climate.zarr",
... variable="temperature",
... clim=(270, 310),
... colormap=['#0000ff', '#ffff00', '#ff0000'],
... selector={"month": 7}
... )
"""
layer_id = name or f"zarr-{len(self._layers)}"
self.call_js_method(
"addZarrLayer",
id=layer_id,
source=url,
variable=variable,
colormap=colormap or ["#000000", "#ffffff"],
clim=list(clim) if clim else [0, 100],
opacity=opacity,
selector=selector or {},
minzoom=minzoom,
maxzoom=maxzoom,
fillValue=fill_value,
spatialDimensions=spatial_dimensions,
zarrVersion=zarr_version,
bounds=bounds,
**kwargs,
)
self._layers = {
**self._layers,
layer_id: {
"id": layer_id,
"type": "zarr",
"url": url,
"variable": variable,
},
}
def remove_zarr_layer(self, layer_id: str) -> None:
"""Remove a Zarr layer.
Args:
layer_id: Layer identifier to remove.
"""
if layer_id in self._layers:
layers = dict(self._layers)
del layers[layer_id]
self._layers = layers
self.call_js_method("removeZarrLayer", layer_id)
def update_zarr_layer(
self,
layer_id: str,
selector: Optional[Dict[str, Any]] = None,
clim: Optional[Tuple[float, float]] = None,
colormap: Optional[List[str]] = None,
opacity: Optional[float] = None,
) -> None:
"""Update a Zarr layer's properties dynamically.
Args:
layer_id: Layer identifier.
selector: New dimension selector.
clim: New color range.
colormap: New colormap.
opacity: New opacity value (0-1).
"""
update_kwargs: Dict[str, Any] = {"id": layer_id}
if selector is not None:
update_kwargs["selector"] = selector
if clim is not None:
update_kwargs["clim"] = list(clim)
if colormap is not None:
update_kwargs["colormap"] = colormap
if opacity is not None:
update_kwargs["opacity"] = opacity
self.call_js_method("updateZarrLayer", **update_kwargs)
# -------------------------------------------------------------------------
# Arc Layer (deck.gl)
# -------------------------------------------------------------------------
def add_arc_layer(
self,
data: Any,
name: Optional[str] = None,
get_source_position: Union[str, Any] = "source",
get_target_position: Union[str, Any] = "target",
get_source_color: Optional[List[int]] = None,
get_target_color: Optional[List[int]] = None,
get_width: Union[float, str] = 1,
get_height: float = 1,
great_circle: bool = False,
pickable: bool = True,
opacity: float = 0.8,
**kwargs,
) -> None:
"""Add an arc layer for origin-destination visualization using deck.gl.
Arc layers are ideal for visualizing connections between locations,
such as flight routes, migration patterns, or network flows.
Args:
data: Array of data objects with source/target coordinates.
Each object should have source and target positions.
name: Layer ID. If None, auto-generated.
get_source_position: Accessor for source position [lng, lat].
Can be a string (property name) or a value.
get_target_position: Accessor for target position [lng, lat].
Can be a string (property name) or a value.
get_source_color: Source end color as [r, g, b, a].
Default: [51, 136, 255, 255] (blue).
get_target_color: Target end color as [r, g, b, a].
Default: [255, 136, 51, 255] (orange).
get_width: Arc width in pixels. Can be a number or accessor.
get_height: Arc height multiplier. Higher values create more curved arcs.
great_circle: Whether to draw arcs along great circles.
pickable: Whether layer responds to hover/click events.
opacity: Layer opacity (0-1).
**kwargs: Additional ArcLayer props.
Example:
>>> from anymap_ts import MapLibreMap
>>> m = MapLibreMap()
>>> arcs = [
... {"source": [-122.4, 37.8], "target": [-73.9, 40.7]},
... {"source": [-122.4, 37.8], "target": [-0.1, 51.5]},
... ]
>>> m.add_arc_layer(arcs, name="flights")
"""
layer_id = name or f"arc-{len(self._layers)}"
processed_data = self._process_deck_data(data)
self.call_js_method(
"addArcLayer",
id=layer_id,
data=processed_data,
getSourcePosition=get_source_position,
getTargetPosition=get_target_position,
getSourceColor=get_source_color or [51, 136, 255, 255],
getTargetColor=get_target_color or [255, 136, 51, 255],
getWidth=get_width,
getHeight=get_height,
greatCircle=great_circle,
pickable=pickable,
opacity=opacity,
**kwargs,
)
self._layers = {
**self._layers,
layer_id: {
"id": layer_id,
"type": "arc",
},
}
def remove_arc_layer(self, layer_id: str) -> None:
"""Remove an arc layer.
Args:
layer_id: Layer identifier to remove.
"""
if layer_id in self._layers:
layers = dict(self._layers)
del layers[layer_id]
self._layers = layers
self.call_js_method("removeArcLayer", layer_id)
# -------------------------------------------------------------------------
# PointCloud Layer (deck.gl)
# -------------------------------------------------------------------------
def add_point_cloud_layer(
self,
data: Any,
name: Optional[str] = None,
get_position: Union[str, Any] = "position",
get_color: Optional[Union[List[int], str]] = None,
get_normal: Optional[Union[str, Any]] = None,
point_size: float = 2,
size_units: str = "pixels",
pickable: bool = True,
opacity: float = 1.0,
material: bool = True,
coordinate_system: Optional[int] = None,
coordinate_origin: Optional[List[float]] = None,
**kwargs,
) -> None:
"""Add a point cloud layer for 3D point visualization using deck.gl.
Point cloud layers render large collections of 3D points, ideal for
LiDAR data, photogrammetry outputs, or any 3D point dataset.
Args:
data: Array of point data with positions. Each point should have
x, y, z coordinates (or position array).
name: Layer ID. If None, auto-generated.
get_position: Accessor for point position [x, y, z].
Can be a string (property name) or a value.
get_color: Accessor or value for point color [r, g, b, a].
Default: [255, 255, 255, 255] (white).
get_normal: Accessor for point normal [nx, ny, nz] for lighting.
Default: [0, 0, 1] (pointing up).
point_size: Point size in pixels or meters (depends on size_units).
size_units: Size units: 'pixels', 'meters', or 'common'.
pickable: Whether layer responds to hover/click events.
opacity: Layer opacity (0-1).
material: Whether to enable lighting effects.
coordinate_system: Coordinate system for positions.
coordinate_origin: Origin for coordinate system [x, y, z].
**kwargs: Additional PointCloudLayer props.
Example:
>>> from anymap_ts import MapLibreMap
>>> import numpy as np
>>> m = MapLibreMap(pitch=45)
>>> points = [
... {"position": [-122.4, 37.8, 100], "color": [255, 0, 0, 255]},
... {"position": [-122.3, 37.7, 200], "color": [0, 255, 0, 255]},
... ]
>>> m.add_point_cloud_layer(points, point_size=5)
"""
layer_id = name or f"pointcloud-{len(self._layers)}"
processed_data = self._process_deck_data(data)
self.call_js_method(
"addPointCloudLayer",
id=layer_id,
data=processed_data,
getPosition=get_position,
getColor=get_color or [255, 255, 255, 255],
getNormal=get_normal,
pointSize=point_size,
sizeUnits=size_units,
pickable=pickable,
opacity=opacity,
material=material,
coordinateSystem=coordinate_system,
coordinateOrigin=coordinate_origin,
**kwargs,
)
self._layers = {
**self._layers,
layer_id: {
"id": layer_id,
"type": "pointcloud",
},
}
def remove_point_cloud_layer(self, layer_id: str) -> None:
"""Remove a point cloud layer.
Args:
layer_id: Layer identifier to remove.
"""
if layer_id in self._layers:
layers = dict(self._layers)
del layers[layer_id]
self._layers = layers
self.call_js_method("removePointCloudLayer", layer_id)
def _process_deck_data(self, data: Any) -> Any:
"""Process data for deck.gl layers.
Handles GeoDataFrame, file paths, GeoJSON, and list of dicts.
Args:
data: Input data in various formats.
Returns:
Processed data suitable for deck.gl layers.
"""
# Handle GeoDataFrame
if hasattr(data, "__geo_interface__"):
return data.__geo_interface__
# Handle file paths
if isinstance(data, (str, Path)):
path = Path(data)
if path.exists():
try:
import geopandas as gpd
gdf = gpd.read_file(path)
return gdf.__geo_interface__
except ImportError:
pass
# Return as-is for lists, dicts, etc.
return data
# -------------------------------------------------------------------------
# Layer Management
# -------------------------------------------------------------------------
def add_layer(
self,
layer_id: str,
layer_type: str,
source: Union[str, Dict],
paint: Optional[Dict] = None,
layout: Optional[Dict] = None,
before_id: Optional[str] = None,
**kwargs,
) -> None:
"""Add a generic layer to the map.
Args:
layer_id: Unique layer identifier
layer_type: MapLibre layer type
source: Source ID or source configuration dict
paint: Paint properties
layout: Layout properties
before_id: ID of layer to insert before
**kwargs: Additional layer options
"""
layer_config = {
"id": layer_id,
"type": layer_type,
"paint": paint or {},
"layout": layout or {},
**kwargs,
}
if isinstance(source, str):
layer_config["source"] = source
else:
source_id = f"{layer_id}-source"
self._sources = {**self._sources, source_id: source}
self.call_js_method("addSource", source_id, **source)
layer_config["source"] = source_id
self._layers = {**self._layers, layer_id: layer_config}
self.call_js_method("addLayer", beforeId=before_id, **layer_config)
def remove_layer(self, layer_id: str) -> None:
"""Remove a layer from the map.
Args:
layer_id: Layer identifier to remove
"""
if layer_id in self._layers:
layers = dict(self._layers)
del layers[layer_id]
self._layers = layers
self.call_js_method("removeLayer", layer_id)
def set_visibility(self, layer_id: str, visible: bool) -> None:
"""Set layer visibility.
Args:
layer_id: Layer identifier
visible: Whether layer should be visible
"""
self.call_js_method("setVisibility", layer_id, visible)
def set_opacity(self, layer_id: str, opacity: float) -> None:
"""Set layer opacity.
Args:
layer_id: Layer identifier
opacity: Opacity value between 0 and 1
"""
self.call_js_method("setOpacity", layer_id, opacity)
# -------------------------------------------------------------------------
# Controls
# -------------------------------------------------------------------------
def add_control(
self,
control_type: str,
position: str = "top-right",
**kwargs,
) -> None:
"""Add a map control.
Args:
control_type: Type of control ('navigation', 'scale', 'fullscreen', etc.)
position: Control position
**kwargs: Control-specific options
"""
self.call_js_method("addControl", control_type, position=position, **kwargs)
self._controls = {
**self._controls,
control_type: {"type": control_type, "position": position, **kwargs},
}
def remove_control(self, control_type: str) -> None:
"""Remove a map control.
Args:
control_type: Type of control to remove
"""
self.call_js_method("removeControl", control_type)
if control_type in self._controls:
controls = dict(self._controls)
del controls[control_type]
self._controls = controls
def add_layer_control(
self,
layers: Optional[List[str]] = None,
position: str = "top-right",
collapsed: bool = True,
) -> None:
"""Add a layer visibility control.
Uses maplibre-gl-layer-control for layer toggling and opacity.
Args:
layers: List of layer IDs to include (None = all layers)
position: Control position
collapsed: Whether control starts collapsed
"""
if layers is None:
layers = list(self._layers.keys())
self.call_js_method(
"addLayerControl",
layers=layers,
position=position,
collapsed=collapsed,
)
self._controls = {
**self._controls,
"layer-control": {"layers": layers, "position": position},
}
# -------------------------------------------------------------------------
# Drawing
# -------------------------------------------------------------------------
def add_draw_control(
self,
position: str = "top-right",
draw_modes: Optional[List[str]] = None,
edit_modes: Optional[List[str]] = None,
collapsed: bool = False,
**kwargs,
) -> None:
"""Add a drawing control using maplibre-gl-geo-editor.
Args:
position: Control position
draw_modes: Drawing modes to enable (e.g., ['polygon', 'line', 'marker'])
edit_modes: Edit modes to enable (e.g., ['select', 'drag', 'delete'])
collapsed: Whether control starts collapsed
**kwargs: Additional geo-editor options
"""
if draw_modes is None:
draw_modes = ["polygon", "line", "rectangle", "circle", "marker"]
if edit_modes is None:
edit_modes = ["select", "drag", "change", "rotate", "delete"]
self.call_js_method(
"addDrawControl",
position=position,
drawModes=draw_modes,
editModes=edit_modes,
collapsed=collapsed,
**kwargs,
)
self._controls = {
**self._controls,
"draw-control": {
"position": position,
"drawModes": draw_modes,
"editModes": edit_modes,
},
}
def get_draw_data(self) -> Dict:
"""Get the current drawn features as GeoJSON.
Returns:
GeoJSON FeatureCollection of drawn features
"""
self.call_js_method("getDrawData")
# Small delay to allow JS to update the trait
import time
time.sleep(0.1)
return self._draw_data or {"type": "FeatureCollection", "features": []}
@property
def draw_data(self) -> Dict:
"""Property to access current draw data."""
return self._draw_data or {"type": "FeatureCollection", "features": []}
def load_draw_data(self, geojson: Dict) -> None:
"""Load GeoJSON features into the drawing layer.
Args:
geojson: GeoJSON FeatureCollection to load
"""
self._draw_data = geojson
self.call_js_method("loadDrawData", geojson)
def clear_draw_data(self) -> None:
"""Clear all drawn features."""
self._draw_data = {"type": "FeatureCollection", "features": []}
self.call_js_method("clearDrawData")
def save_draw_data(
self,
filepath: Union[str, Path],
driver: Optional[str] = None,
) -> None:
"""Save drawn features to a file.
Args:
filepath: Path to save file
driver: Output driver (auto-detected from extension if not provided)
Raises:
ImportError: If geopandas is not installed
"""
try:
import geopandas as gpd
except ImportError:
raise ImportError(
"geopandas is required to save draw data. "
"Install with: pip install anymap-ts[vector]"
)
data = self.get_draw_data()
if not data.get("features"):
print("No features to save")
return
gdf = gpd.GeoDataFrame.from_features(data["features"])
filepath = Path(filepath)
# Infer driver from extension
if driver is None:
ext = filepath.suffix.lower()
driver_map = {
".geojson": "GeoJSON",
".json": "GeoJSON",
".shp": "ESRI Shapefile",
".gpkg": "GPKG",
}
driver = driver_map.get(ext, "GeoJSON")
gdf.to_file(filepath, driver=driver)
# -------------------------------------------------------------------------
# HTML Export
# -------------------------------------------------------------------------
def _generate_html_template(self) -> str:
"""Generate standalone HTML for the map."""
template_path = Path(__file__).parent / "templates" / "maplibre.html"
if template_path.exists():
template = template_path.read_text(encoding="utf-8")
else:
template = self._get_default_template()
# Serialize state
state = {
"center": self.center,
"zoom": self.zoom,
"style": self.style,
"bearing": self.bearing,
"pitch": self.pitch,
"width": self.width,
"height": self.height,
"layers": self._layers,
"sources": self._sources,
"controls": self._controls,
"js_calls": self._js_calls,
}
template = template.replace("{{state}}", json.dumps(state, indent=2))
return template
def _get_default_template(self) -> str:
"""Get default HTML template."""
return """<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>{{title}}</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<script src="https://unpkg.com/maplibre-gl@5/dist/maplibre-gl.js"></script>
<link href="https://unpkg.com/maplibre-gl@5/dist/maplibre-gl.css" rel="stylesheet" />
<style>
body { margin: 0; padding: 0; }
#map { position: absolute; top: 0; bottom: 0; width: 100%; }
</style>
</head>
<body>
<div id="map"></div>
<script>
const state = {{state}};
const map = new maplibregl.Map({
container: 'map',
style: state.style,
center: state.center,
zoom: state.zoom,
bearing: state.bearing || 0,
pitch: state.pitch || 0
});
map.on('load', function() {
// Replay JS calls
for (const call of state.js_calls || []) {
try {
executeMethod(call.method, call.args, call.kwargs);
} catch (e) {
console.error('Error executing', call.method, e);
}
}
});
function executeMethod(method, args, kwargs) {
switch (method) {
case 'addBasemap':
const url = args[0];
const name = kwargs.name || 'basemap';
const sourceId = 'basemap-' + name;
if (!map.getSource(sourceId)) {
map.addSource(sourceId, {
type: 'raster',
tiles: [url],
tileSize: 256,
attribution: kwargs.attribution || ''
});
}
if (!map.getLayer(sourceId)) {
map.addLayer({
id: sourceId,
type: 'raster',
source: sourceId
});
}
break;
case 'addGeoJSON':
const layerName = kwargs.name;
const sourceIdGeo = layerName + '-source';
if (!map.getSource(sourceIdGeo)) {
map.addSource(sourceIdGeo, {
type: 'geojson',
data: kwargs.data
});
}
if (!map.getLayer(layerName)) {
map.addLayer({
id: layerName,
type: kwargs.layerType || 'circle',
source: sourceIdGeo,
paint: kwargs.paint || {}
});
}
if (kwargs.fitBounds && kwargs.bounds) {
map.fitBounds([
[kwargs.bounds[0], kwargs.bounds[1]],
[kwargs.bounds[2], kwargs.bounds[3]]
], { padding: 50 });
}
break;
case 'addTileLayer':
const tileUrl = args[0];
const tileName = kwargs.name;
const tileSourceId = tileName + '-source';
if (!map.getSource(tileSourceId)) {
map.addSource(tileSourceId, {
type: 'raster',
tiles: [tileUrl],
tileSize: 256,
attribution: kwargs.attribution || ''
});
}
if (!map.getLayer(tileName)) {
map.addLayer({
id: tileName,
type: 'raster',
source: tileSourceId
});
}
break;
case 'addControl':
const controlType = args[0];
const position = kwargs.position || 'top-right';
let control;
switch (controlType) {
case 'navigation':
control = new maplibregl.NavigationControl();
break;
case 'scale':
control = new maplibregl.ScaleControl();
break;
case 'fullscreen':
control = new maplibregl.FullscreenControl();
break;
}
if (control) {
map.addControl(control, position);
}
break;
case 'flyTo':
map.flyTo({
center: [args[0], args[1]],
zoom: kwargs.zoom,
duration: kwargs.duration || 2000
});
break;
case 'fitBounds':
const bounds = args[0];
map.fitBounds([
[bounds[0], bounds[1]],
[bounds[2], bounds[3]]
], {
padding: kwargs.padding || 50,
duration: kwargs.duration || 1000
});
break;
default:
console.log('Unknown method:', method);
}
}
</script>
</body>
</html>"""
draw_data: Dict
property
readonly
¶
Property to access current draw data.
__init__(self, center=(0.0, 0.0), zoom=2.0, width='100%', height='600px', style='https://demotiles.maplibre.org/style.json', bearing=0.0, pitch=0.0, controls=None, **kwargs)
special
¶
Initialize a MapLibre map.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
center |
Tuple[float, float] |
Map center as (longitude, latitude) |
(0.0, 0.0) |
zoom |
float |
Initial zoom level |
2.0 |
width |
str |
Map width as CSS string |
'100%' |
height |
str |
Map height as CSS string |
'600px' |
style |
Union[str, Dict] |
MapLibre style URL or style object |
'https://demotiles.maplibre.org/style.json' |
bearing |
float |
Map bearing in degrees |
0.0 |
pitch |
float |
Map pitch in degrees |
0.0 |
controls |
Optional[Dict[str, Any]] |
Dict of controls to add. If None, defaults to {"navigation": True, "fullscreen": True, "globe": True, "layer-control": True}. Use {"layer-control": {"collapsed": True}} for custom options. |
None |
**kwargs |
Additional widget arguments |
{} |
Source code in anymap_ts/maplibre.py
def __init__(
self,
center: Tuple[float, float] = (0.0, 0.0),
zoom: float = 2.0,
width: str = "100%",
height: str = "600px",
style: Union[str, Dict] = "https://demotiles.maplibre.org/style.json",
bearing: float = 0.0,
pitch: float = 0.0,
controls: Optional[Dict[str, Any]] = None,
**kwargs,
):
"""Initialize a MapLibre map.
Args:
center: Map center as (longitude, latitude)
zoom: Initial zoom level
width: Map width as CSS string
height: Map height as CSS string
style: MapLibre style URL or style object
bearing: Map bearing in degrees
pitch: Map pitch in degrees
controls: Dict of controls to add. If None, defaults to
{"navigation": True, "fullscreen": True, "globe": True, "layer-control": True}.
Use {"layer-control": {"collapsed": True}} for custom options.
**kwargs: Additional widget arguments
"""
# Handle style shortcuts
if isinstance(style, str) and not style.startswith("http"):
try:
style = get_maplibre_style(style)
except ValueError:
pass # Use as-is
super().__init__(
center=list(center),
zoom=zoom,
width=width,
height=height,
style=style,
bearing=bearing,
pitch=pitch,
**kwargs,
)
# Initialize layer dictionary
self._layer_dict = {"Background": []}
# Add default controls
if controls is None:
controls = {
"navigation": True,
"fullscreen": True,
"globe": True,
"layer-control": True,
}
for control_name, config in controls.items():
if config:
if control_name == "layer-control":
# Layer control uses a separate method
self.add_layer_control(
**(config if isinstance(config, dict) else {})
)
else:
self.add_control(
control_name, **(config if isinstance(config, dict) else {})
)
add_arc_layer(self, data, name=None, get_source_position='source', get_target_position='target', get_source_color=None, get_target_color=None, get_width=1, get_height=1, great_circle=False, pickable=True, opacity=0.8, **kwargs)
¶
Add an arc layer for origin-destination visualization using deck.gl.
Arc layers are ideal for visualizing connections between locations, such as flight routes, migration patterns, or network flows.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data |
Any |
Array of data objects with source/target coordinates. Each object should have source and target positions. |
required |
name |
Optional[str] |
Layer ID. If None, auto-generated. |
None |
get_source_position |
Union[str, Any] |
Accessor for source position [lng, lat]. Can be a string (property name) or a value. |
'source' |
get_target_position |
Union[str, Any] |
Accessor for target position [lng, lat]. Can be a string (property name) or a value. |
'target' |
get_source_color |
Optional[List[int]] |
Source end color as [r, g, b, a]. Default: [51, 136, 255, 255] (blue). |
None |
get_target_color |
Optional[List[int]] |
Target end color as [r, g, b, a]. Default: [255, 136, 51, 255] (orange). |
None |
get_width |
Union[float, str] |
Arc width in pixels. Can be a number or accessor. |
1 |
get_height |
float |
Arc height multiplier. Higher values create more curved arcs. |
1 |
great_circle |
bool |
Whether to draw arcs along great circles. |
False |
pickable |
bool |
Whether layer responds to hover/click events. |
True |
opacity |
float |
Layer opacity (0-1). |
0.8 |
**kwargs |
Additional ArcLayer props. |
{} |
Examples:
>>> from anymap_ts import MapLibreMap
>>> m = MapLibreMap()
>>> arcs = [
... {"source": [-122.4, 37.8], "target": [-73.9, 40.7]},
... {"source": [-122.4, 37.8], "target": [-0.1, 51.5]},
... ]
>>> m.add_arc_layer(arcs, name="flights")
Source code in anymap_ts/maplibre.py
def add_arc_layer(
self,
data: Any,
name: Optional[str] = None,
get_source_position: Union[str, Any] = "source",
get_target_position: Union[str, Any] = "target",
get_source_color: Optional[List[int]] = None,
get_target_color: Optional[List[int]] = None,
get_width: Union[float, str] = 1,
get_height: float = 1,
great_circle: bool = False,
pickable: bool = True,
opacity: float = 0.8,
**kwargs,
) -> None:
"""Add an arc layer for origin-destination visualization using deck.gl.
Arc layers are ideal for visualizing connections between locations,
such as flight routes, migration patterns, or network flows.
Args:
data: Array of data objects with source/target coordinates.
Each object should have source and target positions.
name: Layer ID. If None, auto-generated.
get_source_position: Accessor for source position [lng, lat].
Can be a string (property name) or a value.
get_target_position: Accessor for target position [lng, lat].
Can be a string (property name) or a value.
get_source_color: Source end color as [r, g, b, a].
Default: [51, 136, 255, 255] (blue).
get_target_color: Target end color as [r, g, b, a].
Default: [255, 136, 51, 255] (orange).
get_width: Arc width in pixels. Can be a number or accessor.
get_height: Arc height multiplier. Higher values create more curved arcs.
great_circle: Whether to draw arcs along great circles.
pickable: Whether layer responds to hover/click events.
opacity: Layer opacity (0-1).
**kwargs: Additional ArcLayer props.
Example:
>>> from anymap_ts import MapLibreMap
>>> m = MapLibreMap()
>>> arcs = [
... {"source": [-122.4, 37.8], "target": [-73.9, 40.7]},
... {"source": [-122.4, 37.8], "target": [-0.1, 51.5]},
... ]
>>> m.add_arc_layer(arcs, name="flights")
"""
layer_id = name or f"arc-{len(self._layers)}"
processed_data = self._process_deck_data(data)
self.call_js_method(
"addArcLayer",
id=layer_id,
data=processed_data,
getSourcePosition=get_source_position,
getTargetPosition=get_target_position,
getSourceColor=get_source_color or [51, 136, 255, 255],
getTargetColor=get_target_color or [255, 136, 51, 255],
getWidth=get_width,
getHeight=get_height,
greatCircle=great_circle,
pickable=pickable,
opacity=opacity,
**kwargs,
)
self._layers = {
**self._layers,
layer_id: {
"id": layer_id,
"type": "arc",
},
}
add_basemap(self, basemap='OpenStreetMap', attribution=None, **kwargs)
¶
Add a basemap layer.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
basemap |
str |
Name of basemap provider (e.g., "OpenStreetMap", "CartoDB.Positron") |
'OpenStreetMap' |
attribution |
Optional[str] |
Custom attribution text |
None |
**kwargs |
Additional options |
{} |
Source code in anymap_ts/maplibre.py
def add_basemap(
self,
basemap: str = "OpenStreetMap",
attribution: Optional[str] = None,
**kwargs,
) -> None:
"""Add a basemap layer.
Args:
basemap: Name of basemap provider (e.g., "OpenStreetMap", "CartoDB.Positron")
attribution: Custom attribution text
**kwargs: Additional options
"""
url, default_attribution = get_basemap_url(basemap)
self.call_js_method(
"addBasemap",
url,
attribution=attribution or default_attribution,
name=basemap,
**kwargs,
)
# Track in layer dict
basemaps = self._layer_dict.get("Basemaps", [])
if basemap not in basemaps:
self._layer_dict = {
**self._layer_dict,
"Basemaps": basemaps + [basemap],
}
add_cog_layer(self, url, name=None, opacity=1.0, visible=True, debug=False, debug_opacity=0.25, max_error=0.125, fit_bounds=True, before_id=None, **kwargs)
¶
Add a Cloud Optimized GeoTIFF (COG) layer using @developmentseed/deck.gl-geotiff.
This method renders COG files directly in the browser using GPU-accelerated deck.gl-geotiff rendering with automatic reprojection support.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
url |
str |
URL to the Cloud Optimized GeoTIFF file. |
required |
name |
Optional[str] |
Layer ID. If None, auto-generated. |
None |
opacity |
float |
Layer opacity (0-1). |
1.0 |
visible |
bool |
Whether layer is visible. |
True |
debug |
bool |
Show reprojection mesh for debugging. |
False |
debug_opacity |
float |
Opacity of debug mesh (0-1). |
0.25 |
max_error |
float |
Maximum reprojection error in pixels. Lower values create denser mesh for better accuracy. |
0.125 |
fit_bounds |
bool |
Whether to fit map to COG bounds after loading. |
True |
before_id |
Optional[str] |
ID of layer to insert before. |
None |
**kwargs |
Additional COGLayer props. |
{} |
Examples:
>>> from anymap_ts import Map
>>> m = Map()
>>> m.add_cog_layer(
... "https://example.com/landcover.tif",
... name="landcover",
... opacity=0.8
... )
Source code in anymap_ts/maplibre.py
def add_cog_layer(
self,
url: str,
name: Optional[str] = None,
opacity: float = 1.0,
visible: bool = True,
debug: bool = False,
debug_opacity: float = 0.25,
max_error: float = 0.125,
fit_bounds: bool = True,
before_id: Optional[str] = None,
**kwargs,
) -> None:
"""Add a Cloud Optimized GeoTIFF (COG) layer using @developmentseed/deck.gl-geotiff.
This method renders COG files directly in the browser using GPU-accelerated
deck.gl-geotiff rendering with automatic reprojection support.
Args:
url: URL to the Cloud Optimized GeoTIFF file.
name: Layer ID. If None, auto-generated.
opacity: Layer opacity (0-1).
visible: Whether layer is visible.
debug: Show reprojection mesh for debugging.
debug_opacity: Opacity of debug mesh (0-1).
max_error: Maximum reprojection error in pixels. Lower values
create denser mesh for better accuracy.
fit_bounds: Whether to fit map to COG bounds after loading.
before_id: ID of layer to insert before.
**kwargs: Additional COGLayer props.
Example:
>>> from anymap_ts import Map
>>> m = Map()
>>> m.add_cog_layer(
... "https://example.com/landcover.tif",
... name="landcover",
... opacity=0.8
... )
"""
layer_id = name or f"cog-{len(self._layers)}"
self.call_js_method(
"addCOGLayer",
id=layer_id,
geotiff=url,
opacity=opacity,
visible=visible,
debug=debug,
debugOpacity=debug_opacity,
maxError=max_error,
fitBounds=fit_bounds,
beforeId=before_id,
**kwargs,
)
self._layers = {
**self._layers,
layer_id: {
"id": layer_id,
"type": "cog",
"url": url,
},
}
add_control(self, control_type, position='top-right', **kwargs)
¶
Add a map control.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
control_type |
str |
Type of control ('navigation', 'scale', 'fullscreen', etc.) |
required |
position |
str |
Control position |
'top-right' |
**kwargs |
Control-specific options |
{} |
Source code in anymap_ts/maplibre.py
def add_control(
self,
control_type: str,
position: str = "top-right",
**kwargs,
) -> None:
"""Add a map control.
Args:
control_type: Type of control ('navigation', 'scale', 'fullscreen', etc.)
position: Control position
**kwargs: Control-specific options
"""
self.call_js_method("addControl", control_type, position=position, **kwargs)
self._controls = {
**self._controls,
control_type: {"type": control_type, "position": position, **kwargs},
}
add_draw_control(self, position='top-right', draw_modes=None, edit_modes=None, collapsed=False, **kwargs)
¶
Add a drawing control using maplibre-gl-geo-editor.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
position |
str |
Control position |
'top-right' |
draw_modes |
Optional[List[str]] |
Drawing modes to enable (e.g., ['polygon', 'line', 'marker']) |
None |
edit_modes |
Optional[List[str]] |
Edit modes to enable (e.g., ['select', 'drag', 'delete']) |
None |
collapsed |
bool |
Whether control starts collapsed |
False |
**kwargs |
Additional geo-editor options |
{} |
Source code in anymap_ts/maplibre.py
def add_draw_control(
self,
position: str = "top-right",
draw_modes: Optional[List[str]] = None,
edit_modes: Optional[List[str]] = None,
collapsed: bool = False,
**kwargs,
) -> None:
"""Add a drawing control using maplibre-gl-geo-editor.
Args:
position: Control position
draw_modes: Drawing modes to enable (e.g., ['polygon', 'line', 'marker'])
edit_modes: Edit modes to enable (e.g., ['select', 'drag', 'delete'])
collapsed: Whether control starts collapsed
**kwargs: Additional geo-editor options
"""
if draw_modes is None:
draw_modes = ["polygon", "line", "rectangle", "circle", "marker"]
if edit_modes is None:
edit_modes = ["select", "drag", "change", "rotate", "delete"]
self.call_js_method(
"addDrawControl",
position=position,
drawModes=draw_modes,
editModes=edit_modes,
collapsed=collapsed,
**kwargs,
)
self._controls = {
**self._controls,
"draw-control": {
"position": position,
"drawModes": draw_modes,
"editModes": edit_modes,
},
}
add_geojson(self, data, layer_type=None, paint=None, name=None, fit_bounds=True, **kwargs)
¶
Add GeoJSON data to the map.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data |
Union[str, Dict] |
GeoJSON dict or URL to GeoJSON file |
required |
layer_type |
Optional[str] |
MapLibre layer type |
None |
paint |
Optional[Dict] |
MapLibre paint properties |
None |
name |
Optional[str] |
Layer name |
None |
fit_bounds |
bool |
Whether to fit map to data bounds |
True |
**kwargs |
Additional layer options |
{} |
Source code in anymap_ts/maplibre.py
def add_geojson(
self,
data: Union[str, Dict],
layer_type: Optional[str] = None,
paint: Optional[Dict] = None,
name: Optional[str] = None,
fit_bounds: bool = True,
**kwargs,
) -> None:
"""Add GeoJSON data to the map.
Args:
data: GeoJSON dict or URL to GeoJSON file
layer_type: MapLibre layer type
paint: MapLibre paint properties
name: Layer name
fit_bounds: Whether to fit map to data bounds
**kwargs: Additional layer options
"""
self.add_vector(
data,
layer_type=layer_type,
paint=paint,
name=name,
fit_bounds=fit_bounds,
**kwargs,
)
add_layer(self, layer_id, layer_type, source, paint=None, layout=None, before_id=None, **kwargs)
¶
Add a generic layer to the map.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
layer_id |
str |
Unique layer identifier |
required |
layer_type |
str |
MapLibre layer type |
required |
source |
Union[str, Dict] |
Source ID or source configuration dict |
required |
paint |
Optional[Dict] |
Paint properties |
None |
layout |
Optional[Dict] |
Layout properties |
None |
before_id |
Optional[str] |
ID of layer to insert before |
None |
**kwargs |
Additional layer options |
{} |
Source code in anymap_ts/maplibre.py
def add_layer(
self,
layer_id: str,
layer_type: str,
source: Union[str, Dict],
paint: Optional[Dict] = None,
layout: Optional[Dict] = None,
before_id: Optional[str] = None,
**kwargs,
) -> None:
"""Add a generic layer to the map.
Args:
layer_id: Unique layer identifier
layer_type: MapLibre layer type
source: Source ID or source configuration dict
paint: Paint properties
layout: Layout properties
before_id: ID of layer to insert before
**kwargs: Additional layer options
"""
layer_config = {
"id": layer_id,
"type": layer_type,
"paint": paint or {},
"layout": layout or {},
**kwargs,
}
if isinstance(source, str):
layer_config["source"] = source
else:
source_id = f"{layer_id}-source"
self._sources = {**self._sources, source_id: source}
self.call_js_method("addSource", source_id, **source)
layer_config["source"] = source_id
self._layers = {**self._layers, layer_id: layer_config}
self.call_js_method("addLayer", beforeId=before_id, **layer_config)
add_layer_control(self, layers=None, position='top-right', collapsed=True)
¶
Add a layer visibility control.
Uses maplibre-gl-layer-control for layer toggling and opacity.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
layers |
Optional[List[str]] |
List of layer IDs to include (None = all layers) |
None |
position |
str |
Control position |
'top-right' |
collapsed |
bool |
Whether control starts collapsed |
True |
Source code in anymap_ts/maplibre.py
def add_layer_control(
self,
layers: Optional[List[str]] = None,
position: str = "top-right",
collapsed: bool = True,
) -> None:
"""Add a layer visibility control.
Uses maplibre-gl-layer-control for layer toggling and opacity.
Args:
layers: List of layer IDs to include (None = all layers)
position: Control position
collapsed: Whether control starts collapsed
"""
if layers is None:
layers = list(self._layers.keys())
self.call_js_method(
"addLayerControl",
layers=layers,
position=position,
collapsed=collapsed,
)
self._controls = {
**self._controls,
"layer-control": {"layers": layers, "position": position},
}
add_point_cloud_layer(self, data, name=None, get_position='position', get_color=None, get_normal=None, point_size=2, size_units='pixels', pickable=True, opacity=1.0, material=True, coordinate_system=None, coordinate_origin=None, **kwargs)
¶
Add a point cloud layer for 3D point visualization using deck.gl.
Point cloud layers render large collections of 3D points, ideal for LiDAR data, photogrammetry outputs, or any 3D point dataset.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data |
Any |
Array of point data with positions. Each point should have x, y, z coordinates (or position array). |
required |
name |
Optional[str] |
Layer ID. If None, auto-generated. |
None |
get_position |
Union[str, Any] |
Accessor for point position [x, y, z]. Can be a string (property name) or a value. |
'position' |
get_color |
Optional[Union[List[int], str]] |
Accessor or value for point color [r, g, b, a]. Default: [255, 255, 255, 255] (white). |
None |
get_normal |
Optional[Union[str, Any]] |
Accessor for point normal [nx, ny, nz] for lighting. Default: [0, 0, 1] (pointing up). |
None |
point_size |
float |
Point size in pixels or meters (depends on size_units). |
2 |
size_units |
str |
Size units: 'pixels', 'meters', or 'common'. |
'pixels' |
pickable |
bool |
Whether layer responds to hover/click events. |
True |
opacity |
float |
Layer opacity (0-1). |
1.0 |
material |
bool |
Whether to enable lighting effects. |
True |
coordinate_system |
Optional[int] |
Coordinate system for positions. |
None |
coordinate_origin |
Optional[List[float]] |
Origin for coordinate system [x, y, z]. |
None |
**kwargs |
Additional PointCloudLayer props. |
{} |
Examples:
>>> from anymap_ts import MapLibreMap
>>> import numpy as np
>>> m = MapLibreMap(pitch=45)
>>> points = [
... {"position": [-122.4, 37.8, 100], "color": [255, 0, 0, 255]},
... {"position": [-122.3, 37.7, 200], "color": [0, 255, 0, 255]},
... ]
>>> m.add_point_cloud_layer(points, point_size=5)
Source code in anymap_ts/maplibre.py
def add_point_cloud_layer(
self,
data: Any,
name: Optional[str] = None,
get_position: Union[str, Any] = "position",
get_color: Optional[Union[List[int], str]] = None,
get_normal: Optional[Union[str, Any]] = None,
point_size: float = 2,
size_units: str = "pixels",
pickable: bool = True,
opacity: float = 1.0,
material: bool = True,
coordinate_system: Optional[int] = None,
coordinate_origin: Optional[List[float]] = None,
**kwargs,
) -> None:
"""Add a point cloud layer for 3D point visualization using deck.gl.
Point cloud layers render large collections of 3D points, ideal for
LiDAR data, photogrammetry outputs, or any 3D point dataset.
Args:
data: Array of point data with positions. Each point should have
x, y, z coordinates (or position array).
name: Layer ID. If None, auto-generated.
get_position: Accessor for point position [x, y, z].
Can be a string (property name) or a value.
get_color: Accessor or value for point color [r, g, b, a].
Default: [255, 255, 255, 255] (white).
get_normal: Accessor for point normal [nx, ny, nz] for lighting.
Default: [0, 0, 1] (pointing up).
point_size: Point size in pixels or meters (depends on size_units).
size_units: Size units: 'pixels', 'meters', or 'common'.
pickable: Whether layer responds to hover/click events.
opacity: Layer opacity (0-1).
material: Whether to enable lighting effects.
coordinate_system: Coordinate system for positions.
coordinate_origin: Origin for coordinate system [x, y, z].
**kwargs: Additional PointCloudLayer props.
Example:
>>> from anymap_ts import MapLibreMap
>>> import numpy as np
>>> m = MapLibreMap(pitch=45)
>>> points = [
... {"position": [-122.4, 37.8, 100], "color": [255, 0, 0, 255]},
... {"position": [-122.3, 37.7, 200], "color": [0, 255, 0, 255]},
... ]
>>> m.add_point_cloud_layer(points, point_size=5)
"""
layer_id = name or f"pointcloud-{len(self._layers)}"
processed_data = self._process_deck_data(data)
self.call_js_method(
"addPointCloudLayer",
id=layer_id,
data=processed_data,
getPosition=get_position,
getColor=get_color or [255, 255, 255, 255],
getNormal=get_normal,
pointSize=point_size,
sizeUnits=size_units,
pickable=pickable,
opacity=opacity,
material=material,
coordinateSystem=coordinate_system,
coordinateOrigin=coordinate_origin,
**kwargs,
)
self._layers = {
**self._layers,
layer_id: {
"id": layer_id,
"type": "pointcloud",
},
}
add_raster(self, source, name=None, attribution='', indexes=None, colormap=None, vmin=None, vmax=None, nodata=None, fit_bounds=True, **kwargs)
¶
Add a raster layer from a local file using localtileserver.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
source |
str |
Path to local raster file |
required |
name |
Optional[str] |
Layer name |
None |
attribution |
str |
Attribution text |
'' |
indexes |
Optional[List[int]] |
Band indexes to use |
None |
colormap |
Optional[str] |
Colormap name |
None |
vmin |
Optional[float] |
Minimum value for colormap |
None |
vmax |
Optional[float] |
Maximum value for colormap |
None |
nodata |
Optional[float] |
NoData value |
None |
fit_bounds |
bool |
Whether to fit map to raster bounds |
True |
**kwargs |
Additional options |
{} |
Source code in anymap_ts/maplibre.py
def add_raster(
self,
source: str,
name: Optional[str] = None,
attribution: str = "",
indexes: Optional[List[int]] = None,
colormap: Optional[str] = None,
vmin: Optional[float] = None,
vmax: Optional[float] = None,
nodata: Optional[float] = None,
fit_bounds: bool = True,
**kwargs,
) -> None:
"""Add a raster layer from a local file using localtileserver.
Args:
source: Path to local raster file
name: Layer name
attribution: Attribution text
indexes: Band indexes to use
colormap: Colormap name
vmin: Minimum value for colormap
vmax: Maximum value for colormap
nodata: NoData value
fit_bounds: Whether to fit map to raster bounds
**kwargs: Additional options
"""
try:
from localtileserver import TileClient
except ImportError:
raise ImportError(
"localtileserver is required for local raster support. "
"Install with: pip install anymap-ts[raster]"
)
client = TileClient(source)
# Build tile URL with parameters
tile_url = client.get_tile_url()
if indexes:
tile_url = client.get_tile_url(indexes=indexes)
if colormap:
tile_url = client.get_tile_url(colormap=colormap)
if vmin is not None or vmax is not None:
tile_url = client.get_tile_url(
vmin=vmin or client.min, vmax=vmax or client.max
)
if nodata is not None:
tile_url = client.get_tile_url(nodata=nodata)
layer_name = name or Path(source).stem
self.add_tile_layer(
tile_url,
name=layer_name,
attribution=attribution,
**kwargs,
)
# Fit bounds if requested
if fit_bounds:
bounds = client.bounds()
if bounds:
self.fit_bounds([bounds[0], bounds[1], bounds[2], bounds[3]])
add_tile_layer(self, url, name=None, attribution='', min_zoom=0, max_zoom=22, **kwargs)
¶
Add an XYZ tile layer.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
url |
str |
Tile URL template with {x}, {y}, {z} placeholders |
required |
name |
Optional[str] |
Layer name |
None |
attribution |
str |
Attribution text |
'' |
min_zoom |
int |
Minimum zoom level |
0 |
max_zoom |
int |
Maximum zoom level |
22 |
**kwargs |
Additional options |
{} |
Source code in anymap_ts/maplibre.py
def add_tile_layer(
self,
url: str,
name: Optional[str] = None,
attribution: str = "",
min_zoom: int = 0,
max_zoom: int = 22,
**kwargs,
) -> None:
"""Add an XYZ tile layer.
Args:
url: Tile URL template with {x}, {y}, {z} placeholders
name: Layer name
attribution: Attribution text
min_zoom: Minimum zoom level
max_zoom: Maximum zoom level
**kwargs: Additional options
"""
layer_id = name or f"tiles-{len(self._layers)}"
self.call_js_method(
"addTileLayer",
url,
name=layer_id,
attribution=attribution,
minZoom=min_zoom,
maxZoom=max_zoom,
**kwargs,
)
# Track layer
self._layers = {
**self._layers,
layer_id: {
"id": layer_id,
"type": "raster",
"source": f"{layer_id}-source",
},
}
add_vector(self, data, layer_type=None, paint=None, name=None, fit_bounds=True, **kwargs)
¶
Add vector data to the map.
Supports GeoJSON, GeoDataFrame, or file paths to vector formats.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data |
Any |
GeoJSON dict, GeoDataFrame, or path to vector file |
required |
layer_type |
Optional[str] |
MapLibre layer type ('circle', 'line', 'fill', 'symbol') |
None |
paint |
Optional[Dict] |
MapLibre paint properties |
None |
name |
Optional[str] |
Layer name |
None |
fit_bounds |
bool |
Whether to fit map to data bounds |
True |
**kwargs |
Additional layer options |
{} |
Source code in anymap_ts/maplibre.py
def add_vector(
self,
data: Any,
layer_type: Optional[str] = None,
paint: Optional[Dict] = None,
name: Optional[str] = None,
fit_bounds: bool = True,
**kwargs,
) -> None:
"""Add vector data to the map.
Supports GeoJSON, GeoDataFrame, or file paths to vector formats.
Args:
data: GeoJSON dict, GeoDataFrame, or path to vector file
layer_type: MapLibre layer type ('circle', 'line', 'fill', 'symbol')
paint: MapLibre paint properties
name: Layer name
fit_bounds: Whether to fit map to data bounds
**kwargs: Additional layer options
"""
geojson = to_geojson(data)
# Handle URL data
if geojson.get("type") == "url":
self.add_geojson(
geojson["url"],
layer_type=layer_type,
paint=paint,
name=name,
fit_bounds=fit_bounds,
**kwargs,
)
return
layer_id = name or f"vector-{len(self._layers)}"
# Infer layer type if not specified
if layer_type is None:
layer_type = infer_layer_type(geojson)
# Get default paint if not provided
if paint is None:
paint = get_default_paint(layer_type)
# Get bounds
bounds = get_bounds(data) if fit_bounds else None
# Call JavaScript
self.call_js_method(
"addGeoJSON",
data=geojson,
name=layer_id,
layerType=layer_type,
paint=paint,
fitBounds=fit_bounds,
bounds=bounds,
**kwargs,
)
# Track layer
self._layers = {
**self._layers,
layer_id: {
"id": layer_id,
"type": layer_type,
"source": f"{layer_id}-source",
"paint": paint,
},
}
add_zarr_layer(self, url, variable, name=None, colormap=None, clim=None, opacity=1.0, selector=None, minzoom=0, maxzoom=22, fill_value=None, spatial_dimensions=None, zarr_version=None, bounds=None, **kwargs)
¶
Add a Zarr dataset layer for visualizing multidimensional array data.
This method renders Zarr pyramid datasets directly in the browser using GPU-accelerated WebGL rendering via @carbonplan/zarr-layer.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
url |
str |
URL to the Zarr store (pyramid format recommended). |
required |
variable |
str |
Variable name in the Zarr dataset to visualize. |
required |
name |
Optional[str] |
Layer ID. If None, auto-generated. |
None |
colormap |
Optional[List[str]] |
List of hex color strings for visualization. Example: ['#0000ff', '#ffff00', '#ff0000'] (blue-yellow-red). Default: ['#000000', '#ffffff'] (black to white). |
None |
clim |
Optional[Tuple[float, float]] |
Color range as (min, max) tuple. Default: (0, 100). |
None |
opacity |
float |
Layer opacity (0-1). |
1.0 |
selector |
Optional[Dict[str, Any]] |
Dimension selector for multi-dimensional data. Example: {"month": 4} to select 4th month. |
None |
minzoom |
int |
Minimum zoom level for rendering. |
0 |
maxzoom |
int |
Maximum zoom level for rendering. |
22 |
fill_value |
Optional[float] |
No-data value (auto-detected from metadata if not set). |
None |
spatial_dimensions |
Optional[Dict[str, str]] |
Custom spatial dimension names. Example: {"lat": "y", "lon": "x"} for non-standard names. |
None |
zarr_version |
Optional[int] |
Zarr format version (2 or 3). Auto-detected if not set. |
None |
bounds |
Optional[List[float]] |
Explicit spatial bounds [xMin, yMin, xMax, yMax]. Units depend on CRS: degrees for EPSG:4326, meters for EPSG:3857. |
None |
**kwargs |
Additional ZarrLayer props. |
{} |
Examples:
>>> from anymap_ts import Map
>>> m = Map()
>>> m.add_zarr_layer(
... "https://example.com/climate.zarr",
... variable="temperature",
... clim=(270, 310),
... colormap=['#0000ff', '#ffff00', '#ff0000'],
... selector={"month": 7}
... )
Source code in anymap_ts/maplibre.py
def add_zarr_layer(
self,
url: str,
variable: str,
name: Optional[str] = None,
colormap: Optional[List[str]] = None,
clim: Optional[Tuple[float, float]] = None,
opacity: float = 1.0,
selector: Optional[Dict[str, Any]] = None,
minzoom: int = 0,
maxzoom: int = 22,
fill_value: Optional[float] = None,
spatial_dimensions: Optional[Dict[str, str]] = None,
zarr_version: Optional[int] = None,
bounds: Optional[List[float]] = None,
**kwargs,
) -> None:
"""Add a Zarr dataset layer for visualizing multidimensional array data.
This method renders Zarr pyramid datasets directly in the browser using
GPU-accelerated WebGL rendering via @carbonplan/zarr-layer.
Args:
url: URL to the Zarr store (pyramid format recommended).
variable: Variable name in the Zarr dataset to visualize.
name: Layer ID. If None, auto-generated.
colormap: List of hex color strings for visualization.
Example: ['#0000ff', '#ffff00', '#ff0000'] (blue-yellow-red).
Default: ['#000000', '#ffffff'] (black to white).
clim: Color range as (min, max) tuple.
Default: (0, 100).
opacity: Layer opacity (0-1).
selector: Dimension selector for multi-dimensional data.
Example: {"month": 4} to select 4th month.
minzoom: Minimum zoom level for rendering.
maxzoom: Maximum zoom level for rendering.
fill_value: No-data value (auto-detected from metadata if not set).
spatial_dimensions: Custom spatial dimension names.
Example: {"lat": "y", "lon": "x"} for non-standard names.
zarr_version: Zarr format version (2 or 3). Auto-detected if not set.
bounds: Explicit spatial bounds [xMin, yMin, xMax, yMax].
Units depend on CRS: degrees for EPSG:4326, meters for EPSG:3857.
**kwargs: Additional ZarrLayer props.
Example:
>>> from anymap_ts import Map
>>> m = Map()
>>> m.add_zarr_layer(
... "https://example.com/climate.zarr",
... variable="temperature",
... clim=(270, 310),
... colormap=['#0000ff', '#ffff00', '#ff0000'],
... selector={"month": 7}
... )
"""
layer_id = name or f"zarr-{len(self._layers)}"
self.call_js_method(
"addZarrLayer",
id=layer_id,
source=url,
variable=variable,
colormap=colormap or ["#000000", "#ffffff"],
clim=list(clim) if clim else [0, 100],
opacity=opacity,
selector=selector or {},
minzoom=minzoom,
maxzoom=maxzoom,
fillValue=fill_value,
spatialDimensions=spatial_dimensions,
zarrVersion=zarr_version,
bounds=bounds,
**kwargs,
)
self._layers = {
**self._layers,
layer_id: {
"id": layer_id,
"type": "zarr",
"url": url,
"variable": variable,
},
}
clear_draw_data(self)
¶
Clear all drawn features.
Source code in anymap_ts/maplibre.py
def clear_draw_data(self) -> None:
"""Clear all drawn features."""
self._draw_data = {"type": "FeatureCollection", "features": []}
self.call_js_method("clearDrawData")
get_draw_data(self)
¶
Get the current drawn features as GeoJSON.
Returns:
| Type | Description |
|---|---|
Dict |
GeoJSON FeatureCollection of drawn features |
Source code in anymap_ts/maplibre.py
def get_draw_data(self) -> Dict:
"""Get the current drawn features as GeoJSON.
Returns:
GeoJSON FeatureCollection of drawn features
"""
self.call_js_method("getDrawData")
# Small delay to allow JS to update the trait
import time
time.sleep(0.1)
return self._draw_data or {"type": "FeatureCollection", "features": []}
load_draw_data(self, geojson)
¶
Load GeoJSON features into the drawing layer.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
geojson |
Dict |
GeoJSON FeatureCollection to load |
required |
Source code in anymap_ts/maplibre.py
def load_draw_data(self, geojson: Dict) -> None:
"""Load GeoJSON features into the drawing layer.
Args:
geojson: GeoJSON FeatureCollection to load
"""
self._draw_data = geojson
self.call_js_method("loadDrawData", geojson)
remove_arc_layer(self, layer_id)
¶
Remove an arc layer.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
layer_id |
str |
Layer identifier to remove. |
required |
Source code in anymap_ts/maplibre.py
def remove_arc_layer(self, layer_id: str) -> None:
"""Remove an arc layer.
Args:
layer_id: Layer identifier to remove.
"""
if layer_id in self._layers:
layers = dict(self._layers)
del layers[layer_id]
self._layers = layers
self.call_js_method("removeArcLayer", layer_id)
remove_cog_layer(self, layer_id)
¶
Remove a COG layer.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
layer_id |
str |
Layer identifier to remove. |
required |
Source code in anymap_ts/maplibre.py
def remove_cog_layer(self, layer_id: str) -> None:
"""Remove a COG layer.
Args:
layer_id: Layer identifier to remove.
"""
if layer_id in self._layers:
layers = dict(self._layers)
del layers[layer_id]
self._layers = layers
self.call_js_method("removeCOGLayer", layer_id)
remove_control(self, control_type)
¶
Remove a map control.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
control_type |
str |
Type of control to remove |
required |
Source code in anymap_ts/maplibre.py
def remove_control(self, control_type: str) -> None:
"""Remove a map control.
Args:
control_type: Type of control to remove
"""
self.call_js_method("removeControl", control_type)
if control_type in self._controls:
controls = dict(self._controls)
del controls[control_type]
self._controls = controls
remove_layer(self, layer_id)
¶
Remove a layer from the map.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
layer_id |
str |
Layer identifier to remove |
required |
Source code in anymap_ts/maplibre.py
def remove_layer(self, layer_id: str) -> None:
"""Remove a layer from the map.
Args:
layer_id: Layer identifier to remove
"""
if layer_id in self._layers:
layers = dict(self._layers)
del layers[layer_id]
self._layers = layers
self.call_js_method("removeLayer", layer_id)
remove_point_cloud_layer(self, layer_id)
¶
Remove a point cloud layer.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
layer_id |
str |
Layer identifier to remove. |
required |
Source code in anymap_ts/maplibre.py
def remove_point_cloud_layer(self, layer_id: str) -> None:
"""Remove a point cloud layer.
Args:
layer_id: Layer identifier to remove.
"""
if layer_id in self._layers:
layers = dict(self._layers)
del layers[layer_id]
self._layers = layers
self.call_js_method("removePointCloudLayer", layer_id)
remove_zarr_layer(self, layer_id)
¶
Remove a Zarr layer.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
layer_id |
str |
Layer identifier to remove. |
required |
Source code in anymap_ts/maplibre.py
def remove_zarr_layer(self, layer_id: str) -> None:
"""Remove a Zarr layer.
Args:
layer_id: Layer identifier to remove.
"""
if layer_id in self._layers:
layers = dict(self._layers)
del layers[layer_id]
self._layers = layers
self.call_js_method("removeZarrLayer", layer_id)
save_draw_data(self, filepath, driver=None)
¶
Save drawn features to a file.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
filepath |
Union[str, Path] |
Path to save file |
required |
driver |
Optional[str] |
Output driver (auto-detected from extension if not provided) |
None |
Exceptions:
| Type | Description |
|---|---|
ImportError |
If geopandas is not installed |
Source code in anymap_ts/maplibre.py
def save_draw_data(
self,
filepath: Union[str, Path],
driver: Optional[str] = None,
) -> None:
"""Save drawn features to a file.
Args:
filepath: Path to save file
driver: Output driver (auto-detected from extension if not provided)
Raises:
ImportError: If geopandas is not installed
"""
try:
import geopandas as gpd
except ImportError:
raise ImportError(
"geopandas is required to save draw data. "
"Install with: pip install anymap-ts[vector]"
)
data = self.get_draw_data()
if not data.get("features"):
print("No features to save")
return
gdf = gpd.GeoDataFrame.from_features(data["features"])
filepath = Path(filepath)
# Infer driver from extension
if driver is None:
ext = filepath.suffix.lower()
driver_map = {
".geojson": "GeoJSON",
".json": "GeoJSON",
".shp": "ESRI Shapefile",
".gpkg": "GPKG",
}
driver = driver_map.get(ext, "GeoJSON")
gdf.to_file(filepath, driver=driver)
set_opacity(self, layer_id, opacity)
¶
Set layer opacity.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
layer_id |
str |
Layer identifier |
required |
opacity |
float |
Opacity value between 0 and 1 |
required |
Source code in anymap_ts/maplibre.py
def set_opacity(self, layer_id: str, opacity: float) -> None:
"""Set layer opacity.
Args:
layer_id: Layer identifier
opacity: Opacity value between 0 and 1
"""
self.call_js_method("setOpacity", layer_id, opacity)
set_visibility(self, layer_id, visible)
¶
Set layer visibility.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
layer_id |
str |
Layer identifier |
required |
visible |
bool |
Whether layer should be visible |
required |
Source code in anymap_ts/maplibre.py
def set_visibility(self, layer_id: str, visible: bool) -> None:
"""Set layer visibility.
Args:
layer_id: Layer identifier
visible: Whether layer should be visible
"""
self.call_js_method("setVisibility", layer_id, visible)
update_zarr_layer(self, layer_id, selector=None, clim=None, colormap=None, opacity=None)
¶
Update a Zarr layer's properties dynamically.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
layer_id |
str |
Layer identifier. |
required |
selector |
Optional[Dict[str, Any]] |
New dimension selector. |
None |
clim |
Optional[Tuple[float, float]] |
New color range. |
None |
colormap |
Optional[List[str]] |
New colormap. |
None |
opacity |
Optional[float] |
New opacity value (0-1). |
None |
Source code in anymap_ts/maplibre.py
def update_zarr_layer(
self,
layer_id: str,
selector: Optional[Dict[str, Any]] = None,
clim: Optional[Tuple[float, float]] = None,
colormap: Optional[List[str]] = None,
opacity: Optional[float] = None,
) -> None:
"""Update a Zarr layer's properties dynamically.
Args:
layer_id: Layer identifier.
selector: New dimension selector.
clim: New color range.
colormap: New colormap.
opacity: New opacity value (0-1).
"""
update_kwargs: Dict[str, Any] = {"id": layer_id}
if selector is not None:
update_kwargs["selector"] = selector
if clim is not None:
update_kwargs["clim"] = list(clim)
if colormap is not None:
update_kwargs["colormap"] = colormap
if opacity is not None:
update_kwargs["opacity"] = opacity
self.call_js_method("updateZarrLayer", **update_kwargs)
openlayers
¶
OpenLayers map widget implementation.
OpenLayersMap (MapWidget)
¶
Interactive map widget using OpenLayers.
This class provides a Python interface to OpenLayers maps with full bidirectional communication through anywidget. OpenLayers excels at WMS/WMTS support and projection handling.
Examples:
>>> from anymap_ts import OpenLayersMap
>>> m = OpenLayersMap(center=[-122.4, 37.8], zoom=10)
>>> m.add_basemap("OpenStreetMap")
>>> m.add_wms_layer(
... url="https://example.com/wms",
... layers="layer_name",
... name="WMS Layer"
... )
>>> m
Source code in anymap_ts/openlayers.py
class OpenLayersMap(MapWidget):
"""Interactive map widget using OpenLayers.
This class provides a Python interface to OpenLayers maps with
full bidirectional communication through anywidget. OpenLayers
excels at WMS/WMTS support and projection handling.
Example:
>>> from anymap_ts import OpenLayersMap
>>> m = OpenLayersMap(center=[-122.4, 37.8], zoom=10)
>>> m.add_basemap("OpenStreetMap")
>>> m.add_wms_layer(
... url="https://example.com/wms",
... layers="layer_name",
... name="WMS Layer"
... )
>>> m
"""
# ESM module for frontend
_esm = STATIC_DIR / "openlayers.js"
# OpenLayers-specific traits
projection = traitlets.Unicode("EPSG:3857").tag(sync=True)
rotation = traitlets.Float(0.0).tag(sync=True)
# Layer tracking
_layer_dict = traitlets.Dict({}).tag(sync=True)
def __init__(
self,
center: Tuple[float, float] = (0.0, 0.0),
zoom: float = 2.0,
width: str = "100%",
height: str = "600px",
projection: str = "EPSG:3857",
rotation: float = 0.0,
controls: Optional[Dict[str, Any]] = None,
**kwargs,
):
"""Initialize an OpenLayers map.
Args:
center: Map center as (longitude, latitude).
zoom: Initial zoom level.
width: Map width as CSS string.
height: Map height as CSS string.
projection: Map projection (default EPSG:3857).
rotation: Map rotation in radians.
controls: Dict of controls to add.
**kwargs: Additional widget arguments.
"""
super().__init__(
center=list(center),
zoom=zoom,
width=width,
height=height,
projection=projection,
rotation=rotation,
**kwargs,
)
# Initialize layer dictionary
self._layer_dict = {"Background": []}
# Add default controls
if controls is None:
controls = {"zoom": True, "attribution": True}
for control_name, config in controls.items():
if config:
self.add_control(
control_name, **(config if isinstance(config, dict) else {})
)
# -------------------------------------------------------------------------
# Basemap Methods
# -------------------------------------------------------------------------
def add_basemap(
self,
basemap: str = "OpenStreetMap",
attribution: Optional[str] = None,
**kwargs,
) -> None:
"""Add a basemap layer.
Args:
basemap: Name of basemap provider (e.g., "OpenStreetMap", "CartoDB.Positron").
attribution: Custom attribution text.
**kwargs: Additional options.
"""
url, default_attribution = get_basemap_url(basemap)
self.call_js_method(
"addBasemap",
url,
attribution=attribution or default_attribution,
name=basemap,
**kwargs,
)
basemaps = self._layer_dict.get("Basemaps", [])
if basemap not in basemaps:
self._layer_dict = {
**self._layer_dict,
"Basemaps": basemaps + [basemap],
}
# -------------------------------------------------------------------------
# Tile Layer Methods
# -------------------------------------------------------------------------
def add_tile_layer(
self,
url: str,
name: Optional[str] = None,
attribution: str = "",
min_zoom: int = 0,
max_zoom: int = 22,
opacity: float = 1.0,
**kwargs,
) -> None:
"""Add an XYZ tile layer.
Args:
url: Tile URL template with {x}, {y}, {z} placeholders.
name: Layer name.
attribution: Attribution text.
min_zoom: Minimum zoom level.
max_zoom: Maximum zoom level.
opacity: Layer opacity.
**kwargs: Additional options.
"""
layer_id = name or f"tiles-{len(self._layers)}"
self.call_js_method(
"addTileLayer",
url,
name=layer_id,
attribution=attribution,
minZoom=min_zoom,
maxZoom=max_zoom,
opacity=opacity,
**kwargs,
)
self._layers = {
**self._layers,
layer_id: {"id": layer_id, "type": "tile"},
}
# -------------------------------------------------------------------------
# Vector Data Methods
# -------------------------------------------------------------------------
def add_vector(
self,
data: Any,
name: Optional[str] = None,
style: Optional[Dict] = None,
fit_bounds: bool = True,
**kwargs,
) -> None:
"""Add vector data to the map.
Args:
data: GeoJSON dict, GeoDataFrame, or path to vector file.
name: Layer name.
style: Style configuration dict.
fit_bounds: Whether to fit map to data bounds.
**kwargs: Additional layer options.
"""
geojson = to_geojson(data)
layer_id = name or f"vector-{len(self._layers)}"
if style is None:
style = self._get_default_style(geojson)
self.call_js_method(
"addGeoJSON",
data=geojson,
name=layer_id,
style=style,
fitBounds=fit_bounds,
**kwargs,
)
self._layers = {
**self._layers,
layer_id: {"id": layer_id, "type": "vector"},
}
def add_geojson(
self,
data: Union[str, Dict],
name: Optional[str] = None,
style: Optional[Dict] = None,
fit_bounds: bool = True,
**kwargs,
) -> None:
"""Add GeoJSON data to the map.
Args:
data: GeoJSON dict or URL to GeoJSON file.
name: Layer name.
style: Style configuration dict.
fit_bounds: Whether to fit map to data bounds.
**kwargs: Additional layer options.
"""
self.add_vector(
data,
name=name,
style=style,
fit_bounds=fit_bounds,
**kwargs,
)
def _get_default_style(self, geojson: Dict) -> Dict:
"""Get default style based on geometry type.
Args:
geojson: GeoJSON data.
Returns:
Style configuration dict.
"""
geom_type = self._infer_geom_type(geojson)
if geom_type in ["Point", "MultiPoint"]:
return {
"fillColor": "rgba(51, 136, 255, 0.8)",
"strokeColor": "#ffffff",
"strokeWidth": 2,
"radius": 6,
}
elif geom_type in ["LineString", "MultiLineString"]:
return {
"strokeColor": "#3388ff",
"strokeWidth": 3,
}
else: # Polygon, MultiPolygon
return {
"fillColor": "rgba(51, 136, 255, 0.5)",
"strokeColor": "#3388ff",
"strokeWidth": 2,
}
def _infer_geom_type(self, geojson: Dict) -> str:
"""Infer geometry type from GeoJSON.
Args:
geojson: GeoJSON data.
Returns:
Geometry type string.
"""
if geojson.get("type") == "FeatureCollection":
features = geojson.get("features", [])
if features:
return features[0].get("geometry", {}).get("type", "Point")
elif geojson.get("type") == "Feature":
return geojson.get("geometry", {}).get("type", "Point")
return "Point"
# -------------------------------------------------------------------------
# WMS/WMTS Methods
# -------------------------------------------------------------------------
def add_wms_layer(
self,
url: str,
layers: str,
name: Optional[str] = None,
format: str = "image/png",
transparent: bool = True,
server_type: Optional[str] = None,
attribution: str = "",
**kwargs,
) -> None:
"""Add a WMS tile layer.
Args:
url: WMS service URL.
layers: Comma-separated layer names.
name: Layer name for the map.
format: Image format (default: image/png).
transparent: Whether to request transparent images.
server_type: Server type ('mapserver', 'geoserver', 'qgis').
attribution: Attribution text.
**kwargs: Additional WMS parameters.
"""
layer_id = name or f"wms-{len(self._layers)}"
self.call_js_method(
"addWMSLayer",
url=url,
layers=layers,
name=layer_id,
format=format,
transparent=transparent,
serverType=server_type,
attribution=attribution,
**kwargs,
)
self._layers = {
**self._layers,
layer_id: {"id": layer_id, "type": "wms"},
}
def add_image_wms_layer(
self,
url: str,
layers: str,
name: Optional[str] = None,
format: str = "image/png",
transparent: bool = True,
server_type: Optional[str] = None,
attribution: str = "",
**kwargs,
) -> None:
"""Add a single-image WMS layer (not tiled).
Args:
url: WMS service URL.
layers: Comma-separated layer names.
name: Layer name for the map.
format: Image format (default: image/png).
transparent: Whether to request transparent images.
server_type: Server type ('mapserver', 'geoserver', 'qgis').
attribution: Attribution text.
**kwargs: Additional WMS parameters.
"""
layer_id = name or f"imagewms-{len(self._layers)}"
self.call_js_method(
"addImageWMSLayer",
url=url,
layers=layers,
name=layer_id,
format=format,
transparent=transparent,
serverType=server_type,
attribution=attribution,
**kwargs,
)
self._layers = {
**self._layers,
layer_id: {"id": layer_id, "type": "imagewms"},
}
# -------------------------------------------------------------------------
# Layer Management
# -------------------------------------------------------------------------
def remove_layer(self, layer_id: str) -> None:
"""Remove a layer from the map.
Args:
layer_id: Layer identifier to remove.
"""
if layer_id in self._layers:
layers = dict(self._layers)
del layers[layer_id]
self._layers = layers
self.call_js_method("removeLayer", layer_id)
def set_visibility(self, layer_id: str, visible: bool) -> None:
"""Set layer visibility.
Args:
layer_id: Layer identifier.
visible: Whether layer should be visible.
"""
self.call_js_method("setVisibility", layer_id, visible)
def set_opacity(self, layer_id: str, opacity: float) -> None:
"""Set layer opacity.
Args:
layer_id: Layer identifier.
opacity: Opacity value between 0 and 1.
"""
self.call_js_method("setOpacity", layer_id, opacity)
# -------------------------------------------------------------------------
# Controls
# -------------------------------------------------------------------------
def add_control(
self,
control_type: str,
position: str = "top-right",
**kwargs,
) -> None:
"""Add a map control.
Args:
control_type: Type of control ('zoom', 'scale', 'fullscreen', etc.).
position: Control position.
**kwargs: Control-specific options.
"""
self.call_js_method("addControl", control_type, position=position, **kwargs)
self._controls = {
**self._controls,
control_type: {"type": control_type, "position": position, **kwargs},
}
def remove_control(self, control_type: str) -> None:
"""Remove a map control.
Args:
control_type: Type of control to remove.
"""
self.call_js_method("removeControl", control_type)
if control_type in self._controls:
controls = dict(self._controls)
del controls[control_type]
self._controls = controls
# -------------------------------------------------------------------------
# Navigation
# -------------------------------------------------------------------------
def set_center(self, lng: float, lat: float) -> None:
"""Set the map center.
Args:
lng: Longitude.
lat: Latitude.
"""
self.center = [lng, lat]
self.call_js_method("setCenter", lng, lat)
def set_zoom(self, zoom: float) -> None:
"""Set the map zoom level.
Args:
zoom: Zoom level.
"""
self.zoom = zoom
self.call_js_method("setZoom", zoom)
def fly_to(
self,
lng: float,
lat: float,
zoom: Optional[float] = None,
duration: int = 2000,
) -> None:
"""Animate to a new location.
Args:
lng: Target longitude.
lat: Target latitude.
zoom: Target zoom level (optional).
duration: Animation duration in milliseconds.
"""
self.call_js_method(
"flyTo", lng, lat, zoom=zoom or self.zoom, duration=duration
)
def fit_bounds(
self,
bounds: List[float],
padding: int = 50,
duration: int = 1000,
) -> None:
"""Fit the map to bounds.
Args:
bounds: Bounds as [minLng, minLat, maxLng, maxLat].
padding: Padding in pixels.
duration: Animation duration in milliseconds.
"""
self.call_js_method("fitBounds", bounds, padding=padding, duration=duration)
def fit_extent(
self,
extent: List[float],
padding: int = 50,
duration: int = 1000,
) -> None:
"""Fit the map to an extent (in map projection).
Args:
extent: Extent as [minX, minY, maxX, maxY] in map projection.
padding: Padding in pixels.
duration: Animation duration in milliseconds.
"""
self.call_js_method("fitExtent", extent, padding=padding, duration=duration)
# -------------------------------------------------------------------------
# Markers
# -------------------------------------------------------------------------
def add_marker(
self,
lng: float,
lat: float,
popup: Optional[str] = None,
color: str = "#3388ff",
name: Optional[str] = None,
**kwargs,
) -> None:
"""Add a marker to the map.
Args:
lng: Marker longitude.
lat: Marker latitude.
popup: Popup content (HTML string).
color: Marker color.
name: Marker identifier.
**kwargs: Additional options.
"""
marker_id = name or f"marker-{len(self._layers)}"
self.call_js_method(
"addMarker",
lng,
lat,
popup=popup,
color=color,
id=marker_id,
**kwargs,
)
# -------------------------------------------------------------------------
# HTML Export
# -------------------------------------------------------------------------
def _generate_html_template(self) -> str:
"""Generate standalone HTML for the map."""
template_path = Path(__file__).parent / "templates" / "openlayers.html"
if template_path.exists():
template = template_path.read_text(encoding="utf-8")
else:
template = self._get_default_template()
state = {
"center": self.center,
"zoom": self.zoom,
"projection": self.projection,
"rotation": self.rotation,
"width": self.width,
"height": self.height,
"layers": self._layers,
"controls": self._controls,
"js_calls": self._js_calls,
}
template = template.replace("{{state}}", json.dumps(state, indent=2))
return template
def _get_default_template(self) -> str:
"""Get default HTML template."""
return """<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>OpenLayers Map</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/ol@v10.0.0/ol.css">
<script src="https://cdn.jsdelivr.net/npm/ol@v10.0.0/dist/ol.js"></script>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
html, body { height: 100%; }
#map { position: absolute; top: 0; bottom: 0; width: 100%; }
</style>
</head>
<body>
<div id="map"></div>
<script>
const state = {{state}};
const map = new ol.Map({
target: 'map',
view: new ol.View({
center: ol.proj.fromLonLat(state.center),
zoom: state.zoom
})
});
for (const call of state.js_calls || []) {
executeMethod(call.method, call.args, call.kwargs);
}
function executeMethod(method, args, kwargs) {
console.log('Executing:', method, args, kwargs);
}
</script>
</body>
</html>"""
__init__(self, center=(0.0, 0.0), zoom=2.0, width='100%', height='600px', projection='EPSG:3857', rotation=0.0, controls=None, **kwargs)
special
¶
Initialize an OpenLayers map.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
center |
Tuple[float, float] |
Map center as (longitude, latitude). |
(0.0, 0.0) |
zoom |
float |
Initial zoom level. |
2.0 |
width |
str |
Map width as CSS string. |
'100%' |
height |
str |
Map height as CSS string. |
'600px' |
projection |
str |
Map projection (default EPSG:3857). |
'EPSG:3857' |
rotation |
float |
Map rotation in radians. |
0.0 |
controls |
Optional[Dict[str, Any]] |
Dict of controls to add. |
None |
**kwargs |
Additional widget arguments. |
{} |
Source code in anymap_ts/openlayers.py
def __init__(
self,
center: Tuple[float, float] = (0.0, 0.0),
zoom: float = 2.0,
width: str = "100%",
height: str = "600px",
projection: str = "EPSG:3857",
rotation: float = 0.0,
controls: Optional[Dict[str, Any]] = None,
**kwargs,
):
"""Initialize an OpenLayers map.
Args:
center: Map center as (longitude, latitude).
zoom: Initial zoom level.
width: Map width as CSS string.
height: Map height as CSS string.
projection: Map projection (default EPSG:3857).
rotation: Map rotation in radians.
controls: Dict of controls to add.
**kwargs: Additional widget arguments.
"""
super().__init__(
center=list(center),
zoom=zoom,
width=width,
height=height,
projection=projection,
rotation=rotation,
**kwargs,
)
# Initialize layer dictionary
self._layer_dict = {"Background": []}
# Add default controls
if controls is None:
controls = {"zoom": True, "attribution": True}
for control_name, config in controls.items():
if config:
self.add_control(
control_name, **(config if isinstance(config, dict) else {})
)
add_basemap(self, basemap='OpenStreetMap', attribution=None, **kwargs)
¶
Add a basemap layer.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
basemap |
str |
Name of basemap provider (e.g., "OpenStreetMap", "CartoDB.Positron"). |
'OpenStreetMap' |
attribution |
Optional[str] |
Custom attribution text. |
None |
**kwargs |
Additional options. |
{} |
Source code in anymap_ts/openlayers.py
def add_basemap(
self,
basemap: str = "OpenStreetMap",
attribution: Optional[str] = None,
**kwargs,
) -> None:
"""Add a basemap layer.
Args:
basemap: Name of basemap provider (e.g., "OpenStreetMap", "CartoDB.Positron").
attribution: Custom attribution text.
**kwargs: Additional options.
"""
url, default_attribution = get_basemap_url(basemap)
self.call_js_method(
"addBasemap",
url,
attribution=attribution or default_attribution,
name=basemap,
**kwargs,
)
basemaps = self._layer_dict.get("Basemaps", [])
if basemap not in basemaps:
self._layer_dict = {
**self._layer_dict,
"Basemaps": basemaps + [basemap],
}
add_control(self, control_type, position='top-right', **kwargs)
¶
Add a map control.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
control_type |
str |
Type of control ('zoom', 'scale', 'fullscreen', etc.). |
required |
position |
str |
Control position. |
'top-right' |
**kwargs |
Control-specific options. |
{} |
Source code in anymap_ts/openlayers.py
def add_control(
self,
control_type: str,
position: str = "top-right",
**kwargs,
) -> None:
"""Add a map control.
Args:
control_type: Type of control ('zoom', 'scale', 'fullscreen', etc.).
position: Control position.
**kwargs: Control-specific options.
"""
self.call_js_method("addControl", control_type, position=position, **kwargs)
self._controls = {
**self._controls,
control_type: {"type": control_type, "position": position, **kwargs},
}
add_geojson(self, data, name=None, style=None, fit_bounds=True, **kwargs)
¶
Add GeoJSON data to the map.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data |
Union[str, Dict] |
GeoJSON dict or URL to GeoJSON file. |
required |
name |
Optional[str] |
Layer name. |
None |
style |
Optional[Dict] |
Style configuration dict. |
None |
fit_bounds |
bool |
Whether to fit map to data bounds. |
True |
**kwargs |
Additional layer options. |
{} |
Source code in anymap_ts/openlayers.py
def add_geojson(
self,
data: Union[str, Dict],
name: Optional[str] = None,
style: Optional[Dict] = None,
fit_bounds: bool = True,
**kwargs,
) -> None:
"""Add GeoJSON data to the map.
Args:
data: GeoJSON dict or URL to GeoJSON file.
name: Layer name.
style: Style configuration dict.
fit_bounds: Whether to fit map to data bounds.
**kwargs: Additional layer options.
"""
self.add_vector(
data,
name=name,
style=style,
fit_bounds=fit_bounds,
**kwargs,
)
add_image_wms_layer(self, url, layers, name=None, format='image/png', transparent=True, server_type=None, attribution='', **kwargs)
¶
Add a single-image WMS layer (not tiled).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
url |
str |
WMS service URL. |
required |
layers |
str |
Comma-separated layer names. |
required |
name |
Optional[str] |
Layer name for the map. |
None |
format |
str |
Image format (default: image/png). |
'image/png' |
transparent |
bool |
Whether to request transparent images. |
True |
server_type |
Optional[str] |
Server type ('mapserver', 'geoserver', 'qgis'). |
None |
attribution |
str |
Attribution text. |
'' |
**kwargs |
Additional WMS parameters. |
{} |
Source code in anymap_ts/openlayers.py
def add_image_wms_layer(
self,
url: str,
layers: str,
name: Optional[str] = None,
format: str = "image/png",
transparent: bool = True,
server_type: Optional[str] = None,
attribution: str = "",
**kwargs,
) -> None:
"""Add a single-image WMS layer (not tiled).
Args:
url: WMS service URL.
layers: Comma-separated layer names.
name: Layer name for the map.
format: Image format (default: image/png).
transparent: Whether to request transparent images.
server_type: Server type ('mapserver', 'geoserver', 'qgis').
attribution: Attribution text.
**kwargs: Additional WMS parameters.
"""
layer_id = name or f"imagewms-{len(self._layers)}"
self.call_js_method(
"addImageWMSLayer",
url=url,
layers=layers,
name=layer_id,
format=format,
transparent=transparent,
serverType=server_type,
attribution=attribution,
**kwargs,
)
self._layers = {
**self._layers,
layer_id: {"id": layer_id, "type": "imagewms"},
}
add_marker(self, lng, lat, popup=None, color='#3388ff', name=None, **kwargs)
¶
Add a marker to the map.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
lng |
float |
Marker longitude. |
required |
lat |
float |
Marker latitude. |
required |
popup |
Optional[str] |
Popup content (HTML string). |
None |
color |
str |
Marker color. |
'#3388ff' |
name |
Optional[str] |
Marker identifier. |
None |
**kwargs |
Additional options. |
{} |
Source code in anymap_ts/openlayers.py
def add_marker(
self,
lng: float,
lat: float,
popup: Optional[str] = None,
color: str = "#3388ff",
name: Optional[str] = None,
**kwargs,
) -> None:
"""Add a marker to the map.
Args:
lng: Marker longitude.
lat: Marker latitude.
popup: Popup content (HTML string).
color: Marker color.
name: Marker identifier.
**kwargs: Additional options.
"""
marker_id = name or f"marker-{len(self._layers)}"
self.call_js_method(
"addMarker",
lng,
lat,
popup=popup,
color=color,
id=marker_id,
**kwargs,
)
add_tile_layer(self, url, name=None, attribution='', min_zoom=0, max_zoom=22, opacity=1.0, **kwargs)
¶
Add an XYZ tile layer.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
url |
str |
Tile URL template with {x}, {y}, {z} placeholders. |
required |
name |
Optional[str] |
Layer name. |
None |
attribution |
str |
Attribution text. |
'' |
min_zoom |
int |
Minimum zoom level. |
0 |
max_zoom |
int |
Maximum zoom level. |
22 |
opacity |
float |
Layer opacity. |
1.0 |
**kwargs |
Additional options. |
{} |
Source code in anymap_ts/openlayers.py
def add_tile_layer(
self,
url: str,
name: Optional[str] = None,
attribution: str = "",
min_zoom: int = 0,
max_zoom: int = 22,
opacity: float = 1.0,
**kwargs,
) -> None:
"""Add an XYZ tile layer.
Args:
url: Tile URL template with {x}, {y}, {z} placeholders.
name: Layer name.
attribution: Attribution text.
min_zoom: Minimum zoom level.
max_zoom: Maximum zoom level.
opacity: Layer opacity.
**kwargs: Additional options.
"""
layer_id = name or f"tiles-{len(self._layers)}"
self.call_js_method(
"addTileLayer",
url,
name=layer_id,
attribution=attribution,
minZoom=min_zoom,
maxZoom=max_zoom,
opacity=opacity,
**kwargs,
)
self._layers = {
**self._layers,
layer_id: {"id": layer_id, "type": "tile"},
}
add_vector(self, data, name=None, style=None, fit_bounds=True, **kwargs)
¶
Add vector data to the map.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data |
Any |
GeoJSON dict, GeoDataFrame, or path to vector file. |
required |
name |
Optional[str] |
Layer name. |
None |
style |
Optional[Dict] |
Style configuration dict. |
None |
fit_bounds |
bool |
Whether to fit map to data bounds. |
True |
**kwargs |
Additional layer options. |
{} |
Source code in anymap_ts/openlayers.py
def add_vector(
self,
data: Any,
name: Optional[str] = None,
style: Optional[Dict] = None,
fit_bounds: bool = True,
**kwargs,
) -> None:
"""Add vector data to the map.
Args:
data: GeoJSON dict, GeoDataFrame, or path to vector file.
name: Layer name.
style: Style configuration dict.
fit_bounds: Whether to fit map to data bounds.
**kwargs: Additional layer options.
"""
geojson = to_geojson(data)
layer_id = name or f"vector-{len(self._layers)}"
if style is None:
style = self._get_default_style(geojson)
self.call_js_method(
"addGeoJSON",
data=geojson,
name=layer_id,
style=style,
fitBounds=fit_bounds,
**kwargs,
)
self._layers = {
**self._layers,
layer_id: {"id": layer_id, "type": "vector"},
}
add_wms_layer(self, url, layers, name=None, format='image/png', transparent=True, server_type=None, attribution='', **kwargs)
¶
Add a WMS tile layer.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
url |
str |
WMS service URL. |
required |
layers |
str |
Comma-separated layer names. |
required |
name |
Optional[str] |
Layer name for the map. |
None |
format |
str |
Image format (default: image/png). |
'image/png' |
transparent |
bool |
Whether to request transparent images. |
True |
server_type |
Optional[str] |
Server type ('mapserver', 'geoserver', 'qgis'). |
None |
attribution |
str |
Attribution text. |
'' |
**kwargs |
Additional WMS parameters. |
{} |
Source code in anymap_ts/openlayers.py
def add_wms_layer(
self,
url: str,
layers: str,
name: Optional[str] = None,
format: str = "image/png",
transparent: bool = True,
server_type: Optional[str] = None,
attribution: str = "",
**kwargs,
) -> None:
"""Add a WMS tile layer.
Args:
url: WMS service URL.
layers: Comma-separated layer names.
name: Layer name for the map.
format: Image format (default: image/png).
transparent: Whether to request transparent images.
server_type: Server type ('mapserver', 'geoserver', 'qgis').
attribution: Attribution text.
**kwargs: Additional WMS parameters.
"""
layer_id = name or f"wms-{len(self._layers)}"
self.call_js_method(
"addWMSLayer",
url=url,
layers=layers,
name=layer_id,
format=format,
transparent=transparent,
serverType=server_type,
attribution=attribution,
**kwargs,
)
self._layers = {
**self._layers,
layer_id: {"id": layer_id, "type": "wms"},
}
fit_bounds(self, bounds, padding=50, duration=1000)
¶
Fit the map to bounds.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
bounds |
List[float] |
Bounds as [minLng, minLat, maxLng, maxLat]. |
required |
padding |
int |
Padding in pixels. |
50 |
duration |
int |
Animation duration in milliseconds. |
1000 |
Source code in anymap_ts/openlayers.py
def fit_bounds(
self,
bounds: List[float],
padding: int = 50,
duration: int = 1000,
) -> None:
"""Fit the map to bounds.
Args:
bounds: Bounds as [minLng, minLat, maxLng, maxLat].
padding: Padding in pixels.
duration: Animation duration in milliseconds.
"""
self.call_js_method("fitBounds", bounds, padding=padding, duration=duration)
fit_extent(self, extent, padding=50, duration=1000)
¶
Fit the map to an extent (in map projection).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
extent |
List[float] |
Extent as [minX, minY, maxX, maxY] in map projection. |
required |
padding |
int |
Padding in pixels. |
50 |
duration |
int |
Animation duration in milliseconds. |
1000 |
Source code in anymap_ts/openlayers.py
def fit_extent(
self,
extent: List[float],
padding: int = 50,
duration: int = 1000,
) -> None:
"""Fit the map to an extent (in map projection).
Args:
extent: Extent as [minX, minY, maxX, maxY] in map projection.
padding: Padding in pixels.
duration: Animation duration in milliseconds.
"""
self.call_js_method("fitExtent", extent, padding=padding, duration=duration)
fly_to(self, lng, lat, zoom=None, duration=2000)
¶
Animate to a new location.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
lng |
float |
Target longitude. |
required |
lat |
float |
Target latitude. |
required |
zoom |
Optional[float] |
Target zoom level (optional). |
None |
duration |
int |
Animation duration in milliseconds. |
2000 |
Source code in anymap_ts/openlayers.py
def fly_to(
self,
lng: float,
lat: float,
zoom: Optional[float] = None,
duration: int = 2000,
) -> None:
"""Animate to a new location.
Args:
lng: Target longitude.
lat: Target latitude.
zoom: Target zoom level (optional).
duration: Animation duration in milliseconds.
"""
self.call_js_method(
"flyTo", lng, lat, zoom=zoom or self.zoom, duration=duration
)
remove_control(self, control_type)
¶
Remove a map control.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
control_type |
str |
Type of control to remove. |
required |
Source code in anymap_ts/openlayers.py
def remove_control(self, control_type: str) -> None:
"""Remove a map control.
Args:
control_type: Type of control to remove.
"""
self.call_js_method("removeControl", control_type)
if control_type in self._controls:
controls = dict(self._controls)
del controls[control_type]
self._controls = controls
remove_layer(self, layer_id)
¶
Remove a layer from the map.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
layer_id |
str |
Layer identifier to remove. |
required |
Source code in anymap_ts/openlayers.py
def remove_layer(self, layer_id: str) -> None:
"""Remove a layer from the map.
Args:
layer_id: Layer identifier to remove.
"""
if layer_id in self._layers:
layers = dict(self._layers)
del layers[layer_id]
self._layers = layers
self.call_js_method("removeLayer", layer_id)
set_center(self, lng, lat)
¶
Set the map center.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
lng |
float |
Longitude. |
required |
lat |
float |
Latitude. |
required |
Source code in anymap_ts/openlayers.py
def set_center(self, lng: float, lat: float) -> None:
"""Set the map center.
Args:
lng: Longitude.
lat: Latitude.
"""
self.center = [lng, lat]
self.call_js_method("setCenter", lng, lat)
set_opacity(self, layer_id, opacity)
¶
Set layer opacity.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
layer_id |
str |
Layer identifier. |
required |
opacity |
float |
Opacity value between 0 and 1. |
required |
Source code in anymap_ts/openlayers.py
def set_opacity(self, layer_id: str, opacity: float) -> None:
"""Set layer opacity.
Args:
layer_id: Layer identifier.
opacity: Opacity value between 0 and 1.
"""
self.call_js_method("setOpacity", layer_id, opacity)
set_visibility(self, layer_id, visible)
¶
Set layer visibility.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
layer_id |
str |
Layer identifier. |
required |
visible |
bool |
Whether layer should be visible. |
required |
Source code in anymap_ts/openlayers.py
def set_visibility(self, layer_id: str, visible: bool) -> None:
"""Set layer visibility.
Args:
layer_id: Layer identifier.
visible: Whether layer should be visible.
"""
self.call_js_method("setVisibility", layer_id, visible)
set_zoom(self, zoom)
¶
Set the map zoom level.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
zoom |
float |
Zoom level. |
required |
Source code in anymap_ts/openlayers.py
def set_zoom(self, zoom: float) -> None:
"""Set the map zoom level.
Args:
zoom: Zoom level.
"""
self.zoom = zoom
self.call_js_method("setZoom", zoom)
potree
¶
Potree point cloud viewer widget implementation.
Potree is loaded via CDN since it's a complex Three.js-based viewer. This implementation provides a Python wrapper for point cloud visualization.
PotreeViewer (MapWidget)
¶
Interactive point cloud viewer using Potree.
Potree is a WebGL-based point cloud renderer for large-scale LiDAR datasets. This class provides a Python interface for loading and visualizing point clouds.
Note: Potree is loaded from CDN due to its complex Three.js dependencies.
Examples:
>>> from anymap_ts import PotreeViewer
>>> viewer = PotreeViewer()
>>> viewer.load_point_cloud("path/to/pointcloud/cloud.js")
>>> viewer
Source code in anymap_ts/potree.py
class PotreeViewer(MapWidget):
"""Interactive point cloud viewer using Potree.
Potree is a WebGL-based point cloud renderer for large-scale LiDAR
datasets. This class provides a Python interface for loading and
visualizing point clouds.
Note: Potree is loaded from CDN due to its complex Three.js dependencies.
Example:
>>> from anymap_ts import PotreeViewer
>>> viewer = PotreeViewer()
>>> viewer.load_point_cloud("path/to/pointcloud/cloud.js")
>>> viewer
"""
# ESM module for frontend
_esm = STATIC_DIR / "potree.js"
# Potree-specific traits
point_budget = traitlets.Int(1000000).tag(sync=True)
point_size = traitlets.Float(1.0).tag(sync=True)
fov = traitlets.Float(60.0).tag(sync=True)
background = traitlets.Unicode("#000000").tag(sync=True)
# EDL (Eye Dome Lighting) settings
edl_enabled = traitlets.Bool(True).tag(sync=True)
edl_radius = traitlets.Float(1.4).tag(sync=True)
edl_strength = traitlets.Float(0.4).tag(sync=True)
# Point clouds
point_clouds = traitlets.Dict({}).tag(sync=True)
# Camera
camera_position = traitlets.List([0, 0, 100]).tag(sync=True)
camera_target = traitlets.List([0, 0, 0]).tag(sync=True)
def __init__(
self,
width: str = "100%",
height: str = "600px",
point_budget: int = 1000000,
point_size: float = 1.0,
fov: float = 60.0,
background: str = "#000000",
edl_enabled: bool = True,
**kwargs,
):
"""Initialize a Potree viewer.
Args:
width: Widget width as CSS string.
height: Widget height as CSS string.
point_budget: Maximum number of points to render.
point_size: Default point size.
fov: Field of view in degrees.
background: Background color (hex string).
edl_enabled: Enable Eye Dome Lighting.
**kwargs: Additional widget arguments.
"""
# Potree doesn't use center/zoom like maps
super().__init__(
center=[0, 0],
zoom=1,
width=width,
height=height,
point_budget=point_budget,
point_size=point_size,
fov=fov,
background=background,
edl_enabled=edl_enabled,
**kwargs,
)
self.point_clouds = {}
# -------------------------------------------------------------------------
# Point Cloud Methods
# -------------------------------------------------------------------------
def load_point_cloud(
self,
url: str,
name: Optional[str] = None,
visible: bool = True,
point_size: Optional[float] = None,
point_size_type: str = "adaptive",
shape: str = "circle",
color: Optional[str] = None,
**kwargs,
) -> None:
"""Load a point cloud.
Args:
url: URL to point cloud (Potree format or LAZ/LAS via Entwine).
name: Point cloud name.
visible: Whether point cloud is visible.
point_size: Point size (overrides default).
point_size_type: 'fixed', 'attenuated', or 'adaptive'.
shape: Point shape ('square', 'circle', 'paraboloid').
color: Point color (hex string or None for native colors).
**kwargs: Additional material options.
"""
cloud_id = name or f"pointcloud_{len(self.point_clouds)}"
self.point_clouds = {
**self.point_clouds,
cloud_id: {
"url": url,
"name": cloud_id,
"visible": visible,
"material": {
"size": point_size or self.point_size,
"pointSizeType": point_size_type,
"shape": shape,
"color": color,
**kwargs,
},
},
}
self.call_js_method(
"loadPointCloud",
url=url,
name=cloud_id,
visible=visible,
material={
"size": point_size or self.point_size,
"pointSizeType": point_size_type,
"shape": shape,
"color": color,
**kwargs,
},
)
def remove_point_cloud(self, name: str) -> None:
"""Remove a point cloud.
Args:
name: Point cloud name to remove.
"""
if name in self.point_clouds:
clouds = dict(self.point_clouds)
del clouds[name]
self.point_clouds = clouds
self.call_js_method("removePointCloud", name=name)
def set_point_cloud_visibility(self, name: str, visible: bool) -> None:
"""Set point cloud visibility.
Args:
name: Point cloud name.
visible: Whether to show the point cloud.
"""
self.call_js_method("setPointCloudVisibility", name=name, visible=visible)
# -------------------------------------------------------------------------
# Camera Methods
# -------------------------------------------------------------------------
def set_camera_position(
self,
x: float,
y: float,
z: float,
) -> None:
"""Set camera position.
Args:
x: X coordinate.
y: Y coordinate.
z: Z coordinate.
"""
self.camera_position = [x, y, z]
self.call_js_method("setCameraPosition", x=x, y=y, z=z)
def set_camera_target(
self,
x: float,
y: float,
z: float,
) -> None:
"""Set camera target (look-at point).
Args:
x: X coordinate.
y: Y coordinate.
z: Z coordinate.
"""
self.camera_target = [x, y, z]
self.call_js_method("setCameraTarget", x=x, y=y, z=z)
def fly_to_point_cloud(self, name: Optional[str] = None) -> None:
"""Fly to a point cloud or all point clouds.
Args:
name: Point cloud name (None for all).
"""
self.call_js_method("flyToPointCloud", name=name)
def reset_camera(self) -> None:
"""Reset camera to default view."""
self.call_js_method("resetCamera")
# -------------------------------------------------------------------------
# Visualization Settings
# -------------------------------------------------------------------------
def set_point_budget(self, budget: int) -> None:
"""Set the point budget (max points to render).
Args:
budget: Maximum number of points.
"""
self.point_budget = budget
self.call_js_method("setPointBudget", budget=budget)
def set_point_size(self, size: float) -> None:
"""Set default point size.
Args:
size: Point size.
"""
self.point_size = size
self.call_js_method("setPointSize", size=size)
def set_fov(self, fov: float) -> None:
"""Set field of view.
Args:
fov: Field of view in degrees.
"""
self.fov = fov
self.call_js_method("setFOV", fov=fov)
def set_background(self, color: str) -> None:
"""Set background color.
Args:
color: Background color (hex string).
"""
self.background = color
self.call_js_method("setBackground", color=color)
def set_edl(
self,
enabled: bool = True,
radius: float = 1.4,
strength: float = 0.4,
) -> None:
"""Configure Eye Dome Lighting.
Args:
enabled: Whether to enable EDL.
radius: EDL radius.
strength: EDL strength.
"""
self.edl_enabled = enabled
self.edl_radius = radius
self.edl_strength = strength
self.call_js_method(
"setEDL",
enabled=enabled,
radius=radius,
strength=strength,
)
# -------------------------------------------------------------------------
# Measurement Tools
# -------------------------------------------------------------------------
def add_measurement_tool(self, tool_type: str = "distance") -> None:
"""Add a measurement tool.
Args:
tool_type: Type of measurement ('point', 'distance', 'area', 'angle', 'height', 'profile').
"""
self.call_js_method("addMeasurementTool", type=tool_type)
def clear_measurements(self) -> None:
"""Clear all measurements."""
self.call_js_method("clearMeasurements")
# -------------------------------------------------------------------------
# Clipping
# -------------------------------------------------------------------------
def add_clipping_volume(
self,
volume_type: str = "box",
position: Optional[Tuple[float, float, float]] = None,
scale: Optional[Tuple[float, float, float]] = None,
) -> None:
"""Add a clipping volume.
Args:
volume_type: Type of volume ('box', 'polygon', 'plane').
position: Volume position (x, y, z).
scale: Volume scale (x, y, z).
"""
self.call_js_method(
"addClippingVolume",
type=volume_type,
position=list(position) if position else None,
scale=list(scale) if scale else None,
)
def clear_clipping_volumes(self) -> None:
"""Clear all clipping volumes."""
self.call_js_method("clearClippingVolumes")
# -------------------------------------------------------------------------
# Annotations
# -------------------------------------------------------------------------
def add_annotation(
self,
position: Tuple[float, float, float],
title: str,
description: str = "",
camera_position: Optional[Tuple[float, float, float]] = None,
camera_target: Optional[Tuple[float, float, float]] = None,
) -> None:
"""Add an annotation.
Args:
position: Annotation position (x, y, z).
title: Annotation title.
description: Annotation description.
camera_position: Camera position when focused.
camera_target: Camera target when focused.
"""
self.call_js_method(
"addAnnotation",
position=list(position),
title=title,
description=description,
cameraPosition=list(camera_position) if camera_position else None,
cameraTarget=list(camera_target) if camera_target else None,
)
def clear_annotations(self) -> None:
"""Clear all annotations."""
self.call_js_method("clearAnnotations")
# -------------------------------------------------------------------------
# HTML Export
# -------------------------------------------------------------------------
def _generate_html_template(self) -> str:
"""Generate standalone HTML for Potree viewer."""
template_path = Path(__file__).parent / "templates" / "potree.html"
if template_path.exists():
template = template_path.read_text(encoding="utf-8")
else:
template = self._get_default_template()
state = {
"point_budget": self.point_budget,
"point_size": self.point_size,
"fov": self.fov,
"background": self.background,
"edl_enabled": self.edl_enabled,
"edl_radius": self.edl_radius,
"edl_strength": self.edl_strength,
"point_clouds": self.point_clouds,
"camera_position": self.camera_position,
"camera_target": self.camera_target,
"width": self.width,
"height": self.height,
"js_calls": self._js_calls,
}
template = template.replace("{{state}}", json.dumps(state, indent=2))
return template
def _get_default_template(self) -> str:
"""Get default HTML template."""
return """<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Potree Viewer</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
html, body { height: 100%; overflow: hidden; }
#potree_render_area { position: absolute; top: 0; bottom: 0; width: 100%; }
</style>
</head>
<body>
<div id="potree_render_area"></div>
<script>
const state = {{state}};
document.getElementById('potree_render_area').innerHTML = '<p style="color: white; padding: 20px;">Potree viewer requires Potree library. Point clouds: ' + Object.keys(state.point_clouds || {}).length + '</p>';
</script>
</body>
</html>"""
__init__(self, width='100%', height='600px', point_budget=1000000, point_size=1.0, fov=60.0, background='#000000', edl_enabled=True, **kwargs)
special
¶
Initialize a Potree viewer.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
width |
str |
Widget width as CSS string. |
'100%' |
height |
str |
Widget height as CSS string. |
'600px' |
point_budget |
int |
Maximum number of points to render. |
1000000 |
point_size |
float |
Default point size. |
1.0 |
fov |
float |
Field of view in degrees. |
60.0 |
background |
str |
Background color (hex string). |
'#000000' |
edl_enabled |
bool |
Enable Eye Dome Lighting. |
True |
**kwargs |
Additional widget arguments. |
{} |
Source code in anymap_ts/potree.py
def __init__(
self,
width: str = "100%",
height: str = "600px",
point_budget: int = 1000000,
point_size: float = 1.0,
fov: float = 60.0,
background: str = "#000000",
edl_enabled: bool = True,
**kwargs,
):
"""Initialize a Potree viewer.
Args:
width: Widget width as CSS string.
height: Widget height as CSS string.
point_budget: Maximum number of points to render.
point_size: Default point size.
fov: Field of view in degrees.
background: Background color (hex string).
edl_enabled: Enable Eye Dome Lighting.
**kwargs: Additional widget arguments.
"""
# Potree doesn't use center/zoom like maps
super().__init__(
center=[0, 0],
zoom=1,
width=width,
height=height,
point_budget=point_budget,
point_size=point_size,
fov=fov,
background=background,
edl_enabled=edl_enabled,
**kwargs,
)
self.point_clouds = {}
add_annotation(self, position, title, description='', camera_position=None, camera_target=None)
¶
Add an annotation.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
position |
Tuple[float, float, float] |
Annotation position (x, y, z). |
required |
title |
str |
Annotation title. |
required |
description |
str |
Annotation description. |
'' |
camera_position |
Optional[Tuple[float, float, float]] |
Camera position when focused. |
None |
camera_target |
Optional[Tuple[float, float, float]] |
Camera target when focused. |
None |
Source code in anymap_ts/potree.py
def add_annotation(
self,
position: Tuple[float, float, float],
title: str,
description: str = "",
camera_position: Optional[Tuple[float, float, float]] = None,
camera_target: Optional[Tuple[float, float, float]] = None,
) -> None:
"""Add an annotation.
Args:
position: Annotation position (x, y, z).
title: Annotation title.
description: Annotation description.
camera_position: Camera position when focused.
camera_target: Camera target when focused.
"""
self.call_js_method(
"addAnnotation",
position=list(position),
title=title,
description=description,
cameraPosition=list(camera_position) if camera_position else None,
cameraTarget=list(camera_target) if camera_target else None,
)
add_clipping_volume(self, volume_type='box', position=None, scale=None)
¶
Add a clipping volume.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
volume_type |
str |
Type of volume ('box', 'polygon', 'plane'). |
'box' |
position |
Optional[Tuple[float, float, float]] |
Volume position (x, y, z). |
None |
scale |
Optional[Tuple[float, float, float]] |
Volume scale (x, y, z). |
None |
Source code in anymap_ts/potree.py
def add_clipping_volume(
self,
volume_type: str = "box",
position: Optional[Tuple[float, float, float]] = None,
scale: Optional[Tuple[float, float, float]] = None,
) -> None:
"""Add a clipping volume.
Args:
volume_type: Type of volume ('box', 'polygon', 'plane').
position: Volume position (x, y, z).
scale: Volume scale (x, y, z).
"""
self.call_js_method(
"addClippingVolume",
type=volume_type,
position=list(position) if position else None,
scale=list(scale) if scale else None,
)
add_measurement_tool(self, tool_type='distance')
¶
Add a measurement tool.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
tool_type |
str |
Type of measurement ('point', 'distance', 'area', 'angle', 'height', 'profile'). |
'distance' |
Source code in anymap_ts/potree.py
def add_measurement_tool(self, tool_type: str = "distance") -> None:
"""Add a measurement tool.
Args:
tool_type: Type of measurement ('point', 'distance', 'area', 'angle', 'height', 'profile').
"""
self.call_js_method("addMeasurementTool", type=tool_type)
clear_annotations(self)
¶
Clear all annotations.
Source code in anymap_ts/potree.py
def clear_annotations(self) -> None:
"""Clear all annotations."""
self.call_js_method("clearAnnotations")
clear_clipping_volumes(self)
¶
Clear all clipping volumes.
Source code in anymap_ts/potree.py
def clear_clipping_volumes(self) -> None:
"""Clear all clipping volumes."""
self.call_js_method("clearClippingVolumes")
clear_measurements(self)
¶
Clear all measurements.
Source code in anymap_ts/potree.py
def clear_measurements(self) -> None:
"""Clear all measurements."""
self.call_js_method("clearMeasurements")
fly_to_point_cloud(self, name=None)
¶
Fly to a point cloud or all point clouds.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
name |
Optional[str] |
Point cloud name (None for all). |
None |
Source code in anymap_ts/potree.py
def fly_to_point_cloud(self, name: Optional[str] = None) -> None:
"""Fly to a point cloud or all point clouds.
Args:
name: Point cloud name (None for all).
"""
self.call_js_method("flyToPointCloud", name=name)
load_point_cloud(self, url, name=None, visible=True, point_size=None, point_size_type='adaptive', shape='circle', color=None, **kwargs)
¶
Load a point cloud.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
url |
str |
URL to point cloud (Potree format or LAZ/LAS via Entwine). |
required |
name |
Optional[str] |
Point cloud name. |
None |
visible |
bool |
Whether point cloud is visible. |
True |
point_size |
Optional[float] |
Point size (overrides default). |
None |
point_size_type |
str |
'fixed', 'attenuated', or 'adaptive'. |
'adaptive' |
shape |
str |
Point shape ('square', 'circle', 'paraboloid'). |
'circle' |
color |
Optional[str] |
Point color (hex string or None for native colors). |
None |
**kwargs |
Additional material options. |
{} |
Source code in anymap_ts/potree.py
def load_point_cloud(
self,
url: str,
name: Optional[str] = None,
visible: bool = True,
point_size: Optional[float] = None,
point_size_type: str = "adaptive",
shape: str = "circle",
color: Optional[str] = None,
**kwargs,
) -> None:
"""Load a point cloud.
Args:
url: URL to point cloud (Potree format or LAZ/LAS via Entwine).
name: Point cloud name.
visible: Whether point cloud is visible.
point_size: Point size (overrides default).
point_size_type: 'fixed', 'attenuated', or 'adaptive'.
shape: Point shape ('square', 'circle', 'paraboloid').
color: Point color (hex string or None for native colors).
**kwargs: Additional material options.
"""
cloud_id = name or f"pointcloud_{len(self.point_clouds)}"
self.point_clouds = {
**self.point_clouds,
cloud_id: {
"url": url,
"name": cloud_id,
"visible": visible,
"material": {
"size": point_size or self.point_size,
"pointSizeType": point_size_type,
"shape": shape,
"color": color,
**kwargs,
},
},
}
self.call_js_method(
"loadPointCloud",
url=url,
name=cloud_id,
visible=visible,
material={
"size": point_size or self.point_size,
"pointSizeType": point_size_type,
"shape": shape,
"color": color,
**kwargs,
},
)
remove_point_cloud(self, name)
¶
Remove a point cloud.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
name |
str |
Point cloud name to remove. |
required |
Source code in anymap_ts/potree.py
def remove_point_cloud(self, name: str) -> None:
"""Remove a point cloud.
Args:
name: Point cloud name to remove.
"""
if name in self.point_clouds:
clouds = dict(self.point_clouds)
del clouds[name]
self.point_clouds = clouds
self.call_js_method("removePointCloud", name=name)
reset_camera(self)
¶
Reset camera to default view.
Source code in anymap_ts/potree.py
def reset_camera(self) -> None:
"""Reset camera to default view."""
self.call_js_method("resetCamera")
set_background(self, color)
¶
Set background color.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
color |
str |
Background color (hex string). |
required |
Source code in anymap_ts/potree.py
def set_background(self, color: str) -> None:
"""Set background color.
Args:
color: Background color (hex string).
"""
self.background = color
self.call_js_method("setBackground", color=color)
set_camera_position(self, x, y, z)
¶
Set camera position.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x |
float |
X coordinate. |
required |
y |
float |
Y coordinate. |
required |
z |
float |
Z coordinate. |
required |
Source code in anymap_ts/potree.py
def set_camera_position(
self,
x: float,
y: float,
z: float,
) -> None:
"""Set camera position.
Args:
x: X coordinate.
y: Y coordinate.
z: Z coordinate.
"""
self.camera_position = [x, y, z]
self.call_js_method("setCameraPosition", x=x, y=y, z=z)
set_camera_target(self, x, y, z)
¶
Set camera target (look-at point).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x |
float |
X coordinate. |
required |
y |
float |
Y coordinate. |
required |
z |
float |
Z coordinate. |
required |
Source code in anymap_ts/potree.py
def set_camera_target(
self,
x: float,
y: float,
z: float,
) -> None:
"""Set camera target (look-at point).
Args:
x: X coordinate.
y: Y coordinate.
z: Z coordinate.
"""
self.camera_target = [x, y, z]
self.call_js_method("setCameraTarget", x=x, y=y, z=z)
set_edl(self, enabled=True, radius=1.4, strength=0.4)
¶
Configure Eye Dome Lighting.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
enabled |
bool |
Whether to enable EDL. |
True |
radius |
float |
EDL radius. |
1.4 |
strength |
float |
EDL strength. |
0.4 |
Source code in anymap_ts/potree.py
def set_edl(
self,
enabled: bool = True,
radius: float = 1.4,
strength: float = 0.4,
) -> None:
"""Configure Eye Dome Lighting.
Args:
enabled: Whether to enable EDL.
radius: EDL radius.
strength: EDL strength.
"""
self.edl_enabled = enabled
self.edl_radius = radius
self.edl_strength = strength
self.call_js_method(
"setEDL",
enabled=enabled,
radius=radius,
strength=strength,
)
set_fov(self, fov)
¶
Set field of view.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
fov |
float |
Field of view in degrees. |
required |
Source code in anymap_ts/potree.py
def set_fov(self, fov: float) -> None:
"""Set field of view.
Args:
fov: Field of view in degrees.
"""
self.fov = fov
self.call_js_method("setFOV", fov=fov)
set_point_budget(self, budget)
¶
Set the point budget (max points to render).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
budget |
int |
Maximum number of points. |
required |
Source code in anymap_ts/potree.py
def set_point_budget(self, budget: int) -> None:
"""Set the point budget (max points to render).
Args:
budget: Maximum number of points.
"""
self.point_budget = budget
self.call_js_method("setPointBudget", budget=budget)
set_point_cloud_visibility(self, name, visible)
¶
Set point cloud visibility.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
name |
str |
Point cloud name. |
required |
visible |
bool |
Whether to show the point cloud. |
required |
Source code in anymap_ts/potree.py
def set_point_cloud_visibility(self, name: str, visible: bool) -> None:
"""Set point cloud visibility.
Args:
name: Point cloud name.
visible: Whether to show the point cloud.
"""
self.call_js_method("setPointCloudVisibility", name=name, visible=visible)
set_point_size(self, size)
¶
Set default point size.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
size |
float |
Point size. |
required |
Source code in anymap_ts/potree.py
def set_point_size(self, size: float) -> None:
"""Set default point size.
Args:
size: Point size.
"""
self.point_size = size
self.call_js_method("setPointSize", size=size)
utils
¶
Utility functions for anymap-ts.
get_bounds(data)
¶
Calculate bounds from GeoJSON or GeoDataFrame.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data |
Any |
GeoJSON dict or GeoDataFrame |
required |
Returns:
| Type | Description |
|---|---|
Optional[List[float]] |
[west, south, east, north] bounds or None |
Source code in anymap_ts/utils.py
def get_bounds(data: Any) -> Optional[List[float]]:
"""Calculate bounds from GeoJSON or GeoDataFrame.
Args:
data: GeoJSON dict or GeoDataFrame
Returns:
[west, south, east, north] bounds or None
"""
if HAS_GEOPANDAS and isinstance(data, gpd.GeoDataFrame):
bounds = data.total_bounds
return [bounds[0], bounds[1], bounds[2], bounds[3]]
if isinstance(data, dict):
if HAS_SHAPELY:
return _get_geojson_bounds_shapely(data)
return _get_geojson_bounds_simple(data)
return None
get_default_paint(layer_type)
¶
Get default paint properties for a layer type.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
layer_type |
str |
MapLibre layer type |
required |
Returns:
| Type | Description |
|---|---|
Dict[str, Any] |
Paint properties dict |
Source code in anymap_ts/utils.py
def get_default_paint(layer_type: str) -> Dict[str, Any]:
"""Get default paint properties for a layer type.
Args:
layer_type: MapLibre layer type
Returns:
Paint properties dict
"""
defaults = {
"circle": {
"circle-radius": 5,
"circle-color": "#3388ff",
"circle-opacity": 0.8,
"circle-stroke-width": 1,
"circle-stroke-color": "#ffffff",
},
"line": {
"line-color": "#3388ff",
"line-width": 2,
"line-opacity": 0.8,
},
"fill": {
"fill-color": "#3388ff",
"fill-opacity": 0.5,
"fill-outline-color": "#0000ff",
},
"fill-extrusion": {
"fill-extrusion-color": "#3388ff",
"fill-extrusion-opacity": 0.6,
"fill-extrusion-height": 100,
},
"raster": {
"raster-opacity": 1,
},
"heatmap": {
"heatmap-opacity": 0.8,
},
}
return defaults.get(layer_type, {})
infer_layer_type(geojson)
¶
Infer MapLibre layer type from GeoJSON geometry.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
geojson |
Dict |
GeoJSON dict |
required |
Returns:
| Type | Description |
|---|---|
str |
Layer type ('circle', 'line', 'fill') |
Source code in anymap_ts/utils.py
def infer_layer_type(geojson: Dict) -> str:
"""Infer MapLibre layer type from GeoJSON geometry.
Args:
geojson: GeoJSON dict
Returns:
Layer type ('circle', 'line', 'fill')
"""
geometry_type = None
if geojson.get("type") == "FeatureCollection":
features = geojson.get("features", [])
if features:
geometry_type = features[0].get("geometry", {}).get("type")
elif geojson.get("type") == "Feature":
geometry_type = geojson.get("geometry", {}).get("type")
else:
geometry_type = geojson.get("type")
type_map = {
"Point": "circle",
"MultiPoint": "circle",
"LineString": "line",
"MultiLineString": "line",
"Polygon": "fill",
"MultiPolygon": "fill",
"GeometryCollection": "fill",
}
return type_map.get(geometry_type, "circle")
to_geojson(data)
¶
Convert various data formats to GeoJSON.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data |
Any |
GeoJSON dict, GeoDataFrame, file path, or URL |
required |
Returns:
| Type | Description |
|---|---|
Dict |
GeoJSON dict |
Exceptions:
| Type | Description |
|---|---|
ValueError |
If data cannot be converted |
ImportError |
If geopandas is required but not installed |
Source code in anymap_ts/utils.py
def to_geojson(data: Any) -> Dict:
"""Convert various data formats to GeoJSON.
Args:
data: GeoJSON dict, GeoDataFrame, file path, or URL
Returns:
GeoJSON dict
Raises:
ValueError: If data cannot be converted
ImportError: If geopandas is required but not installed
"""
# Already a dict (GeoJSON)
if isinstance(data, dict):
return data
# GeoDataFrame
if HAS_GEOPANDAS and isinstance(data, gpd.GeoDataFrame):
return json.loads(data.to_json())
# File path or URL
if isinstance(data, (str, Path)):
path_str = str(data)
# If it's a URL, return as-is (will be handled by JS)
if path_str.startswith(("http://", "https://")):
return {"type": "url", "url": path_str}
# Read file with geopandas
if not HAS_GEOPANDAS:
raise ImportError(
"geopandas is required to read vector files. "
"Install with: pip install anymap-ts[vector]"
)
gdf = gpd.read_file(path_str)
return json.loads(gdf.to_json())
# Has __geo_interface__ (shapely geometry, etc.)
if hasattr(data, "__geo_interface__"):
geo = data.__geo_interface__
if geo.get("type") in (
"Point",
"LineString",
"Polygon",
"MultiPoint",
"MultiLineString",
"MultiPolygon",
"GeometryCollection",
):
return {"type": "Feature", "geometry": geo, "properties": {}}
return geo
raise ValueError(f"Cannot convert {type(data)} to GeoJSON")