Replace param by pn.widget in parametrized class app

Hi,

I just started playing with Parameterized class and I tried several approaches to adapt this code:

class PanelFoliumMap(param.Parameterized):
    points_count = param.Integer(20, bounds=(10,100))
        
    def __init__(self, **params):
        super().__init__(**params)
        self.map = get_map()
        self.html_pane = pn.pane.HTML(sizing_mode="stretch_width", min_height=600)    
        self.view = pn.Column(
            self.param.points_count,
            self.html_pane,
            sizing_mode="stretch_width", min_height=600,
        )
        self._update_map()

    @param.depends("points_count", watch=True)
    def _update_map(self):
        self.map = get_map()
        df_aqi = get_df_aqi(points_count=self.points_count)
        add_aqi_circles(self.map, df_aqi)
        self.html_pane.object = self.map

        
app = PanelFoliumMap()
app.view.embed()

from the reference example: Folium — Panel 0.11.3 documentation

My goal is to replace the widget param.Integer(20, bounds=(10,100)) with a IntInput panel widget. Is there an easy solution to do that?

I would like to initialize the widget so it shows a default value of 15 and incremental step of 10
int_input = pn.widgets.IntInput(name='IntInput', value=50, step=10, start=10, end=100)

Thank you!

Loïc

Absolutely, to map parameters to custom widgets use the widgets argument to the Param pane:

pn.Param(self.param.points_count, widgets={'points_count': pn.widgets.IntInput})

You can also provide a step to the parameter definition:

    points_count = param.Integer(default=15, bounds=(10,100), step=10)

Thank you Philips,
I tried defining points_count as param.Integer(default=15, bounds=(10,100), step=10)
but it doesnt change the slider in my app produced.

I only have three positions:
Points count =10,
Points count = 55,
and Point count = 100

Do you know how I could get more parameter values (e.g. corresponding to my step)?

The full reproductible code can be found below:

import folium as fm
import pandas as pd
import param
import panel as pn
import random
pn.extension()

def get_map(lat=20.5936832, long=78.962883, zoom_start=5):
    return fm.Map(location=[lat,long], zoom_start=zoom_start)



def get_df_aqi(lat_bounds=(10,30), long_bounds=(70,90), points_count=40):
    aqi = {
        "Latitude": [random.uniform(*lat_bounds) for _ in range(0,points_count)],
        "Longitude": [random.uniform(*long_bounds) for _ in range(0,points_count)],
        "AQI": [random.uniform(0,500) for _ in range(0,points_count)],
    }
    return pd.DataFrame(aqi)


def add_aqi_circles(map, df_aqi):
    green_p1  = fm.map.FeatureGroup()
    yellow_p1 = fm.map.FeatureGroup()
    orange_p1 = fm.map.FeatureGroup()
    red_p1    = fm.map.FeatureGroup()
    purple_p1 = fm.map.FeatureGroup()
    maroon_p1 = fm.map.FeatureGroup()

    for _, row in df_aqi.iterrows():
        if row.AQI<50:
            feature_group = green_p1
            fill_color = "green"
        elif row.AQI < 100:
            feature_group = yellow_p1
            fill_color = "yellow"
        elif row.AQI < 150:
            feature_group = orange_p1
            fill_color = "orange"
        elif row.AQI < 200:
            feature_group = red_p1
            fill_color = "red"
        elif row.AQI < 300:
            feature_group = purple_p1
            fill_color='purple'
        else:
            feature_group = maroon_p1
            fill_color = "maroon"

        feature_group.add_child(
            fm.CircleMarker(
                [row.Latitude, row.Longitude],
                radius=10, 
                fill=True,
                fill_color=fill_color,
                fill_opacity=0.7
            )
        )

    map.add_child(green_p1)
    map.add_child(yellow_p1)
    map.add_child(orange_p1)
    map.add_child(red_p1)
    map.add_child(purple_p1)




class PanelFoliumMap(param.Parameterized):
    points_count = param.Integer(default=15, bounds=(10,100), step=10)
        
    def __init__(self, **params):
        super().__init__(**params)
        self.map = get_map()
        self.html_pane = pn.pane.HTML(sizing_mode="stretch_width", min_height=600)    
        self.view = pn.Column(
            self.param.points_count,
            self.html_pane,
            sizing_mode="stretch_width", min_height=600,
        )
        self._update_map()

    @param.depends("points_count", watch=True)
    def _update_map(self):
        self.map = get_map()
        df_aqi = get_df_aqi(points_count=self.points_count)
        add_aqi_circles(self.map, df_aqi)
        self.html_pane.object = self.map

        
app = PanelFoliumMap()
app.view.embed()

I am using jupyter lab

I tried your example and encountered the same behavior with the slider only having stops at 10, 55, 100.
I tried accessing the int slider directly in another cell via slicing with app.view[0], and the slider works correctly with stops from 10-100 in increments of 10. The fact that the same item behaves differently does make me think that there is some sort of bug or issue.
However, I also noticed that updating the secondary slider, or even directly setting the value of app.point_counts did not update the map.

I made some slight changes, and everything seems to behave properly now. Here is the updated version:

class PanelFoliumMap(param.Parameterized):
    points_count = param.Integer(default=15, bounds=(10,100), step=10)

    def __init__(self, **params):
        super().__init__(**params)

    @param.depends("points_count")
    def _update_map(self):
        m = get_map()
        df_aqi = get_df_aqi(points_count=self.points_count)
        add_aqi_circles(m, df_aqi)
        html_pane = pn.pane.HTML(sizing_mode="stretch_width", min_height=600)    
        html_pane.object = m
        return html_pane

    def panel(self):
        return pn.panel(
            pn.Column(
                self.param.points_count,
                self._update_map,
                sizing_mode="stretch_width", min_height=600,
            )
        )

app = PanelFoliumMap()
app.panel()

Now, the slider seems to work correctly and even setting app.points_count directly in another cell or interacting with the value in another cell via pn.Param(app.param.points_count) causes the map to update properly.

I do still wonder if there is a minor issue you are encountering with the slider behavior in your example that may be worth reporting, but hopefully this helps.

1 Like

Thank you very much! You fix it! I will open an issue about it and propose to edit the example notebook with your solution!

Loic

1 Like