Layers Control
the compiled dl2.LayersControl component.
---
.. llms_copy::Layers Control
.. toc::
Overview
Demonstrates the real Python API (this is what users would write), not the JS DEMO style used by the other showcase pages. Two-way: the UI radios/checkboxes write activeBase / activeOverlays back to Python; Python callbacks also push those props to flip the control.
Live demo
.. exec::docs.layers-control.example :code: false
Source
```python
File: docs/layers-control/example.py
""" Layers Control — the compiled dl2.LayersControl component.
Demonstrates the real Python API (this is what users would write), not the JS DEMO style used by the other showcase pages. Two-way: the UI radios/checkboxes write activeBase / activeOverlays back to Python; Python callbacks also push those props to flip the control. """
import dash_leaflet2 as dl2 import dash_mantine_components as dmc from dash import Input, Output, State, callback from dl2_locations import MINNEAPOLIS from dl2_shared import code_panel, header, info_panel
OSM = "https://tile.openstreetmap.org/{z}/{x}/{y}.png" CARTO_LIGHT = "https://basemaps.cartocdn.com/light_all/{z}/{x}/{y}.png" CARTO_DARK = "https://basemaps.cartocdn.com/dark_all/{z}/{x}/{y}.png" ATTR = ( '© <a href="https://openstreetmap.org/copyright">OpenStreetMap</a> ' '© <a href="https://carto.com/attributions">CARTO</a>' )
SENSORS = { "type": "FeatureCollection", "features": [ { "type": "Feature", "properties": {"name": "buoy 1"}, # GeoJSON is [lon, lat] — the opposite order to Leaflet. "geometry": {"type": "Point", "coordinates": MINNEAPOLIS.at_lonlat(2.2, 1.0)}, }, { "type": "Feature", "properties": {"name": "buoy 2"}, "geometry": {"type": "Point", "coordinates": MINNEAPOLIS.at_lonlat(-1.1, -2.9)}, }, { "type": "Feature", "properties": {"name": "buoy 3"}, "geometry": {"type": "Point", "coordinates": MINNEAPOLIS.at_lonlat(4.5, -1.0)}, }, ], }
CODE = '''dl2.Map(children=[ dl2.LayersControl(id="lc", children=[ dl2.BaseLayer(dl2.TileLayer(url=CARTO_LIGHT), name="Light", checked=True), dl2.BaseLayer(dl2.TileLayer(url=CARTO_DARK), name="Dark"), dl2.BaseLayer(dl2.TileLayer(url=OSM), name="OSM"), dl2.Overlay(dl2.Polygon(...), name="Harbor zone", checked=True), dl2.Overlay(dl2.Circle(...), name="Buoy radius"), dl2.Overlay(dl2.GeoJSON(data=SENSORS), name="Sensors"), ]), ])
activeBase / activeOverlays are TWO-WAY:
@callback(Output("out", "children"), Input("lc", "activeBase"), Input("lc", "activeOverlays")) def show(b, o): return f"{b} | {o}"
@callback(Output("lc", "activeBase"), Input("dark-btn", "n_clicks"), prevent_initial_call=True) def force_dark(_): return "Dark"'''
component = dmc.Stack( [ header( "Layers Control", "dl2.LayersControl renders a Leaflet control with N base layers (radio) and M " "overlays (checkbox). BaseLayer/Overlay capture their child layer via a React-context " "map proxy and register it with the control. activeBase + activeOverlays are two-way.", badge="dl2.LayersControl", ), dmc.Grid( [ dmc.GridCol( dmc.Paper( dl2.Map( id="lc-map", center=MINNEAPOLIS.center, zoom=12, style={"height": "60vh"}, children=[ dl2.LayersControl( id="lc", position="topright", children=[ dl2.BaseLayer( dl2.TileLayer( url=CARTO_LIGHT, attribution=ATTR ), name="Light", checked=True, ), dl2.BaseLayer( dl2.TileLayer( url=CARTO_DARK, attribution=ATTR ), name="Dark", ), dl2.BaseLayer( dl2.TileLayer(url=OSM), name="OSM" ), dl2.Overlay( dl2.Polygon( # (north_km, east_km) offsets positions=MINNEAPOLIS.ring([ (3.3, -4.9), (4.5, 2.9), (-1.1, 4.9), (-2.2, -2.9), ]), color="#2f9e44", fillOpacity=0.25, children=dl2.Tooltip( children="harbor zone" ), ), name="Harbor zone", checked=True, ), dl2.Overlay( dl2.Circle( center=MINNEAPOLIS.at(-2.2, 1.0), radius=1500, color="#e8590c", fillOpacity=0.2, children=dl2.Tooltip( children="buoy radius" ), ), name="Buoy radius", ), dl2.Overlay( dl2.GeoJSON( data=SENSORS, style={"color": "#9c36b5", "weight": 2}, ), name="Sensors", ), ], ), ], ), shadow="sm", radius="md", withBorder=True, style={"overflow": "hidden"}, ), span=8, ), dmc.GridCol( dmc.Stack( [ info_panel( "Active state (map → Python)", dmc.Code( id="lc-out", block=True, style={ "minHeight": "84px", "whiteSpace": "pre-wrap", }, ), ), info_panel( "Python → control", dmc.Stack( [ dmc.Text( "Pick a base from a Python callback:", size="sm", c="dimmed", ), dmc.Group( [ dmc.Button( "Light", id="lc-btn-light", size="xs", variant="light", color="gray", ), dmc.Button( "Dark", id="lc-btn-dark", size="xs", variant="light", color="dark", ), dmc.Button( "OSM", id="lc-btn-osm", size="xs", variant="light", color="blue", ), ] ), dmc.Text( "Toggle an overlay from Python:", size="sm", c="dimmed", mt="sm", ), dmc.Group( [ dmc.Button( "Toggle Buoy radius", id="lc-btn-buoy", size="xs", variant="light", color="orange", ), dmc.Button( "Toggle Sensors", id="lc-btn-sensors", size="xs", variant="light", color="grape", ), ] ), ], gap="xs", ), ), ], gap="md", ), span=4, ), ] ), code_panel("dl2.LayersControl pattern", CODE), ], gap="md", )
Two-way: map -> Python (state readout) ----------------------------------------
@callback( Output("lc-out", "children"), Input("lc", "activeBase"), Input("lc", "activeOverlays"), ) def show_active(base, overlays): return f"activeBase:\n {base}\nactiveOverlays:\n {overlays}"
Two-way: Python -> map (button-driven base switch) ----------------------------
@callback( Output("lc", "activeBase", allow_duplicate=True), Input("lc-btn-light", "n_clicks"), prevent_initial_call=True, ) def to_light(_): return "Light"
@callback( Output("lc", "activeBase", allow_duplicate=True), Input("lc-btn-dark", "n_clicks"), prevent_initial_call=True, ) def to_dark(_): return "Dark"
@callback( Output("lc", "activeBase", allow_duplicate=True), Input("lc-btn-osm", "n_clicks"), prevent_initial_call=True, ) def to_osm(_): return "OSM"
def _toggle(current, name): current = list(current or []) return [o for o in current if o != name] if name in current else current + [name]
@callback( Output("lc", "activeOverlays", allow_duplicate=True), Input("lc-btn-buoy", "n_clicks"), State("lc", "activeOverlays"), prevent_initial_call=True, ) def toggle_buoy(_, current): return _toggle(current, "Buoy radius")
@callback( Output("lc", "activeOverlays", allow_duplicate=True), Input("lc-btn-sensors", "n_clicks"), State("lc", "activeOverlays"), prevent_initial_call=True, ) def toggle_sensors(_, current): return _toggle(current, "Sensors") ```
:defaultExpanded: false :withExpandedButton: true
---
*Source: /layers-control*
Note for AI agents: This is the static, prerendered view of an interactive Dash application served because we detected a non-JS user agent. Full prose docs:
- /layers-control/llms.txt — LLM-friendly documentation
- /sitemap.xml
- /robots.txt