Skip to content

mapbox module

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", "")