LayerGroup & FeatureGroup

Bundle N layers so they can be added, removed, toggled, or measured as one — wraps Leaflet 2's LayerGroup / FeatureGroup.

LayerGroup & FeatureGroup

Bundle N layers so they can be added, removed, toggled, or measured as one — wraps Leaflet 2's LayerGroup / FeatureGroup.

---

.. llms_copy::LayerGroup & FeatureGroup

.. toc::

Overview

dl2.LayerGroup and dl2.FeatureGroup mirror Leaflet's container primitives:

| Component | When to use it | |-------------|----------------| | LayerGroup | Bundle any layers so a single addTo / remove shows or hides the whole set. Pair with dl2.Overlay inside a LayersControl to toggle the entire group as one entry. | | FeatureGroup | Like LayerGroup, but extends leaflet.FeatureGroup — also emits a combined geojson (vector children), a single n_clicks no matter which child was clicked, and an n_layers counter that bumps on add/remove. Pair with EditControl when you want to ship the user's drawings out of the map as one piece. |

Both components also accept any layer as a child via the same React context that <Map> uses — children attach to the group via a proxy map instead of the real map directly.

Live demo

A switch toggles a LayerGroup of three markers on/off. Below it, a FeatureGroup aggregates four shapes and reports its combined geojson and the bumping n_clicks counter.

.. exec::docs.layer-group.example :code: false

Source

```python

File: docs/layer-group/example.py

""" LayerGroup & FeatureGroup — limited working example.

Top map: a LayerGroup containing three markers — toggle them all on/off with one switch (the group itself is conditionally rendered, so all children come and go together).

Bottom map: a FeatureGroup wrapping three vector layers + a marker. Click any of them and FeatureGroup's n_clicks bumps; the readout shows the combined GeoJSON it emits. """

import json

import dash_leaflet2 as dl2 import dash_mantine_components as dmc from dash import Input, Output, callback, html from dl2_tiles import ESRI_CANVAS, register_theme_swap from dl2_locations import PHILADELPHIA from dl2_shared import code_panel, header, info_panel

Basemap pair for this page — dl2_tiles owns the light/dark wiring.

TILES = ESRI_CANVAS

CODE_LG = """dl2.Map(children=[ dl2.TileLayer(), dl2.LayerGroup(children=[ dl2.Marker(position=PHILADELPHIA.center), dl2.Marker(position=PHILADELPHIA.at(2.2, 2.9)), dl2.Marker(position=PHILADELPHIA.at(-2.2, -2.9)), ]) if show_markers else None, ])"""

CODE_FG = """dl2.Map(children=[ dl2.TileLayer(), dl2.FeatureGroup(id="fg", children=[ dl2.Polygon(positions=[...]), dl2.Polyline(positions=[...]), dl2.Circle(center=[...], radius=400), dl2.Marker(position=[...]), ]), ])

@callback(Output("fg-readout","children"), Input("fg","geojson")) def show(geojson): ..."""

component = dmc.Stack( [ header( "LayerGroup & FeatureGroup", "LayerGroup bundles N layers so one switch hides them all. " "FeatureGroup adds aggregate event + GeoJSON output — click any child and " "n_clicks bumps; add/remove children and n_layers bumps too.", badge="dl2.LayerGroup / FeatureGroup", ),

dmc.Title("1. LayerGroup", order=3, mt="md"), dmc.Grid( [ dmc.GridCol( dmc.Paper( dl2.Map( id="lg-map", center=PHILADELPHIA.center, zoom=12, style={"height": "45vh"}, children=[ dl2.TileLayer(id="lg-tile", **TILES.kwargs("light")), html.Div(id="lg-container"), ], ), shadow="sm", radius="md", withBorder=True, style={"overflow": "hidden", "height": "45vh"}, ), span=8, ), dmc.GridCol( info_panel( "Toggle the whole group", dmc.Switch( id="lg-toggle", checked=True, label="Show three markers (all in one LayerGroup)", ), ), span=4, ), ], gutter="md", ), code_panel("LayerGroup", CODE_LG),

dmc.Title("2. FeatureGroup", order=3, mt="md"), dmc.Grid( [ dmc.GridCol( dmc.Paper( dl2.Map( id="fg-map", center=PHILADELPHIA.center, zoom=12, style={"height": "45vh"}, children=[ dl2.TileLayer(), dl2.FeatureGroup( id="fg", children=[ dl2.Polygon( # (north_km, east_km) from the # city centre — the shape keeps its # real-world size at any latitude. positions=PHILADELPHIA.ring([ (2.2, -4.9), (4.5, 1.0), (2.2, 5.9), (0.0, 0.0), ]), color="#228be6", fillOpacity=0.35, ), dl2.Polyline( positions=PHILADELPHIA.ring([ (-3.3, -4.9), (-3.3, 1.0), (-3.3, 6.9), ]), color="#fa5252", weight=3, ), dl2.Circle( center=PHILADELPHIA.at(1.1, 2.9), radius=600, color="#40c057", fillOpacity=0.25, ), dl2.Marker(position=PHILADELPHIA.at(-1.1, -1.0)), ], ), ], ), shadow="sm", radius="md", withBorder=True, style={"overflow": "hidden", "height": "45vh"}, ), span=8, ), dmc.GridCol( dmc.Stack( [ info_panel( "Group n_clicks", dmc.Badge(id="fg-clicks", color="blue", variant="light", children="0"), ), info_panel( "Combined geojson (vector children)", dmc.Code( id="fg-geojson-readout", block=True, children="(click a shape)", style={"maxHeight": "20vh", "overflow": "auto"}, ), ), ], gap="md", ), span=4, ), ], gutter="md", ), code_panel("FeatureGroup", CODE_FG), ], gap="md", )

@callback(Output("lg-container", "children"), Input("lg-toggle", "checked")) def render_group(show): if not show: return [] return dl2.LayerGroup( children=[ dl2.Marker(position=PHILADELPHIA.center), dl2.Marker(position=PHILADELPHIA.at(2.2, 2.9)), dl2.Marker(position=PHILADELPHIA.at(-2.2, -2.9)), ] )

@callback(Output("fg-clicks", "children"), Input("fg", "n_clicks")) def show_clicks(n): return str(n or 0)

@callback(Output("fg-geojson-readout", "children"), Input("fg", "geojson")) def show_geojson(gj): if not gj: return "(no children yet)" return json.dumps(gj, indent=2)[:2000]

Light/dark basemap, driven off the color-scheme store.

register_theme_swap("lg-tile", TILES) ```

:defaultExpanded: false :withExpandedButton: true

---

*Source: /layer-group*

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: