Skip to content

deckgl module

DeckGL implementation of the map widget that extends MapLibre.

DeckGLMap (MapLibreMap)

DeckGL implementation of the map widget that extends MapLibre.

Source code in anymap/deckgl.py
class DeckGLMap(MapLibreMap):
    """DeckGL implementation of the map widget that extends MapLibre."""

    # DeckGL-specific traits
    deckgl_layers = traitlets.List([]).tag(sync=True)
    controller_options = traitlets.Dict({}).tag(sync=True)

    # Override the JavaScript module
    _esm = _esm_deckgl
    _css = _css_deckgl

    def __init__(
        self,
        center: List[float] = [0.0, 0.0],
        zoom: float = 2.0,
        style: str = "https://basemaps.cartocdn.com/gl/positron-nolabels-gl-style/style.json",
        width: str = "100%",
        height: str = "600px",
        bearing: float = 0.0,
        pitch: float = 0.0,
        controller_options: Dict[str, Any] = None,
        **kwargs,
    ):
        """Initialize DeckGL map widget.

        Args:
            center: Map center as [latitude, longitude]
            zoom: Initial zoom level
            style: MapLibre style URL or style object
            width: Widget width
            height: Widget height
            bearing: Map bearing (rotation) in degrees
            pitch: Map pitch (tilt) in degrees
            controller_options: DeckGL controller options
        """
        super().__init__(
            center=center,
            zoom=zoom,
            style=style,
            width=width,
            height=height,
            bearing=bearing,
            pitch=pitch,
            **kwargs,
        )

        if controller_options is None:
            controller_options = {"doubleClickZoom": False}
        self.controller_options = controller_options

    def add_deckgl_layer(self, layer_config: Dict[str, Any]) -> None:
        """Add a DeckGL layer to the map.

        Args:
            layer_config: DeckGL layer configuration dictionary
        """
        current_layers = list(self.deckgl_layers)
        current_layers.append(layer_config)
        self.deckgl_layers = current_layers

    def remove_deckgl_layer(self, layer_id: str) -> None:
        """Remove a DeckGL layer from the map.

        Args:
            layer_id: ID of the layer to remove
        """
        current_layers = [
            layer for layer in self.deckgl_layers if layer.get("id") != layer_id
        ]
        self.deckgl_layers = current_layers

    def clear_deckgl_layers(self) -> None:
        """Clear all DeckGL layers from the map."""
        self.deckgl_layers = []

    def add_geojson_layer(
        self,
        layer_id: str,
        geojson_data: Dict[str, Any],
        layer_type: str = "GeoJsonLayer",
        **layer_props,
    ) -> None:
        """Add a GeoJSON layer using DeckGL.

        Args:
            layer_id: Unique identifier for the layer
            geojson_data: GeoJSON data
            layer_type: DeckGL layer type (e.g., 'GeoJsonLayer')
            **layer_props: Additional DeckGL layer properties
        """
        layer_config = {
            "@@type": layer_type,
            "id": layer_id,
            "data": geojson_data,
            "pickable": True,
            "autoHighlight": True,
            **layer_props,
        }
        self.add_deckgl_layer(layer_config)

    def add_arc_layer(
        self,
        layer_id: str,
        data: Union[str, List[Dict[str, Any]]],
        get_source_position: Union[List[float], str] = None,
        get_target_position: Union[List[float], str] = None,
        **layer_props,
    ) -> None:
        """Add an Arc layer using DeckGL.

        Args:
            layer_id: Unique identifier for the layer
            data: Data source (URL or array of objects)
            get_source_position: Source position accessor (coordinates or accessor function)
            get_target_position: Target position accessor (coordinates or accessor function)
            **layer_props: Additional DeckGL layer properties
        """
        layer_config = {
            "@@type": "ArcLayer",
            "id": layer_id,
            "data": data,
            "pickable": True,
            "autoHighlight": True,
            **layer_props,
        }

        if get_source_position:
            layer_config["getSourcePosition"] = get_source_position
        if get_target_position:
            layer_config["getTargetPosition"] = get_target_position

        self.add_deckgl_layer(layer_config)

    def add_scatterplot_layer(
        self,
        layer_id: str,
        data: Union[str, List[Dict[str, Any]]],
        get_position: List[float] = None,
        get_radius: Union[int, List[int]] = 100,
        get_fill_color: List[int] = [255, 140, 0, 160],
        **layer_props,
    ) -> None:
        """Add a Scatterplot layer using DeckGL.

        Args:
            layer_id: Unique identifier for the layer
            data: Data source (URL or array of objects)
            get_position: Position accessor
            get_radius: Radius accessor
            get_fill_color: Fill color accessor
            **layer_props: Additional DeckGL layer properties
        """
        layer_config = {
            "@@type": "ScatterplotLayer",
            "id": layer_id,
            "data": data,
            "pickable": True,
            "autoHighlight": True,
            "radiusMinPixels": 2,
            "radiusMaxPixels": 100,
            "getRadius": get_radius,
            "getFillColor": get_fill_color,
            **layer_props,
        }

        if get_position:
            layer_config["getPosition"] = get_position

        self.add_deckgl_layer(layer_config)

    def to_html(
        self,
        filename: Optional[str] = None,
        title: str = "DeckGL Map Export",
        width: str = "100%",
        height: str = "600px",
        **kwargs,
    ) -> str:
        """Export the DeckGL map to a standalone HTML file.

        Args:
            filename: Optional filename to save the HTML. If None, returns HTML string.
            title: Title for the HTML page
            width: Width of the map container
            height: Height of the map container
            **kwargs: Additional arguments passed to the HTML template

        Returns:
            HTML string content
        """
        # Get the current map state
        map_state = {
            "center": self.center,
            "zoom": self.zoom,
            "width": width,
            "height": height,
            "style": self.style,
            "_layers": dict(self._layers),
            "_sources": dict(self._sources),
            "deckgl_layers": list(self.deckgl_layers),  # Include DeckGL layers
            "controller_options": dict(self.controller_options),
        }

        # Add class-specific attributes
        if hasattr(self, "style"):
            map_state["style"] = self.style
        if hasattr(self, "bearing"):
            map_state["bearing"] = self.bearing
        if hasattr(self, "pitch"):
            map_state["pitch"] = self.pitch
        if hasattr(self, "antialias"):
            map_state["antialias"] = self.antialias

        # Generate HTML content
        html_content = self._generate_html_template(map_state, title, **kwargs)

        # Save to file if filename is provided
        if filename:
            with open(filename, "w", encoding="utf-8") as f:
                f.write(html_content)

        return html_content

    def _generate_html_template(
        self, map_state: Dict[str, Any], title: str, **kwargs
    ) -> str:
        """Generate HTML template for DeckGL."""
        # 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://unpkg.com/deck.gl@9.1.12/dist.min.js"></script>
    <script src="https://unpkg.com/maplibre-gl@5.6.1/dist/maplibre-gl.js"></script>
    <link href="https://unpkg.com/maplibre-gl@5.6.1/dist/maplibre-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;
        }}
    </style>
</head>
<body>
    <h1>{title}</h1>
    <div id="map"></div>

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

        // Parse DeckGL layers from configuration
        function parseDeckGLLayers(layerConfigs) {{
            // Helper function to convert accessor expressions to functions
            function parseAccessor(accessor) {{
                if (typeof accessor === 'string' && accessor.startsWith('@@=')) {{
                    const expression = accessor.substring(3); // Remove '@@=' prefix

                    try {{
                        // Handle arrow function expressions directly
                        if (expression.includes('=>')) {{
                            // This is already an arrow function, just evaluate it
                            return eval(`(${{expression}})`);
                        }}
                        // Create a function from the expression
                        // Handle different variable contexts (d = data item, f = feature, etc.)
                        else if (expression.includes('f.geometry.coordinates')) {{
                            return new Function('f', `return ${{expression}}`);
                        }} else if (expression.includes('f.properties')) {{
                            return new Function('f', `return ${{expression}}`);
                        }} else if (expression.includes('d.features')) {{
                            // For dataTransform functions
                            return new Function('d', `return ${{expression}}`);
                        }} else if (expression.includes('d.')) {{
                            return new Function('d', `return ${{expression}}`);
                        }} else {{
                            // Default context
                            return new Function('d', `return ${{expression}}`);
                        }}
                    }} catch (error) {{
                        console.warn('Failed to parse accessor expression:', accessor, error);
                        return accessor; // Return original if parsing fails
                    }}
                }}
                return accessor;
            }}

            // Helper function to process layer properties and convert accessors
            function processLayerProps(props) {{
                const processed = {{ ...props }};

                // List of properties that should be treated as accessors
                const accessorProps = [
                    'getSourcePosition', 'getTargetPosition', 'getPosition',
                    'getRadius', 'getFillColor', 'getLineColor', 'getWidth',
                    'getPointRadius', 'dataTransform'
                ];

                accessorProps.forEach(prop => {{
                    if (prop in processed) {{
                        processed[prop] = parseAccessor(processed[prop]);
                    }}
                }});

                return processed;
            }}

            return layerConfigs.map(config => {{
                const layerType = config["@@type"];
                const layerProps = processLayerProps({{ ...config }});
                delete layerProps["@@type"];

                try {{
                    switch (layerType) {{
                        case "GeoJsonLayer":
                            return new deck.GeoJsonLayer(layerProps);
                        case "ArcLayer":
                            return new deck.ArcLayer(layerProps);
                        case "ScatterplotLayer":
                            return new deck.ScatterplotLayer(layerProps);
                        default:
                            console.warn(`Unknown DeckGL layer type: ${{layerType}}`);
                            return null;
                    }}
                }} catch (error) {{
                    console.error(`Error creating ${{layerType}}:`, error, layerProps);
                    return null;
                }}
            }}).filter(layer => layer !== null);
        }}

        // Initialize DeckGL with MapLibre
        const deckgl = new deck.DeckGL({{
            container: 'map',
            mapStyle: mapState.style || 'https://basemaps.cartocdn.com/gl/positron-nolabels-gl-style/style.json',
            initialViewState: {{
                latitude: mapState.center[0],
                longitude: mapState.center[1],
                zoom: mapState.zoom || 2,
                bearing: mapState.bearing || 0,
                pitch: mapState.pitch || 0
            }},
            controller: true,
            layers: parseDeckGLLayers(mapState.deckgl_layers || []),
            onViewStateChange: ({{viewState}}) => {{
                console.log('View state changed:', viewState);
            }},
            onClick: (info) => {{
                if (info.object) {{
                    console.log('Clicked object:', info.object);
                    if (info.object.properties && info.object.properties.name) {{
                        alert(`${{info.object.properties.name}} (${{info.object.properties.abbrev || 'N/A'}})`);
                    }}
                }}
            }}
        }});

        // Add navigation controls styling
        const mapContainer = document.getElementById('map');
        mapContainer.style.position = 'relative';

        console.log('DeckGL map initialized successfully');
        console.log('Loaded layers:', mapState.deckgl_layers ? mapState.deckgl_layers.length : 0);
    </script>
</body>
</html>"""

        return html_template

__init__(self, center=[0.0, 0.0], zoom=2.0, style='https://basemaps.cartocdn.com/gl/positron-nolabels-gl-style/style.json', width='100%', height='600px', bearing=0.0, pitch=0.0, controller_options=None, **kwargs) special

Initialize DeckGL 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

MapLibre style URL or style object

'https://basemaps.cartocdn.com/gl/positron-nolabels-gl-style/style.json'
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
controller_options Dict[str, Any]

DeckGL controller options

None
Source code in anymap/deckgl.py
def __init__(
    self,
    center: List[float] = [0.0, 0.0],
    zoom: float = 2.0,
    style: str = "https://basemaps.cartocdn.com/gl/positron-nolabels-gl-style/style.json",
    width: str = "100%",
    height: str = "600px",
    bearing: float = 0.0,
    pitch: float = 0.0,
    controller_options: Dict[str, Any] = None,
    **kwargs,
):
    """Initialize DeckGL map widget.

    Args:
        center: Map center as [latitude, longitude]
        zoom: Initial zoom level
        style: MapLibre style URL or style object
        width: Widget width
        height: Widget height
        bearing: Map bearing (rotation) in degrees
        pitch: Map pitch (tilt) in degrees
        controller_options: DeckGL controller options
    """
    super().__init__(
        center=center,
        zoom=zoom,
        style=style,
        width=width,
        height=height,
        bearing=bearing,
        pitch=pitch,
        **kwargs,
    )

    if controller_options is None:
        controller_options = {"doubleClickZoom": False}
    self.controller_options = controller_options

add_arc_layer(self, layer_id, data, get_source_position=None, get_target_position=None, **layer_props)

Add an Arc layer using DeckGL.

Parameters:

Name Type Description Default
layer_id str

Unique identifier for the layer

required
data Union[str, List[Dict[str, Any]]]

Data source (URL or array of objects)

required
get_source_position Union[List[float], str]

Source position accessor (coordinates or accessor function)

None
get_target_position Union[List[float], str]

Target position accessor (coordinates or accessor function)

None
**layer_props

Additional DeckGL layer properties

{}
Source code in anymap/deckgl.py
def add_arc_layer(
    self,
    layer_id: str,
    data: Union[str, List[Dict[str, Any]]],
    get_source_position: Union[List[float], str] = None,
    get_target_position: Union[List[float], str] = None,
    **layer_props,
) -> None:
    """Add an Arc layer using DeckGL.

    Args:
        layer_id: Unique identifier for the layer
        data: Data source (URL or array of objects)
        get_source_position: Source position accessor (coordinates or accessor function)
        get_target_position: Target position accessor (coordinates or accessor function)
        **layer_props: Additional DeckGL layer properties
    """
    layer_config = {
        "@@type": "ArcLayer",
        "id": layer_id,
        "data": data,
        "pickable": True,
        "autoHighlight": True,
        **layer_props,
    }

    if get_source_position:
        layer_config["getSourcePosition"] = get_source_position
    if get_target_position:
        layer_config["getTargetPosition"] = get_target_position

    self.add_deckgl_layer(layer_config)

add_deckgl_layer(self, layer_config)

Add a DeckGL layer to the map.

Parameters:

Name Type Description Default
layer_config Dict[str, Any]

DeckGL layer configuration dictionary

required
Source code in anymap/deckgl.py
def add_deckgl_layer(self, layer_config: Dict[str, Any]) -> None:
    """Add a DeckGL layer to the map.

    Args:
        layer_config: DeckGL layer configuration dictionary
    """
    current_layers = list(self.deckgl_layers)
    current_layers.append(layer_config)
    self.deckgl_layers = current_layers

add_geojson_layer(self, layer_id, geojson_data, layer_type='GeoJsonLayer', **layer_props)

Add a GeoJSON layer using DeckGL.

Parameters:

Name Type Description Default
layer_id str

Unique identifier for the layer

required
geojson_data Dict[str, Any]

GeoJSON data

required
layer_type str

DeckGL layer type (e.g., 'GeoJsonLayer')

'GeoJsonLayer'
**layer_props

Additional DeckGL layer properties

{}
Source code in anymap/deckgl.py
def add_geojson_layer(
    self,
    layer_id: str,
    geojson_data: Dict[str, Any],
    layer_type: str = "GeoJsonLayer",
    **layer_props,
) -> None:
    """Add a GeoJSON layer using DeckGL.

    Args:
        layer_id: Unique identifier for the layer
        geojson_data: GeoJSON data
        layer_type: DeckGL layer type (e.g., 'GeoJsonLayer')
        **layer_props: Additional DeckGL layer properties
    """
    layer_config = {
        "@@type": layer_type,
        "id": layer_id,
        "data": geojson_data,
        "pickable": True,
        "autoHighlight": True,
        **layer_props,
    }
    self.add_deckgl_layer(layer_config)

add_scatterplot_layer(self, layer_id, data, get_position=None, get_radius=100, get_fill_color=[255, 140, 0, 160], **layer_props)

Add a Scatterplot layer using DeckGL.

Parameters:

Name Type Description Default
layer_id str

Unique identifier for the layer

required
data Union[str, List[Dict[str, Any]]]

Data source (URL or array of objects)

required
get_position List[float]

Position accessor

None
get_radius Union[int, List[int]]

Radius accessor

100
get_fill_color List[int]

Fill color accessor

[255, 140, 0, 160]
**layer_props

Additional DeckGL layer properties

{}
Source code in anymap/deckgl.py
def add_scatterplot_layer(
    self,
    layer_id: str,
    data: Union[str, List[Dict[str, Any]]],
    get_position: List[float] = None,
    get_radius: Union[int, List[int]] = 100,
    get_fill_color: List[int] = [255, 140, 0, 160],
    **layer_props,
) -> None:
    """Add a Scatterplot layer using DeckGL.

    Args:
        layer_id: Unique identifier for the layer
        data: Data source (URL or array of objects)
        get_position: Position accessor
        get_radius: Radius accessor
        get_fill_color: Fill color accessor
        **layer_props: Additional DeckGL layer properties
    """
    layer_config = {
        "@@type": "ScatterplotLayer",
        "id": layer_id,
        "data": data,
        "pickable": True,
        "autoHighlight": True,
        "radiusMinPixels": 2,
        "radiusMaxPixels": 100,
        "getRadius": get_radius,
        "getFillColor": get_fill_color,
        **layer_props,
    }

    if get_position:
        layer_config["getPosition"] = get_position

    self.add_deckgl_layer(layer_config)

clear_deckgl_layers(self)

Clear all DeckGL layers from the map.

Source code in anymap/deckgl.py
def clear_deckgl_layers(self) -> None:
    """Clear all DeckGL layers from the map."""
    self.deckgl_layers = []

remove_deckgl_layer(self, layer_id)

Remove a DeckGL layer from the map.

Parameters:

Name Type Description Default
layer_id str

ID of the layer to remove

required
Source code in anymap/deckgl.py
def remove_deckgl_layer(self, layer_id: str) -> None:
    """Remove a DeckGL layer from the map.

    Args:
        layer_id: ID of the layer to remove
    """
    current_layers = [
        layer for layer in self.deckgl_layers if layer.get("id") != layer_id
    ]
    self.deckgl_layers = current_layers

to_html(self, filename=None, title='DeckGL Map Export', width='100%', height='600px', **kwargs)

Export the DeckGL map to a standalone HTML file.

Parameters:

Name Type Description Default
filename Optional[str]

Optional filename to save the HTML. If None, returns HTML string.

None
title str

Title for the HTML page

'DeckGL Map Export'
width str

Width of the map container

'100%'
height str

Height of the map container

'600px'
**kwargs

Additional arguments passed to the HTML template

{}

Returns:

Type Description
str

HTML string content

Source code in anymap/deckgl.py
def to_html(
    self,
    filename: Optional[str] = None,
    title: str = "DeckGL Map Export",
    width: str = "100%",
    height: str = "600px",
    **kwargs,
) -> str:
    """Export the DeckGL map to a standalone HTML file.

    Args:
        filename: Optional filename to save the HTML. If None, returns HTML string.
        title: Title for the HTML page
        width: Width of the map container
        height: Height of the map container
        **kwargs: Additional arguments passed to the HTML template

    Returns:
        HTML string content
    """
    # Get the current map state
    map_state = {
        "center": self.center,
        "zoom": self.zoom,
        "width": width,
        "height": height,
        "style": self.style,
        "_layers": dict(self._layers),
        "_sources": dict(self._sources),
        "deckgl_layers": list(self.deckgl_layers),  # Include DeckGL layers
        "controller_options": dict(self.controller_options),
    }

    # Add class-specific attributes
    if hasattr(self, "style"):
        map_state["style"] = self.style
    if hasattr(self, "bearing"):
        map_state["bearing"] = self.bearing
    if hasattr(self, "pitch"):
        map_state["pitch"] = self.pitch
    if hasattr(self, "antialias"):
        map_state["antialias"] = self.antialias

    # Generate HTML content
    html_content = self._generate_html_template(map_state, title, **kwargs)

    # Save to file if filename is provided
    if filename:
        with open(filename, "w", encoding="utf-8") as f:
            f.write(html_content)

    return html_content