Dash-leaflet not passing data to geojson layer in callback

Hi,

I’m quite new to plotly dash and dash-leaflet. I’m trying to figure out how to pass data to an existing dl.GeoJSON component via callback.

I have my map (in map.py file)

import dash_leaflet as dl

def map()
    dl_map = dl.Map(
        id="map", 
        center=[43,12], zoom=5, 
        style={'height': '100%'},
        children=[
            dl.TileLayer(),
            dl.GeoJSON( id='esiti-layer'),
            dl.FullScreenControl(),
            dl.ScaleControl(position="bottomleft")
        ]
    )
    return dl_map

The map shows in the layout defined in my app.py file. I defined here a callback that launches a request to an api and returns a geodataframe.

...
@app.callback(
    Output("esiti-layer","data"),
    State("date-from","value"), #datepicker
    State("date-to","value") #datepicker
)
def update_dashboard(date_from, date_to):
    gdf = fetch_geodata(date_from, date_to) #this function fetches a geojson from a third party api and returns a geopandas geodataframe
    gdf.set_crs(epsg="4326", inplace=True)
    print(gdf.head(10) #just to check...
    layer_data = gdf.__geo_interface__

...

And nothing happens to my map when the callback fires! What’s wrong? Thanks for your help!

From your callback it looks like you don’t return anything? If that’s the case, that is a problem :slight_smile:

In any case, it would help if you could post a (runnable) MWE demonstrating the issue.

Hi Emil!
No, I just forgot to add the return line in this post, sorry… :sweat_smile:

The callback is returning “layer_data”

Hi, here a MWE reproducing the problem.
Can you help me to understand what’s wrong?

app.py file:

from dash import Dash, html, dcc, callback, Output, Input
import plotly.express as px
import geopandas as gpd
from api import fetch_geodata
from map import map

app = Dash()
app.layout = html.Div(
    children=[
        map(),
        html.Button('Load data from REST Api', id='my-btn', n_clicks=0),
    ]
)

@app.callback(
    Output('my-layer', 'data'),
    Input('my-btn', 'n_clicks'), 
    PreventInitialCallback=True
)
def load_data_from_api(n_clicks):
    if n_clicks == 0:
        return {}
    else:
        print('Loading data from REST API')
        gdf = fetch_geodata()
        gdf.set_crs(epsg='4326', inplace=True)
        print(gdf.crs)
        print(gdf.head())
        return gdf.__geo_interface__
        
if __name__ == '__main__':
    app.run(debug=True)

map.py file:

import dash_leaflet as dl

def map():

    dl_map = dl.Map(
        center=[43,13], 
        zoom=6, 
        style={'height': '90vh'},
        children=[
            dl.TileLayer(), 
            dl.GeoJSON(id='my-layer', data={})
        ]
    )

    return dl_map

api.py file:

import requests
import geopandas as gpd

SERVICE_URL = "https://services7.arcgis.com/8tIzt6yXOZrB60gX/ArcGIS/rest/services/NetMed/FeatureServer" 
WHERE = "COUNTRY_N = 'ITALY' AND DISEASE_DESC='BT' AND DATE_OF_START_OF_THE_EVENT >= DATE '2025-01-01'"

def fetch_geodata():
    query_url = f"{SERVICE_URL}/0/query/"
    params = {
        "where": WHERE,
        "outFields": "*",
        "returnGeometry": "true",
        "f": "geojson"
    }
    response = requests.get(query_url, params=params)
    if response.status_code == 200:
        geojson_data = response.json()
        features = geojson_data.get("features", [])
        if not features:
            print("No features found in the response.")
            return None

        records = []
        geometries = []
        for feature in features:
            attributes = feature.get("properties", {})
            geometry = feature.get("geometry", None)
            records.append({**attributes, "geometry": geometry})

        gdf = gpd.GeoDataFrame.from_features(features)
        return gdf
    else:
        print(f"Request error: {response.status_code}")
        return None

I’m still stuck, but it seems the data is correctly passed to Dash Leaflet—if I deliberately return invalid data from the callback, Dash throws an error.

If you run the MWE, no errors are reported but the geojson layer is not populated with the data (or I have to force the re-rendering in some way?)

What else can I check?