heroku app has been offline a while. google cache no help. is there anywhere else to read the documentation?
Yes, that is due to Heroku removing their free tier. You can use the render mirror instead,
Sorry for the questions. Trying to switch from Dash/Graph/Mapbox because of Dash Leaflet’s full screen mode and measuring tools.
- is Datashader possible? Looks like it might be with Image Overlay. The URL requirement might be tricky to implement for each client.
- I have millions of points that can’t be clustered. What I do with dcc.Graph(mapbox) is get the zoom from relayout events, once it is low enough, I take the bounds from _mapbox_derived then only plot the points within those bounds. Does that workflow make sense with Dash Leaflet?
The answer to my question #2 above is ‘yes’:
Had a callback output to a dl.Map ‘viewport’ component and another callback Input dl.Map ‘bounds’. Unfortunately the bounds were still the map bounds from prior to the viewport event. Zoom however was accurate so I figured it was possible to get the data. Instead of ‘viewport’ I tried output to ‘center’ and ‘zoom’ changing order of the outputs with no change. finally, toggling “useFlyTo = True” corrected the issue.
With this minor work around (I actually prefer useFlyTo), this component has a major advantage over the built in dcc.Graph(mapbox) in that the bounds are available on init load of the map. Saving the guess work from making a radius big enough for the user’s screen around the known center of the map
Running into the same situation with Heroku. Tried Google Cloud - works but needs to replace the “assign()” function with the manual approach “Namespace()” in the dash_extensions.javascript module since Google Cloud wants the files to be read-only.
Hi Emil,
first of all thanks for the contributions to the Dash-Leaflet!
I am trying to built an app with a click and hover feature with a GeoJSON, basically as your example with the US states and capitals. The only difference is, that I am creating the GeoJSON data in the app first and then want to click on the markers for information purposes. I have a callback for getting the data, then store it via dcc.store, then have them rendered onto a map as a dl.GeoJSON(). Until that part everything works fine. But when I hover or click on a marker, nothing happens.
And another short question. Can I load a GeoJSON directly from my disc into the app?
=> dl.GeoJSON(data=“C:/…/XYZ.geojson”
does not seem to work for me.
Thanks!
Thanks!
Yes, that should be possible. However, without seeing the code, I can’t say what is wrong
Yes and no. You can load any GeoJSON which is served by a webserver. The Flask server powering the Dash application itself serves files placed in the assets folder, so if you put the GeoJSON there, it should just work. If you want to serve data from other folders, you’ll need to add routes accordingly - or use a separate tool for serving the files, e.g. WhiteNoise.
Here is my code:
import dash
import dash_leaflet as dl
import dash_bootstrap_components as dbc
import dash_leaflet.express as dlx
from dash import dcc, html, Output, Input, dash_table
from dash.dependencies import Input, Output, State
from dash_extensions.javascript import arrow_function
import requests
import json
app = Dash()
app.layout = html.Div(children=[
dbc.Button(‘Submit’, id=‘SubmitButton’, n_clicks=0),
dl.Map(center=[39, -98], zoom=4, children=[
dl.TileLayer(),
dl.GeoJSON(id=“dataresponse”),
], style={‘width’: ‘100%’, ‘height’: ‘50vh’, ‘margin’: “auto”, “display”: “block”}, id=“map”),
dcc.Store(id=“datasave”),
html.Div(id=“capital”)
])
@app.callback(Output(‘datasave’, ‘data’), Input(‘SubmitButton’, ‘n_clicks’))
def update_output(n_clicks):
if n_clicks > 0:
response = requests.get(“https://d2ad6b4ur7yvpq.cloudfront.net/naturalearth-3.3.0/ne_50m_geography_regions_elevation_points.geojson”)
response_id = response.text
response_id_json = json.loads(response_id)
return response_id_json
@app.callback(Output(‘dataresponse’, ‘children’), Input(‘datasave’, ‘data’))
def update_map(value):
return dl.GeoJSON(data=value)
@app.callback(Output(“capital”, “children”), [Input(“dataresponse”, “click_feature”)])
def marker_click(feature):
if feature is not None:
return f"You clicked {feature[‘properties’][‘name’]}"
if name == ‘main’:
app.run_server()
I stumbled into another “problem”. When I call back the GeoJSON, I cannot seem to change the marker. I’ve put in the options=dict(pointToLayer…) into the return and also into the app layout dl.GeoJSON, but only the “normal” markers will apear after the callback. Maybe this is the same problem. Thanks.
I solved the above problems myself. I needed to add the id=(“dataresponse”) into the dl.GeoJSON return.
But I have another problem. I want to make a table with dash_table out of the returns. I get the table with the properties, but they are all in one column and the data is returned in multiple rows (each character in one row). My output looks like this:
{‘angebot_id’: 3539754318,
‘startdate’: ‘2022-11-08’,
‘laufzeitw_alle’: 1.1}
I’ve tried to do json_normalize the data, but then nothing gets returned (even not, when I put them “to_dict” afterwards. Anyone has the same problem? The callback looks something like this:
@app.callback(Output(“table”, “data”), [Input(“dataresponse”, “click_feature”)])
def marker_click(feature):
if feature is not None:
f = f"{feature[‘properties’]}"
data = pd.json_normalize(f)
data2 = data.to_dict(orient = ‘records’)
return data2
With dash_table.DataTable(id=“table”) as the table.
Thanks!
the code below is an minimal example from the docs where I’ve simply added a dcc.Dropdown to the the info Div that is used for hover info. I can type “NYC” and click return on the keyboard and make selection I however can not click the dropdown items.
I can add the following to a clientside callback that fires when the id=‘info’ div is placed:
leaflet: function(id) {
var container = L.DomUtil.get('leaflet-map');
var info = L.DomUtil.get('info');
container && info && L.DomEvent.disableScrollPropagation(info);
throw window.dash_clientside.PreventUpdate;
},
this makes SCROLLING the dropdown work correctly.
however “disableClickPropagation” does nothing.
I’ve played around with ‘pointer-events’ css property to no avail. Are there any other hints to get dcc.Dropdown to work in the info div?
import dash_leaflet as dl
import dash_leaflet.express as dlx
from dash import Dash, html, dcc, Output, Input
from dash_extensions.javascript import arrow_function, assign
def get_info(feature=None):
header = [html.H4("US Population Density")]
if not feature:
return header + [
html.P("Hoover over a state"),
dcc.Dropdown(
[
{
"label": html.Span(['Montreal'], style={'color': 'Gold', 'font-size': 20}),
"value": "Montreal",
},
{
"label": html.Span(['NYC'], style={'color': 'MediumTurqoise', 'font-size': 20}),
"value": "NYC",
},
{
"label": html.Span(['London'], style={'color': 'LightGreen', 'font-size': 20}),
"value": "London",
},
], value='Montreal'
),
]
return header + [html.B(feature["properties"]["name"]), html.Br(),
"{:.3f} people / mi".format(feature["properties"]["density"]), html.Sup("2")]
classes = [0, 10, 20, 50, 100, 200, 500, 1000]
colorscale = ['#FFEDA0', '#FED976', '#FEB24C', '#FD8D3C', '#FC4E2A', '#E31A1C', '#BD0026', '#800026']
style = dict(weight=2, opacity=1, color='white', dashArray='3', fillOpacity=0.7)
# Create colorbar.
ctg = ["{}+".format(cls, classes[i + 1]) for i, cls in enumerate(classes[:-1])] + ["{}+".format(classes[-1])]
colorbar = dlx.categorical_colorbar(categories=ctg, colorscale=colorscale, width=300, height=30, position="bottomleft")
# Geojson rendering logic, must be JavaScript as it is executed in clientside.
style_handle = assign("""function(feature, context){
const {classes, colorscale, style, colorProp} = context.props.hideout; // get props from hideout
const value = feature.properties[colorProp]; // get value the determines the color
for (let i = 0; i < classes.length; ++i) {
if (value > classes[i]) {
style.fillColor = colorscale[i]; // set the fill color according to the class
}
}
return style;
}""")
# Create geojson.
geojson = dl.GeoJSON(url="/assets/us-states.json", # url to geojson file
options=dict(style=style_handle), # how to style each polygon
zoomToBounds=True, # when true, zooms to bounds when data changes (e.g. on load)
zoomToBoundsOnClick=True, # when true, zooms to bounds of feature (e.g. polygon) on click
hoverStyle=arrow_function(dict(weight=5, color='#666', dashArray='')), # style applied on hover
hideout=dict(colorscale=colorscale, classes=classes, style=style, colorProp="density"),
id="geojson")
# Create info control.
info = html.Div(children=get_info(), id="info", className="info",
style={"position": "absolute", "top": "10px", "right": "10px", "z-index": "1000"})
# Create app.
app = Dash(prevent_initial_callbacks=True)
app.layout = html.Div([dl.Map(children=[dl.TileLayer(), geojson, colorbar, info])],
style={'width': '100%', 'height': '50vh', 'margin': "auto", "display": "block"}, id="map")
@app.callback(Output("info", "children"), [Input("geojson", "hover_feature")])
def info_hover(feature):
return get_info(feature)
if __name__ == '__main__':
app.run_server()
interestingly when using filter I can’t pass the state of a hideout (or div) to a clientside function, null coalesce it and pass it directly to a hideout as output. I have to for loop the array elements. purpose is to click multiple polygons and know which ones have been clicked
does not work:
var h = some_hideout ?? [];
does work:
h = [];
for (var i = 0, len = some_hideout.length; i < len; i++) {
h.push(some_hideout[i])
}
With the null coalesce version, the map would have to be panned/zoomed and would then show the updated hideout elements
serverside callback works fine without the for loop
I have a Raster file (Geotiff) and I want to show that on the dash leaflet map, and I used the below code and unfortunately it does not work for me.
import dash
import dash_leaflet as dl
app = dash.Dash()
def Raster_N():
raster = dl.GeoTIFFOverlay(id="GEOTIFF_ID", interactive=True, url="/assets/Raster_main.tiff", band=1, opacity=1)
return raster
Raster = Raster_N()
app.layout = dl.Map([dl.TileLayer(), Raster], style={'width': '1000px', 'height': '500px'})
if __name__ == '__main__':
app.run_server(debug = True)
Can you please help me out with this issue? Also, I want to know how I can change the colour of this Tiff file, I mean is there any way that I can change the min, max and colour scale of this Tiff file or not?
Thanks alot
Here is a small example, including how to change color scale,
How can I resize the popup for dl.GeoJSON? I can generate markers from a Pandas dataframe using
for row in df_filtered.itertuples():
markers.append(
dict(
lat=row.Latitude,
lon=row.Longitude,
popup=row.popup_html # this is an HTML string
)
)
# Generate geojson with a marker for each listing
geojson = dlx.dicts_to_geojson([{**m} for m in markers])
But the popups are a little too big on mobile devices. I would like to change the maxHeight and maxWidth of the popup, but I can’t find any documentation on how to do that like I could with dl.Popup. Am I supposed to do this within the popup
option (i.e, in HTML)?
Hi Emil!
I’ve been using dash leaflet for a couple of days and I wanted to know if there’s a way to create a map with multiple layers of different types (polygons, points and lines) and have a layer switch to turn them on and off outside of the map as a dash boolean switch without having to regenerate the entire map and keeping the zoom level. I was thinking something similar to the map in this website
Thanks!
Yes, that should be possible by adding a callback to the button that toggles the visibility of the relevant layer(s). It will thus require a bit more code than the built-in layer control
Dash leaflet is one of my favorite modules in Dash, great work Emil!
I got two questions on the use of geojson layers:
-
Is it possible to use several overlapping dl.GeoJSON() layers on a map and receive hover_feature or click_feature in a callback from a layer that is not the topmost one? I did some tests and was only able to receive features from a layer that is the top most one.
-
Is it possible to use several overlapping dl.GeoJSON() on a map and receive hover_feature or click_feature in a callback from all of them separately?
Hi!
Awesome work you did with dash-leaflet.
I just started with dash in general and am still stumbling around the code and get confused quickly (as I am fairly new to python in generel).
I managed to draw a polyline on a map and center the map based on the mean lat and lon values from my dataframe, now I am trying to fit the zoom of the map to show the polyline, is there any way to do that?
I tried to get the bounds of the Polyline but failed, any tipps here?
Cheers
Edit:
I figured it out myself and wanted to share the code for anyone.
min_lat = df['latitude'].min()
max_lat = df['latitude'].max()
min_lon = df['longitude'].min()
max_lon = df['longitude'].max()
bounds = [(min_lat, min_lon), (max_lat, max_lon)]
map_figure = dl.Map(
[
dl.TileLayer(),
dl.Polyline(
positions=[(row["latitude"], row["longitude"]) for _, row in df.iterrows()],
color='red',
),
],
center=(df["latitude"].mean(), df["longitude"].mean()),
style={"width": "100%", "height": "600px"},
bounds=bounds,
)
Hello @Emil,
Would you have an example on how to use the leaflet plugin easyPrint within dash-leaflet?
I have been looking around for some time, but cannot figure how to use it on my maps.
I “experimented” some things like trying to bind it to an dl.EasyButton
, but with no experience in JS whatsoever, i’m stuck…
Any help would be much much appreciated!
Thanks anyway for this awsome wrapper !
Mateusz