Skip to content

mapbox module

Mapbox GL JS implementation of the map widget.

This module provides the MapboxMap class which implements an interactive map widget using the Mapbox GL JS library. Mapbox GL JS provides fast vector map rendering with WebGL and requires an access token for Mapbox services.

Classes

MapboxMap: Main map widget class for Mapbox GL JS.

Note

Mapbox services require an access token. You can get a free token at https://account.mapbox.com/access-tokens/

Examples:

Basic usage of MapboxMap:

>>> from anymap.mapbox import MapboxMap
>>> m = MapboxMap(
...     center=[40.7, -74.0],
...     zoom=10,
...     access_token="your_mapbox_token"
... )
>>> m.add_basemap("OpenStreetMap.Mapnik")
>>> m

MapboxMap (MapWidget)

Mapbox GL JS implementation of the map widget.

Source code in anymap/mapbox.py
class MapboxMap(MapWidget):
    """Mapbox GL JS implementation of the map widget."""

    # Mapbox-specific traits
    style = traitlets.Unicode("mapbox://styles/mapbox/streets-v12").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)
    access_token = traitlets.Unicode("").tag(sync=True)

    # Define the JavaScript module path
    _esm = _esm_mapbox
    _css = _css_mapbox

    def __init__(
        self,
        center: List[float] = [0.0, 0.0],
        zoom: float = 2.0,
        style: str = "mapbox://styles/mapbox/streets-v12",
        width: str = "100%",
        height: str = "600px",
        bearing: float = 0.0,
        pitch: float = 0.0,
        access_token: str = "",
        **kwargs,
    ):
        """Initialize Mapbox map widget.

        Args:
            center: Map center as [latitude, longitude]
            zoom: Initial zoom level
            style: Mapbox style URL or style object
            width: Widget width
            height: Widget height
            bearing: Map bearing (rotation) in degrees
            pitch: Map pitch (tilt) in degrees
            access_token: Mapbox access token (required for Mapbox services).
                         Get a free token at https://account.mapbox.com/access-tokens/
                         Can also be set via MAPBOX_TOKEN environment variable.
        """
        # Set default access token if not provided
        if not access_token:
            access_token = self._get_default_access_token()

        super().__init__(
            center=center,
            zoom=zoom,
            width=width,
            height=height,
            style=style,
            bearing=bearing,
            pitch=pitch,
            access_token=access_token,
            **kwargs,
        )

    @staticmethod
    def _get_default_access_token() -> str:
        """Get default Mapbox access token from environment or return demo token."""
        import os

        # Try to get from environment variable
        token = os.environ.get("MAPBOX_TOKEN") or os.environ.get("MAPBOX_ACCESS_TOKEN")

        # If no token found, return empty string - user must provide their own token
        if not token:
            import warnings

            warnings.warn(
                "No Mapbox access token found. Please set MAPBOX_ACCESS_TOKEN environment variable "
                "or pass access_token parameter. Get a free token at https://account.mapbox.com/access-tokens/",
                UserWarning,
            )
            token = ""

        return token

    def set_access_token(self, token: str) -> None:
        """Set the Mapbox access token."""
        self.access_token = token

    def set_style(self, style: Union[str, Dict[str, Any]]) -> None:
        """Set the map style."""
        if isinstance(style, str):
            self.style = style
        else:
            self.call_js_method("setStyle", style)

    def set_bearing(self, bearing: float) -> None:
        """Set the map bearing (rotation)."""
        self.bearing = bearing

    def set_pitch(self, pitch: float) -> None:
        """Set the map pitch (tilt)."""
        self.pitch = pitch

    def add_geojson_layer(
        self,
        layer_id: str,
        geojson_data: Dict[str, Any],
        layer_type: str = "fill",
        paint: Optional[Dict[str, Any]] = None,
    ) -> None:
        """Add a GeoJSON layer to the map."""
        source_id = f"{layer_id}_source"

        # Add source
        self.add_source(source_id, {"type": "geojson", "data": geojson_data})

        # Add layer
        layer_config = {"id": layer_id, "type": layer_type, "source": source_id}

        if paint:
            layer_config["paint"] = paint

        self.add_layer(layer_id, layer_config)

    def add_marker(self, lat: float, lng: float, popup: Optional[str] = None) -> None:
        """Add a marker to the map."""
        marker_data = {"coordinates": [lng, lat], "popup": popup}
        self.call_js_method("addMarker", marker_data)

    def fit_bounds(self, bounds: List[List[float]], padding: int = 50) -> None:
        """Fit the map to given bounds."""
        self.call_js_method("fitBounds", bounds, {"padding": padding})

    def add_tile_layer(
        self,
        layer_id: str,
        source_url: str,
        paint: Optional[Dict[str, Any]] = None,
        layout: Optional[Dict[str, Any]] = None,
    ) -> None:
        """Add a raster layer to the map."""
        source_id = f"{layer_id}_source"

        # Add raster source
        self.add_source(
            source_id, {"type": "raster", "tiles": [source_url], "tileSize": 256}
        )

        # Add raster layer
        layer_config = {"id": layer_id, "type": "raster", "source": source_id}

        if paint:
            layer_config["paint"] = paint
        if layout:
            layer_config["layout"] = layout

        self.add_layer(layer_id, layer_config)

    def add_vector_layer(
        self,
        layer_id: str,
        source_url: str,
        source_layer: str,
        layer_type: str = "fill",
        paint: Optional[Dict[str, Any]] = None,
        layout: Optional[Dict[str, Any]] = None,
    ) -> None:
        """Add a vector tile layer to the map."""
        source_id = f"{layer_id}_source"

        # Add vector source
        self.add_source(source_id, {"type": "vector", "url": source_url})

        # Add vector layer
        layer_config = {
            "id": layer_id,
            "type": layer_type,
            "source": source_id,
            "source-layer": source_layer,
        }

        if paint:
            layer_config["paint"] = paint
        if layout:
            layer_config["layout"] = layout

        self.add_layer(layer_id, layer_config)

    def add_image_layer(
        self,
        layer_id: str,
        image_url: str,
        coordinates: List[List[float]],
        paint: Optional[Dict[str, Any]] = None,
    ) -> None:
        """Add an image layer to the map."""
        source_id = f"{layer_id}_source"

        # Add image source
        self.add_source(
            source_id, {"type": "image", "url": image_url, "coordinates": coordinates}
        )

        # Add raster layer for the image
        layer_config = {"id": layer_id, "type": "raster", "source": source_id}

        if paint:
            layer_config["paint"] = paint

        self.add_layer(layer_id, layer_config)

    def add_control(
        self,
        control_type: str,
        position: str = "top-right",
        options: Optional[Dict[str, Any]] = None,
    ) -> None:
        """Add a control to the map.

        Args:
            control_type: Type of control ('navigation', 'scale', 'fullscreen', 'geolocate')
            position: Position on map ('top-left', 'top-right', 'bottom-left', 'bottom-right')
            options: Additional options for the control
        """
        control_options = options or {}
        control_options["position"] = position
        self.call_js_method("addControl", control_type, control_options)

    def set_terrain(self, terrain_config: Optional[Dict[str, Any]] = None) -> None:
        """Set 3D terrain on the map.

        Args:
            terrain_config: Terrain configuration dict, or None to remove terrain
        """
        self.call_js_method("setTerrain", terrain_config)

    def set_fog(self, fog_config: Optional[Dict[str, Any]] = None) -> None:
        """Set atmospheric fog on the map.

        Args:
            fog_config: Fog configuration dict, or None to remove fog
        """
        self.call_js_method("setFog", fog_config)

    def add_3d_buildings(self, layer_id: str = "3d-buildings") -> None:
        """Add 3D buildings layer to the map."""
        # Add the layer for 3D buildings
        layer_config = {
            "id": layer_id,
            "source": "composite",
            "source-layer": "building",
            "filter": ["==", "extrude", "true"],
            "type": "fill-extrusion",
            "minzoom": 15,
            "paint": {
                "fill-extrusion-color": "#aaa",
                "fill-extrusion-height": [
                    "interpolate",
                    ["linear"],
                    ["zoom"],
                    15,
                    0,
                    15.05,
                    ["get", "height"],
                ],
                "fill-extrusion-base": [
                    "interpolate",
                    ["linear"],
                    ["zoom"],
                    15,
                    0,
                    15.05,
                    ["get", "min_height"],
                ],
                "fill-extrusion-opacity": 0.6,
            },
        }
        self.add_layer(layer_id, layer_config)

    def add_basemap(self, basemap: str, layer_id: str = "basemap") -> None:
        """Add a basemap to the map using xyzservices providers.

        Args:
            basemap: Name of the basemap from xyzservices (e.g., "Esri.WorldImagery")
            layer_id: ID for the basemap layer (default: "basemap")
        """
        if basemap not in available_basemaps:
            available_names = list(available_basemaps.keys())
            raise ValueError(
                f"Basemap '{basemap}' not found. Available basemaps: {available_names}"
            )

        basemap_config = available_basemaps[basemap]

        # Convert xyzservices URL template to tile URL
        tile_url = basemap_config.build_url()

        # Get attribution if available
        attribution = basemap_config.get("attribution", "")

        # Add as raster layer
        self.add_tile_layer(
            layer_id=layer_id, source_url=tile_url, paint={"raster-opacity": 1.0}
        )

    def _generate_html_template(
        self, map_state: Dict[str, Any], title: str, **kwargs
    ) -> str:
        """Generate HTML template for Mapbox GL JS."""
        # Serialize map state for JavaScript
        map_state_json = json.dumps(map_state, indent=2)

        html_template = f"""<!DOCTYPE html>
<html>
<head>
    <title>{title}</title>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <script src="https://api.mapbox.com/mapbox-gl-js/v3.13.0/mapbox-gl.js"></script>
    <link href="https://api.mapbox.com/mapbox-gl-js/v3.13.0/mapbox-gl.css" rel="stylesheet">
    <style>
        body {{
            margin: 0;
            padding: 20px;
            font-family: Arial, sans-serif;
        }}
        #map {{
            width: {map_state['width']};
            height: {map_state['height']};
            border: 1px solid #ccc;
        }}
        h1 {{
            margin-top: 0;
            color: #333;
        }}
        .access-token-warning {{
            background-color: #fff3cd;
            border: 1px solid #ffeaa7;
            color: #856404;
            padding: 10px;
            margin-bottom: 20px;
            border-radius: 4px;
        }}
    </style>
</head>
<body>
    <h1>{title}</h1>
    {"<div class='access-token-warning'>Warning: This map requires a Mapbox access token. Please add your token to the mapboxgl.accessToken property.</div>" if not map_state.get('access_token') else ""}
    <div id="map"></div>

    <script>
        // Map state from Python
        const mapState = {map_state_json};

        // Set Mapbox access token
        mapboxgl.accessToken = mapState.access_token || '';

        // Initialize Mapbox map
        const map = new mapboxgl.Map({{
            container: 'map',
            style: mapState.style || 'mapbox://styles/mapbox/streets-v12',
            center: [mapState.center[1], mapState.center[0]], // Convert [lat, lng] to [lng, lat]
            zoom: mapState.zoom || 2,
            bearing: mapState.bearing || 0,
            pitch: mapState.pitch || 0,
            antialias: mapState.antialias !== undefined ? mapState.antialias : true
        }});

        // Restore layers and sources after map loads
        map.on('load', function() {{
            // Add sources first
            const sources = mapState._sources || {{}};
            Object.entries(sources).forEach(([sourceId, sourceConfig]) => {{
                try {{
                    map.addSource(sourceId, sourceConfig);
                }} catch (error) {{
                    console.warn(`Failed to add source ${{sourceId}}:`, error);
                }}
            }});

            // Then add layers
            const layers = mapState._layers || {{}};
            Object.entries(layers).forEach(([layerId, layerConfig]) => {{
                try {{
                    map.addLayer(layerConfig);
                }} catch (error) {{
                    console.warn(`Failed to add layer ${{layerId}}:`, error);
                }}
            }});
        }});

        // Add navigation controls
        map.addControl(new mapboxgl.NavigationControl());

        // Add scale control
        map.addControl(new mapboxgl.ScaleControl());

        // Log map events for debugging
        map.on('click', function(e) {{
            console.log('Map clicked at:', e.lngLat);
        }});

        map.on('load', function() {{
            console.log('Map loaded successfully');
        }});

        map.on('error', function(e) {{
            console.error('Map error:', e);
        }});
    </script>
</body>
</html>"""

        return html_template

__init__(self, center=[0.0, 0.0], zoom=2.0, style='mapbox://styles/mapbox/streets-v12', width='100%', height='600px', bearing=0.0, pitch=0.0, access_token='', **kwargs) special

Initialize Mapbox map widget.

Parameters:

Name Type Description Default
center List[float]

Map center as [latitude, longitude]

[0.0, 0.0]
zoom float

Initial zoom level

2.0
style str

Mapbox style URL or style object

'mapbox://styles/mapbox/streets-v12'
width str

Widget width

'100%'
height str

Widget height

'600px'
bearing float

Map bearing (rotation) in degrees

0.0
pitch float

Map pitch (tilt) in degrees

0.0
access_token str

Mapbox access token (required for Mapbox services). Get a free token at https://account.mapbox.com/access-tokens/ Can also be set via MAPBOX_TOKEN environment variable.

''
Source code in anymap/mapbox.py
def __init__(
    self,
    center: List[float] = [0.0, 0.0],
    zoom: float = 2.0,
    style: str = "mapbox://styles/mapbox/streets-v12",
    width: str = "100%",
    height: str = "600px",
    bearing: float = 0.0,
    pitch: float = 0.0,
    access_token: str = "",
    **kwargs,
):
    """Initialize Mapbox map widget.

    Args:
        center: Map center as [latitude, longitude]
        zoom: Initial zoom level
        style: Mapbox style URL or style object
        width: Widget width
        height: Widget height
        bearing: Map bearing (rotation) in degrees
        pitch: Map pitch (tilt) in degrees
        access_token: Mapbox access token (required for Mapbox services).
                     Get a free token at https://account.mapbox.com/access-tokens/
                     Can also be set via MAPBOX_TOKEN environment variable.
    """
    # Set default access token if not provided
    if not access_token:
        access_token = self._get_default_access_token()

    super().__init__(
        center=center,
        zoom=zoom,
        width=width,
        height=height,
        style=style,
        bearing=bearing,
        pitch=pitch,
        access_token=access_token,
        **kwargs,
    )

add_3d_buildings(self, layer_id='3d-buildings')

Add 3D buildings layer to the map.

Source code in anymap/mapbox.py
def add_3d_buildings(self, layer_id: str = "3d-buildings") -> None:
    """Add 3D buildings layer to the map."""
    # Add the layer for 3D buildings
    layer_config = {
        "id": layer_id,
        "source": "composite",
        "source-layer": "building",
        "filter": ["==", "extrude", "true"],
        "type": "fill-extrusion",
        "minzoom": 15,
        "paint": {
            "fill-extrusion-color": "#aaa",
            "fill-extrusion-height": [
                "interpolate",
                ["linear"],
                ["zoom"],
                15,
                0,
                15.05,
                ["get", "height"],
            ],
            "fill-extrusion-base": [
                "interpolate",
                ["linear"],
                ["zoom"],
                15,
                0,
                15.05,
                ["get", "min_height"],
            ],
            "fill-extrusion-opacity": 0.6,
        },
    }
    self.add_layer(layer_id, layer_config)

add_basemap(self, basemap, layer_id='basemap')

Add a basemap to the map using xyzservices providers.

Parameters:

Name Type Description Default
basemap str

Name of the basemap from xyzservices (e.g., "Esri.WorldImagery")

required
layer_id str

ID for the basemap layer (default: "basemap")

'basemap'
Source code in anymap/mapbox.py
def add_basemap(self, basemap: str, layer_id: str = "basemap") -> None:
    """Add a basemap to the map using xyzservices providers.

    Args:
        basemap: Name of the basemap from xyzservices (e.g., "Esri.WorldImagery")
        layer_id: ID for the basemap layer (default: "basemap")
    """
    if basemap not in available_basemaps:
        available_names = list(available_basemaps.keys())
        raise ValueError(
            f"Basemap '{basemap}' not found. Available basemaps: {available_names}"
        )

    basemap_config = available_basemaps[basemap]

    # Convert xyzservices URL template to tile URL
    tile_url = basemap_config.build_url()

    # Get attribution if available
    attribution = basemap_config.get("attribution", "")

    # Add as raster layer
    self.add_tile_layer(
        layer_id=layer_id, source_url=tile_url, paint={"raster-opacity": 1.0}
    )

add_control(self, control_type, position='top-right', options=None)

Add a control to the map.

Parameters:

Name Type Description Default
control_type str

Type of control ('navigation', 'scale', 'fullscreen', 'geolocate')

required
position str

Position on map ('top-left', 'top-right', 'bottom-left', 'bottom-right')

'top-right'
options Optional[Dict[str, Any]]

Additional options for the control

None
Source code in anymap/mapbox.py
def add_control(
    self,
    control_type: str,
    position: str = "top-right",
    options: Optional[Dict[str, Any]] = None,
) -> None:
    """Add a control to the map.

    Args:
        control_type: Type of control ('navigation', 'scale', 'fullscreen', 'geolocate')
        position: Position on map ('top-left', 'top-right', 'bottom-left', 'bottom-right')
        options: Additional options for the control
    """
    control_options = options or {}
    control_options["position"] = position
    self.call_js_method("addControl", control_type, control_options)

add_geojson_layer(self, layer_id, geojson_data, layer_type='fill', paint=None)

Add a GeoJSON layer to the map.

Source code in anymap/mapbox.py
def add_geojson_layer(
    self,
    layer_id: str,
    geojson_data: Dict[str, Any],
    layer_type: str = "fill",
    paint: Optional[Dict[str, Any]] = None,
) -> None:
    """Add a GeoJSON layer to the map."""
    source_id = f"{layer_id}_source"

    # Add source
    self.add_source(source_id, {"type": "geojson", "data": geojson_data})

    # Add layer
    layer_config = {"id": layer_id, "type": layer_type, "source": source_id}

    if paint:
        layer_config["paint"] = paint

    self.add_layer(layer_id, layer_config)

add_image_layer(self, layer_id, image_url, coordinates, paint=None)

Add an image layer to the map.

Source code in anymap/mapbox.py
def add_image_layer(
    self,
    layer_id: str,
    image_url: str,
    coordinates: List[List[float]],
    paint: Optional[Dict[str, Any]] = None,
) -> None:
    """Add an image layer to the map."""
    source_id = f"{layer_id}_source"

    # Add image source
    self.add_source(
        source_id, {"type": "image", "url": image_url, "coordinates": coordinates}
    )

    # Add raster layer for the image
    layer_config = {"id": layer_id, "type": "raster", "source": source_id}

    if paint:
        layer_config["paint"] = paint

    self.add_layer(layer_id, layer_config)

add_marker(self, lat, lng, popup=None)

Add a marker to the map.

Source code in anymap/mapbox.py
def add_marker(self, lat: float, lng: float, popup: Optional[str] = None) -> None:
    """Add a marker to the map."""
    marker_data = {"coordinates": [lng, lat], "popup": popup}
    self.call_js_method("addMarker", marker_data)

add_tile_layer(self, layer_id, source_url, paint=None, layout=None)

Add a raster layer to the map.

Source code in anymap/mapbox.py
def add_tile_layer(
    self,
    layer_id: str,
    source_url: str,
    paint: Optional[Dict[str, Any]] = None,
    layout: Optional[Dict[str, Any]] = None,
) -> None:
    """Add a raster layer to the map."""
    source_id = f"{layer_id}_source"

    # Add raster source
    self.add_source(
        source_id, {"type": "raster", "tiles": [source_url], "tileSize": 256}
    )

    # Add raster layer
    layer_config = {"id": layer_id, "type": "raster", "source": source_id}

    if paint:
        layer_config["paint"] = paint
    if layout:
        layer_config["layout"] = layout

    self.add_layer(layer_id, layer_config)

add_vector_layer(self, layer_id, source_url, source_layer, layer_type='fill', paint=None, layout=None)

Add a vector tile layer to the map.

Source code in anymap/mapbox.py
def add_vector_layer(
    self,
    layer_id: str,
    source_url: str,
    source_layer: str,
    layer_type: str = "fill",
    paint: Optional[Dict[str, Any]] = None,
    layout: Optional[Dict[str, Any]] = None,
) -> None:
    """Add a vector tile layer to the map."""
    source_id = f"{layer_id}_source"

    # Add vector source
    self.add_source(source_id, {"type": "vector", "url": source_url})

    # Add vector layer
    layer_config = {
        "id": layer_id,
        "type": layer_type,
        "source": source_id,
        "source-layer": source_layer,
    }

    if paint:
        layer_config["paint"] = paint
    if layout:
        layer_config["layout"] = layout

    self.add_layer(layer_id, layer_config)

fit_bounds(self, bounds, padding=50)

Fit the map to given bounds.

Source code in anymap/mapbox.py
def fit_bounds(self, bounds: List[List[float]], padding: int = 50) -> None:
    """Fit the map to given bounds."""
    self.call_js_method("fitBounds", bounds, {"padding": padding})

set_access_token(self, token)

Set the Mapbox access token.

Source code in anymap/mapbox.py
def set_access_token(self, token: str) -> None:
    """Set the Mapbox access token."""
    self.access_token = token

set_bearing(self, bearing)

Set the map bearing (rotation).

Source code in anymap/mapbox.py
def set_bearing(self, bearing: float) -> None:
    """Set the map bearing (rotation)."""
    self.bearing = bearing

set_fog(self, fog_config=None)

Set atmospheric fog on the map.

Parameters:

Name Type Description Default
fog_config Optional[Dict[str, Any]]

Fog configuration dict, or None to remove fog

None
Source code in anymap/mapbox.py
def set_fog(self, fog_config: Optional[Dict[str, Any]] = None) -> None:
    """Set atmospheric fog on the map.

    Args:
        fog_config: Fog configuration dict, or None to remove fog
    """
    self.call_js_method("setFog", fog_config)

set_pitch(self, pitch)

Set the map pitch (tilt).

Source code in anymap/mapbox.py
def set_pitch(self, pitch: float) -> None:
    """Set the map pitch (tilt)."""
    self.pitch = pitch

set_style(self, style)

Set the map style.

Source code in anymap/mapbox.py
def set_style(self, style: Union[str, Dict[str, Any]]) -> None:
    """Set the map style."""
    if isinstance(style, str):
        self.style = style
    else:
        self.call_js_method("setStyle", style)

set_terrain(self, terrain_config=None)

Set 3D terrain on the map.

Parameters:

Name Type Description Default
terrain_config Optional[Dict[str, Any]]

Terrain configuration dict, or None to remove terrain

None
Source code in anymap/mapbox.py
def set_terrain(self, terrain_config: Optional[Dict[str, Any]] = None) -> None:
    """Set 3D terrain on the map.

    Args:
        terrain_config: Terrain configuration dict, or None to remove terrain
    """
    self.call_js_method("setTerrain", terrain_config)