Im very new to working with GIS data (using Dash Leaflet and GeoPandas) and am currently stumped.
My goal is to create a simple app which does the following:
- App starts with an empty dash_leaflet.Map() figure and a numeric input box titled “Buffer Distance” (with a default of
100
) - User draws a polygon on the map which fires a callback
- Callback takes in the GeoJSON data from the map and the “buffer distance”
- Use Geopandas to import the GeoJSON data and create a new polygon which is smaller than the user drawn polygon by “Buffer Distance”
- Pass these 2 polygons (originally drawn & post processed polygon with buffer) back to the map so that both are now displayed on the map
Im having trouble with the last step of pushing the two polygons back the map via some kind of Output
This is the app i am currently working with:
import pandas as pd
from dash import Dash, dcc, html, Input, Output, State
import dash_leaflet as dl
import geopandas as gpd
lat1, lon1 = 36.215487, -81.674006
app = Dash()
input_details = html.Div([
html.Div([
html.Div(['Buffer Distance'], style={'width': '37%', 'display': 'inline-block'}),
dcc.Input(
value=100,
id="buffer-distance",
type='number',
placeholder='Required',
),
]),
])
default_map_children = [
dl.TileLayer(),
dl.FeatureGroup([
dl.EditControl(id="edit_control"),
]),
dl.GeoJSON(id='map-geojsons')
]
map_input_results_tab = html.Div(
[
html.H2('Add Shapes to Map an Area of Interest'),
dl.Map(
id='leaflet-map',
style={'width': '100%', 'height': '50vh'},
center=[lat1, lon1],
zoom=16,
children=default_map_children
)
])
app.layout = html.Div([input_details, map_input_results_tab])
@app.callback(
Output('map-geojsons', 'data'),
Input('edit_control', 'geojson'),
State('buffer-distance', 'value'),
)
def update_estimates(drawn_geojson, perim_clear):
if any([x is None for x in [drawn_geojson, perim_clear]]):
# some value has not been provided, so do not continue with calculations
return drawn_geojson
elif not drawn_geojson["features"]:
# some value has not been provided, so do not continue with calculations
return drawn_geojson
gdf = gpd.GeoDataFrame.from_features(drawn_geojson["features"]) # extract user drawn geometry data from UI
gdf = gdf.set_crs(crs=4326) # Set the initial CRS to specify that this is lat/lon data
gdf = gdf.to_crs(
crs=gdf.estimate_utm_crs()) # Let GeoPandas estimate the best CRS and use that for the area calculation
# create a new geodataframe using buffer that incorporates the perimeter
gdf_minus_perim_buffer = gdf['geometry'].buffer(-perim_clear)
combine_gdf = pd.concat([gdf['geometry'], gdf_minus_perim_buffer])
# convert back to lat, long
combine_gdf = combine_gdf.to_crs(crs=4326)
# convert back to GeoJSON to be rendered in the dash leaflet map
return_geojson_data = combine_gdf.to_json()
return return_geojson_data
if __name__ == '__main__':
app.run_server(debug=True, port=8052)
I think I am close, but am just missing something… Thanks in advance for any help!
P.S. @Emil, sorry to call you out, but i’ve seen you pop up on many helpful threads in my searching