Search code examples
pythonpandaswidgetpanelgeopandas

Create DataFrame from filepath interactive after choosing from Drop Down menu with Panel Select Widget


I would like to have a Select Widget with Panel where I can select ids from a dictionary. The IDs are the key and the values are file paths to geojson files storing tabular data. After i interactively choose an id the data frame should be loaded. My Problem is that it worked only the first time i pick a value. If I try to select another one, the data frame will not update. Is it possible that pandas updates the read_file function after i chosse a new id?

# Import modules
from glob import glob
import geopandas as gpd
import panel as pn
# Initializes the pyviz notebook extension to allow plotting with bokeh and enable comms
pn.extension()

# Store all filepaths within a list
filespaths = ['C://Users/USER/Desktop/Master_Irrigation/03_GIS/ground_trouth/sentinel_ismn_data\\1018_15.15537_48.1521.geojson',
 'C://Users/USER/Desktop/Master_Irrigation/03_GIS/ground_trouth/sentinel_ismn_data\\1026_15.15472_48.15053.geojson']

# Create Station id linked with filepath
ids = [x.split('\\')[-1].split('_')[0] for x in filespaths]
#Create dict with Ids as key and filepaths as value
files = dict(zip(ids,filespaths ))

# Widget to select station id, widget.value stores filepath
id_selector = pn.widgets.Select(name='ISMN Station ID', options=files)

# Load file into GeoPandas DataFrame
gdf = gpd.read_file(id_selector.value)
id_selector

Edit: I know that one way would be to load all data into one DataFrame

gpd = gpd.tools.util.pd.concat(map(gpd.read_file, paths), ignore_index=True)

But with several hundreds of files with thousands of rows this would lead to a performance issue.


Solution

    • fundamentally you need to consider your paradigm. You are using an event based paradigm so need a callback to respond to your event (file selected from drop down)
    • I've not used panel and could not get callbacks to work. Hence switched to ipwidgets given your runtime environment is jupyter (based on comment)
    • with this respond to event and load gdf with selected file. Have synthesized a MWE by using glob() to find geojson files
    import ipywidgets as widgets
    from pathlib import Path
    from glob import glob
    import geopandas as gpd
    
    # Store all filepaths within a list
    # filespaths = ['C://Users/USER/Desktop/Master_Irrigation/03_GIS/ground_trouth/sentinel_ismn_data\\1018_15.15537_48.1521.geojson',
    #  'C://Users/USER/Desktop/Master_Irrigation/03_GIS/ground_trouth/sentinel_ismn_data\\1026_15.15472_48.15053.geojson']
    # well my filesystem is different,  get some geojson files
    filespaths = list(set(Path.cwd().glob("**/*.geojson")) -  set(Path.cwd().glob(".ipynb_checkpoints/*.geojson")))
    
    # Create Station id linked with filepath
    # ids = [x.split('\\')[-1].split('_')[0] for x in filespaths]
    # my filenames don't contain any underscores ...
    ids = list(range(len(filespaths)))
    #Create dict with Ids as key and filepaths as value
    files = dict(zip(ids,filespaths ))
    
    gdf = None
    
    # useful for debug....
    out = widgets.Output(layout={"border": "1px solid black"})
    out.append_stdout("Output appended with append_stdout\n")
    
    # use ipwidgets instead
    id_selector = widgets.Dropdown(
        options=[(k,v) for k,v in files.items()],
        description='ISMN Station ID:',
        disabled=False,
    )
    
    # the key a callback on value change....
    @out.capture()
    def changed(v):
        global gdf
        if v['type'] == 'change':
            print(v["new"])
            gdf = gpd.read_file(str(v["new"]))
    id_selector.observe(changed, names="value")
    
    widgets.VBox([id_selector, out])