jupyter-widgets / ipyleaflet

A Jupyter - Leaflet.js bridge
https://ipyleaflet.readthedocs.io
MIT License
1.47k stars 361 forks source link

Change layer group visibility programmatically #1118

Open giswqs opened 1 year ago

giswqs commented 1 year ago

The layer group can be toggled on/off manually using the LayersControl. Is it possible to change the layer group visibility programmatically? layer_group.visible=False does not work.

from ipyleaflet import (
    Map, basemaps, basemap_to_tiles,
    Circle, Marker, Rectangle, LayerGroup, LayersControl
)

toner = basemap_to_tiles(basemaps.Stamen.Toner)

m = Map(layers=(toner, ), center=(50, 354), zoom=5)

# Create some layers
marker = Marker(location=(50, 354))
circle = Circle(location=(50, 370), radius=50000, color="yellow", fill_color="yellow")
rectangle = Rectangle(bounds=((54, 354), (55, 360)), color="orange", fill_color="orange")

# Create layer group
layer_group = LayerGroup(layers=(marker, circle), name='Layer Group')

m.add_layer(layer_group)

layer_group.add_layer(rectangle)

control = LayersControl(position='topright')
m.add_control(control)
m
Nirdual commented 1 year ago

Hello, I was trying to set a layer group not visible by default, which is easy to do with Folium but unfortunately does not seem to be implemented yet in ipyleaflet. Anyway, i managed to do so by creating a custom layer control, hopefully it can solve your problem as well.

# Add WidgetControl in import
from ipyleaflet import (
    Map, basemaps, basemap_to_tiles,
    Circle, Marker, Rectangle, LayerGroup, LayersControl, WidgetControl
)
# Add some ipywidgets
from ipywidgets import Checkbox, Layout, ToggleButton

toner = basemap_to_tiles(basemaps.Stamen.Toner)
m = Map(layers=(toner, ), center=(50, 354), zoom=5)

# Create some layers
marker = Marker(location=(50, 354))
circle = Circle(location=(50, 370), radius=50000, color="yellow", fill_color="yellow")`
rectangle = Rectangle(bounds=((54, 354), (55, 360)), color="orange", fill_color="orange")

# Create layer group
layer_group = LayerGroup(layers=(marker, circle), name='Layer Group')
layer_group.add_layer(rectangle)

# the observed function
def toggle(change):
    if change.new == False:
        m.remove(layer_group)
    else:
        m.add_layer(layer_group)

# Add layer_group to map if your checkbox is set to True by default
# do not add to map otherwise, or it will not be coherent.
m.add_layer(layer_group)

# Our custom layer control (a checkbox here)
check = Checkbox(value = True,
                 description = 'test', 
                 indent = False,
                 layout = Layout(width = '100%', 
                                 margin = '0px 5px 0px 5px'))

# We observe the checkbox's value, whenever the value changes, it triggers the 'toggle' function
check.observe(toggle, 'value')

# We add the checkbox to the map
m.add(WidgetControl(widget = check, position = 'topright'))

# I programmatically set the tickbox to False (You can still interact manually)
# This is where you want to do your more specific stuff.
check.value = False

# The layer control is made redundant by the tickbox so i commented it.
# control = LayersControl(position='topright')
# m.add_control(control)

m
giswqs commented 1 year ago

I though about this workaround that removes and adds layer program programmatically. It is a bit slow when the layer group has many features. It might also change the layer order if some layers are added after the layer group. I would prefer a native solution that can set layer group visibility like other layers.

12rambau commented 1 year ago

@giswqs, can what I suggested for MarkerCluster herebe adapted for your need ?

giswqs commented 1 year ago

@12rambau Yes. It looks good to me. Thank you