英文:
Callback to subset geometry data - dash Plotly
问题
以下是翻译的内容:
-
Ideally, the dropdown bar will be used to input the desired point data to be visualized within the figures.
理想情况下,下拉菜单将用于输入所需的点数据,以在图表中进行可视化显示。 -
import geopandas as gpd
导入geopandas库并使用别名gpd。 -
import plotly.express as px
导入plotly.express库并使用别名px。 -
import dash
导入dash库。 -
from dash import dcc
从dash库导入dcc模块。 -
from dash import html
从dash库导入html模块。 -
import dash_bootstrap_components as dbc
导入dash_bootstrap_components库并使用别名dbc。 -
point data
点数据 -
polygon data
多边形数据 -
subset African continent
子集 - 非洲大陆 -
subset European continent
子集 - 欧洲大陆 -
function to merge point data within selected polygon area
用于合并所选多边形区域内的点数据的函数 -
external_stylesheets = [dbc.themes.SPACELAB, dbc.icons.BOOTSTRAP]
外部样式表 -
app = dash.Dash(name, external_stylesheets=external_stylesheets)
创建Dash应用程序 -
function to return selected df for plotting
用于返回用于绘图的选定数据框的函数 -
area-dropdown:
区域下拉菜单: -
id='data',
ID为'data' -
value='data',
值为'data' -
options=[{'value': 'gdf_all', 'label': 'gdf_all'}, {'value': 'gdf_Africa', 'label': 'gdf_Africa'}, {'value': 'gdf_Europe', 'label': 'gdf_Europe'}],
选项包括三个值和标签 -
output 1
输出 1 -
output 2
输出 2 -
df = gdf_all
数据框为gdf_all -
df = gdf_Africa
数据框为gdf_Africa -
scatter = px.scatter_mapbox(data_frame=df, lat='LAT', lon='LON', zoom=2, mapbox_style='carto-positron')
创建scatter图表对象 -
bar = px.bar(x=count.index, y=count.values, color=count.index)
创建bar图表对象 -
app.layout = dbc.Container([...], fluid=True)
设置应用程序布局 -
if name == 'main':
如果是主程序入口点: -
app.run_server(debug=True, port=8051)
运行应用程序的服务器,启用调试模式,端口为8051。
英文:
I'm hoping to include a dropdown bar with a callback function that allows the user to display specific points within smaller areas. Initially, I want to use all point geometry data as a default. I'm then aiming to include a dropdown bar and callback function that returns smaller subsets from this main df. This is accomplished by merging the point data within a specific polygon area.
Using below, the default df is labelled gdf_all
. This contains point data across a large region. The smaller polygon files are subset from gdf_poly
. These include African and European continents. These are used within a function to only return point data if it intersects within the polygon shape.
I've hard-coded the outputs below. 1) uses gdf_all
and 2) uses a subset from African contintent.
Ideally, the dropdown bar will be used to input the desired point data to be visualised within the figures.
import geopandas as gpd
import plotly.express as px
import dash
from dash import dcc
from dash import html
import dash_bootstrap_components as dbc
# point data
gdf_all = gpd.read_file(gpd.datasets.get_path("naturalearth_cities"))
# polygon data
gdf_poly = gpd.read_file(gpd.datasets.get_path("naturalearth_lowres"))
gdf_poly = gdf_poly.drop('name', axis = 1)
gdf_all['LON'] = gdf_all['geometry'].x
gdf_all['LAT'] = gdf_all['geometry'].y
# subset African continent
Afr_gdf_area = gdf_poly[gdf_poly['continent'] == 'Africa'].reset_index(drop = True)
# subset European continent
Eur_gdf_area = gdf_poly[gdf_poly['continent'] == 'Europe'].reset_index(drop = True)
# function to merge point data within selected polygon area
def merge_withinboundary(gdf1, gdf2):
# spatial join data within larger boundary
gdf_out = gpd.sjoin(gdf1, gdf2, predicate = 'within', how = 'left').reset_index(drop = True)
return gdf_out
gdf_Africa = merge_withinboundary(gdf_all, Afr_gdf_area)
gdf_Europe = merge_withinboundary(gdf_all, Eur_gdf_area)
external_stylesheets = [dbc.themes.SPACELAB, dbc.icons.BOOTSTRAP]
app = dash.Dash(__name__, external_stylesheets = external_stylesheets)
# function to return selected df for plotting
def update_dataset(df):
if df == 'gdf_Africa':
gdf = gdf_Africa
elif df == 'gdf_Europe':
gdf = gdf_Europe
else:
gdf = gdf_all
return gdf
nav_bar = html.Div([
html.P("area-dropdown:"),
dcc.Dropdown(
id='data',
value='data',
options=[{'value': 'gdf_all', 'label': 'gdf_all'},
{'value': 'gdf_Africa', 'label': 'gdf_Africa'},
{'value': 'gdf_Europe', 'label': 'gdf_Europe'}
],
clearable=False
),
])
# output 1
df = gdf_all
# output 2
#df = gdf_Africa
scatter = px.scatter_mapbox(data_frame = df,
lat = 'LAT',
lon = 'LON',
zoom = 2,
mapbox_style = 'carto-positron',
)
count = df['name'].value_counts()
bar = px.bar(x = count.index,
y = count.values,
color = count.index,
)
app.layout = dbc.Container([
dbc.Row([
dbc.Col(html.Div(nav_bar), width=2),
dbc.Col([
dbc.Row([
dbc.Col(dcc.Graph(figure = scatter))
]),
dbc.Row([
dbc.Col(dcc.Graph(figure = bar))
]),
], width=5),
dbc.Col([
], width=5),
])
], fluid=True)
if __name__ == '__main__':
app.run_server(debug=True, port = 8051)
Output 1:
Output 2:
答案1
得分: 2
I have a partly working solution, but there's a lot of modifications that needs to be made. When you create a choropleth map using px.choropleth
, the geojson argument must be a (GeoJSON-formatted dict) – Must contain a Polygon feature collection, with IDs, which are references from locations
(from the documentation) – passing geojson = A_gdf_area
won't work because A_gdf_area
is a GeoDataFrame
.
In addition, your geojson file needs to have columns for the locations and colors in order to have colors on a choropleth map – I don't know which columns you are intending to use to color the map, so I have made up dummy columns called ids
and vals
that supply the locations and colors.
Since you are drawing regions in Italy (and px.choropleth only draws boundaries for countries of the world or states of the usa), you will need to first create a go.Scattergeo
figure using lats
and lons
extracted from the geojson, and then add the choropleth data as a trace to this figure (credit for this idea goes to this answer on the plotly forum).
I have integrated this into a dash app below. The only thing is that the colors corresponding to the vals
column don't seem to be rendering in the figure like I would expect – I will update this answer when I figure out why.
import numpy as np
import pandas as pd
import geopandas as gpd
import json
import plotly.express as px
import plotly.graph_objs as go
import dash
from dash import dcc
from dash import html
import dash_bootstrap_components as dbc
from dash.dependencies import Input, Output
gdf_area = gpd.read_file('https://raw.githubusercontent.com/openpolis/geojson-italy/master/geojson/limits_IT_municipalities.geojson')
np.random.seed(42)
gdf_area['ids'] = np.random.choice([1, 2, 3], size=len(gdf_area))
gdf_area['vals'] = np.random.uniform(1, 10, size=len(gdf_area))
A_gdf_area = gdf_area[gdf_area['name'].str.startswith('A', na=False)][['name', 'geometry', 'ids', 'vals']]
B_gdf_area = gdf_area[gdf_area['name'].str.startswith('B', na=False)][['name', 'geometry', 'ids', 'vals']]
C_gdf_area = gdf_area[gdf_area['name'].str.startswith('C', na=False)][['name', 'geometry', 'ids', 'vals']]
dropdown_dict = {
'A_gdf_area': A_gdf_area,
'B_gdf_area': B_gdf_area,
'C_gdf_area': C_gdf_area
}
def state_boundaries(geojdata):
pts = [] # list of points defining boundaries of polygons, pts has as coordinates the lon and lat
for feature in geojdata['features']:
if feature['geometry']['type'] == 'Polygon':
pts.extend(feature['geometry']['coordinates'][0])
pts.append([None, None]) # mark the end of a polygon
elif feature['geometry']['type'] == 'MultiPolygon':
for polyg in feature['geometry']['coordinates']:
pts.extend(polyg[0])
pts.append([None, None]) # end of polygon
elif feature['geometry']['type'] == 'LineString':
pts.extend(feature['geometry']['coordinates'])
pts.append([None, None])
else:
pass
lons, lats = zip(*pts)
return lons, lats
def create_choropleth_figure(gdf_area):
geojson_string = gdf_area.to_json()
geojson = json.loads(geojson_string)
lons, lats = state_boundaries(geojson)
fig = go.Figure(go.Scattergeo(lon=lons, lat=lats, mode="lines", line_width=1, line_color="black"))
fig.update_layout(width=700, height=750,
geo=dict(
scope='europe',
resolution=110, # for info help(go.layout.Geo.resolution)
lataxis_range=[33, 48],
lonaxis_range=[5, 20],
landcolor='rgb(225, 225, 225)',
))
reduced_geojson = {"type": "FeatureCollection", "features": []}
for feat in geojson["features"]:
reduced_geojson["features"].append(feat)
figc = px.choropleth(gdf_area, geojson=reduced_geojson, locations='ids',
color="vals", color_continuous_scale="viridis")
fig.add_trace(figc.data[0])
return fig
def merge_withinboundary(gdf1, gdf2):
# spatial join data within larger boundary
gdf_out = gpd.sjoin(gdf1, gdf2, predicate='within', how='left').reset_index(drop=True)
gdf_out['ids'] = gdf_out['ids_left']
gdf_out['vals'] = gdf_out['vals_left']
gdf_out.drop(columns=['ids_left', 'ids_right', 'vals_left', 'vals_right'], inplace=True)
return gdf_out
gdf_A = merge_withinboundary(A_gdf_area, gdf_area)
fig = create_choropleth_figure(gdf_A)
external_stylesheets = [dbc.themes.SPACELAB, dbc.icons.BOOTSTRAP]
app = dash.Dash(__name__, external_stylesheets=external_stylesheets)
nav_bar = html.Div([
html.P("area-dropdown:"),
dcc.Dropdown(
id='gdf',
value='Site',
options=[{'value': x, 'label': x} for x in ['A_gdf_area', 'B_gdf_area', 'C_gdf_area']],
clearable=False
),
])
app.layout = dbc.Container([
dbc.Row([
dbc.Col(html.Div(nav_bar), width=2),
dbc.Col([
dbc.Row([
dbc.Col(dcc.Graph(id='choropleth-fig', figure=fig))
]),
], width=5),
dbc.Col([
], width=5),
])
], fluid=True)
@app.callback(Output('choropleth-fig', 'figure'),
Input('gdf', 'value'),
prevent_initial_call=True)
def update_choropleth(dropdown_value):
if dropdown_value is None:
return dash.no_update
selected_gdf_area = dropdown_dict[dropdown_value]
new_gdf_area = merge_withinboundary(selected_gdf_area, gdf_area)
fig = create_choropleth_figure(new_gdf_area)
return fig
if __name__ == '__main__':
app.run_server(debug=True, port=8051)
英文:
I have a partly working solution, but there's a lot of modifications that needs to be made. When you create a choropleth map using px.choropleth
, the geojson argument must be a (GeoJSON-formatted dict) – Must contain a Polygon feature collection, with IDs, which are references from locations
(from the documentation) – passing geojson = A_gdf_area
won't work because A_gdf_area
is a GeoDataFrame
.
In addition, your geojson file needs to have columns for the locations and colors in order to have colors on a choropleth map – I don't know which columns you are intending to use to color the map, so I have made up dummy columns called ids
and vals
that supply the locations and colors.
Since you are drawing regions in Italy (and px.choropleth only draws boundaries for countries of the world or states of the usa), you will need to first create a go.Scattergeo
figure using lats
and lons
extracted from the geojson, and then add the choropleth data as a trace to this figure (credit for this idea goes to this answer on the plotly forum).
I have integrated this into a dash app below. The only thing is that the colors corresponding to the vals
column don't seem to be rendering in the figure like I would expect – I will update this answer when I figure out why.
import numpy as np
import pandas as pd
import geopandas as gpd
import json
import plotly.express as px
import plotly.graph_objs as go
import dash
from dash import dcc
from dash import html
import dash_bootstrap_components as dbc
from dash.dependencies import Input, Output
gdf_area = gpd.read_file('https://raw.githubusercontent.com/openpolis/geojson-italy/master/geojson/limits_IT_municipalities.geojson')
np.random.seed(42)
gdf_area['ids'] = np.random.choice([1,2,3], size=len(gdf_area))
gdf_area['vals'] = np.random.uniform(1, 10, size=len(gdf_area))
A_gdf_area = gdf_area[gdf_area['name'].str.startswith('A', na=False)][['name','geometry','ids','vals']]
B_gdf_area = gdf_area[gdf_area['name'].str.startswith('B', na=False)][['name','geometry','ids','vals']]
C_gdf_area = gdf_area[gdf_area['name'].str.startswith('C', na=False)][['name','geometry','ids','vals']]
dropdown_dict = {
'A_gdf_area': A_gdf_area,
'B_gdf_area': B_gdf_area,
'C_gdf_area': C_gdf_area
}
def state_boundaries(geojdata):
pts = []#list of points defining boundaries of polygons, pts has as coordinates the lon and lat
for feature in geojdata['features']:
if feature['geometry']['type'] == 'Polygon':
pts.extend(feature['geometry']['coordinates'][0])
pts.append([None, None])#mark the end of a polygon
elif feature['geometry']['type'] == 'MultiPolygon':
for polyg in feature['geometry']['coordinates']:
pts.extend(polyg[0])
pts.append([None, None])#end of polygon
elif feature['geometry']['type'] == 'LineString':
pts.extend(feature['geometry']['coordinates'])
pts.append([None, None])
else: pass
#else: raise ValueError("geometry type irrelevant for map")
lons, lats = zip(*pts)
return lons, lats
def create_choropleth_figure(gdf_area):
geojson_string = gdf_area.to_json()
geojson = json.loads(geojson_string)
lons, lats = state_boundaries(geojson)
fig = go.Figure(go.Scattergeo(lon=lons, lat=lats, mode="lines", line_width=1, line_color="black"))
fig.update_layout(width=700, height=750,
geo = dict(
scope = 'europe',
resolution = 110, #for info help(go.layout.Geo.resolution)
lataxis_range = [33, 48],
lonaxis_range = [5, 20],
landcolor = 'rgb(225, 225, 225)',
))
reduced_geojson={"type": "FeatureCollection", "features":[]}
for feat in geojson["features"]:
reduced_geojson["features"].append(feat)
figc = px.choropleth(gdf_area, geojson=reduced_geojson, locations='ids',
color="vals", color_continuous_scale="viridis")
fig.add_trace(figc.data[0])
return fig
def merge_withinboundary(gdf1, gdf2):
# spatial join data within larger boundary
gdf_out = gpd.sjoin(gdf1, gdf2, predicate = 'within', how = 'left').reset_index(drop = True)
gdf_out['ids'] = gdf_out['ids_left']
gdf_out['vals'] = gdf_out['vals_left']
gdf_out.drop(columns=['ids_left','ids_right','vals_left','vals_right'], inplace=True)
return gdf_out
gdf_A = merge_withinboundary(A_gdf_area, gdf_area)
fig = create_choropleth_figure(gdf_A)
external_stylesheets = [dbc.themes.SPACELAB, dbc.icons.BOOTSTRAP]
app = dash.Dash(__name__, external_stylesheets = external_stylesheets)
nav_bar = html.Div([
html.P("area-dropdown:"),
dcc.Dropdown(
id='gdf',
value='Site',
options=[{'value': x, 'label': x}
for x in ['A_gdf_area', 'B_gdf_area', 'C_gdf_area']],
clearable=False
),
])
app.layout = dbc.Container([
dbc.Row([
dbc.Col(html.Div(nav_bar), width=2),
dbc.Col([
dbc.Row([
dbc.Col(dcc.Graph(id = 'choropleth-fig', figure = fig))
]),
], width=5),
dbc.Col([
], width=5),
])
], fluid=True)
@app.callback(Output('choropleth-fig', 'figure'),
Input('gdf', 'value'),
prevent_initial_call=True)
def update_choropleth(dropdown_value):
if dropdown_value is None:
return dash.no_update
selected_gdf_area = dropdown_dict[dropdown_value]
new_gdf_area = merge_withinboundary(selected_gdf_area, gdf_area)
fig = create_choropleth_figure(new_gdf_area)
return fig
if __name__ == '__main__':
app.run_server(debug=True, port = 8051)
答案2
得分: 2
I think the main task is converting update_dataset
into a callback function that takes the dropdown selection as an input, and outputs both the newly updated scatter mapbox and bar chart. In order to do this, you need to provide an id
argument in dcc.Graph
for both of those figures. Then inside the callback, you can recreate both the scatter mapbox and bar chart depending on the dropdown selection.
Also I am not sure about your use case, but for the purpose of this example, I modifed your merge_withinboundary
function to perform an inner join
instead of a left join (with the sample data you've provided, if you do a left join, you will always end up with gdf_all
if that is the first argument to merge_withinboundary
– because Afr_gdf_area
and Eur_gdf_area
are both completely contained inside gdf_all
). However, I will leave that decision up to you – perhaps a left join is what you want for your actual data set.
For the purpose of this demonstration, I also set zoom = 0
as the default, so when the user selects gdf_all
from the dropdown, all of the points are visible.
import geopandas as gpd
import plotly.express as px
import dash
from dash import dcc, html, Input, Output
import dash_bootstrap_components as dbc
# point data
gdf_all = gpd.read_file(gpd.datasets.get_path("naturalearth_cities"))
# polygon data
gdf_poly = gpd.read_file(gpd.datasets.get_path("naturalearth_lowres"))
gdf_poly = gdf_poly.drop('name', axis=1)
gdf_all['LON'] = gdf_all['geometry'].x
gdf_all['LAT'] = gdf_all['geometry'].y
# subset African continent
Afr_gdf_area = gdf_poly[gdf_poly['continent'] == 'Africa'].reset_index(drop=True)
# subset European continent
Eur_gdf_area = gdf_poly[gdf_poly['continent'] == 'Europe'].reset_index(drop=True)
# function to merge point data within selected polygon area
def merge_withinboundary(gdf1, gdf2):
# spatial join data within larger boundary
gdf_out = gpd.sjoin(gdf1, gdf2, predicate='within', how='inner').reset_index(drop=True)
return gdf_out
gdf_Africa = merge_withinboundary(gdf_all, Afr_gdf_area)
gdf_Europe = merge_withinboundary(gdf_all, Eur_gdf_area)
external_stylesheets = [dbc.themes.SPACELAB, dbc.icons.BOOTSTRAP]
app = dash.Dash(__name__, external_stylesheets=external_stylesheets)
# function to return selected df for plotting
@app.callback(Output('scatter-mapbox', 'figure'),
Output('bar', 'figure'),
Input('data', 'value'),
prevent_initial_call=True)
# function to return df using smaller areas
def update_dataset(dropdown_selection):
if dropdown_selection == 'gdf_Africa':
gdf = gdf_Africa
zoom = 2
elif dropdown_selection == 'gdf_Europe':
gdf = gdf_Europe
zoom = 2
else:
gdf = gdf_all
zoom = 0
scatter_subset = px.scatter_mapbox(data_frame=gdf,
lat='LAT',
lon='LON',
zoom=zoom,
mapbox_style='carto-positron',
)
count = gdf['name'].value_counts()
bar_subset = px.bar(x=count.index,
y=count.values,
color=count.index,
)
return scatter_subset, bar_subset
nav_bar = html.Div([
html.P("area-dropdown:"),
dcc.Dropdown(
id='data',
value='data',
options=[{'value': 'gdf_all', 'label': 'gdf_all'},
{'value': 'gdf_Africa', 'label': 'gdf_Africa'},
{'value': 'gdf_Europe', 'label': 'gdf_Europe'}
],
clearable=False
),
])
# output 1
df = gdf_all
# output 2
#df = gdf_Africa
scatter = px.scatter_mapbox(data_frame=df,
lat='LAT',
lon='LON',
zoom=0,
mapbox_style='carto-positron',
)
count = df['name'].value_counts()
bar = px.bar(x=count.index,
y=count.values,
color=count.index,
)
app.layout = dbc.Container([
dbc.Row([
dbc.Col(html.Div(nav_bar), width=2),
dbc.Col([
dbc.Row([
dbc.Col(dcc.Graph(figure=scatter, id='scatter-mapbox'))
]),
dbc.Row([
dbc.Col(dcc.Graph(figure=bar, id='bar'))
]),
], width=5),
dbc.Col([
], width=5),
])
], fluid=True)
if __name__ == '__main__':
app.run_server(debug=True, port=8051)
英文:
I think the main task is converting update_dataset
into a callback function that takes the dropdown selection as an input, and outputs both the newly updated scatter mapbox and bar chart. In order to do this, you need to provide an id
argument in dcc.Graph
for both of those figures. Then inside the callback, you can recreate both the scatter mapbox and bar chart depending on the dropdown selection.
Also I am not sure about your use case, but for the purpose of this example, I modifed your merge_withinboundary
function to perform an inner join
instead of a left join (with the sample data you've provided, if you do a left join, you will always end up with gdf_all
if that is the first argument to merge_withinboundary
– because Afr_gdf_area
and Eur_gdf_area
are both completely contained inside gdf_all
). However, I will leave that decision up to you – perhaps a left join is what you want for your actual data set.
For the purpose of this demonstration, I also set zoom = 0
as the default, so when the user selects gdf_all
from the dropdown, all of the points are visible.
import geopandas as gpd
import plotly.express as px
import dash
from dash import dcc, html, Input, Output
import dash_bootstrap_components as dbc
# point data
gdf_all = gpd.read_file(gpd.datasets.get_path("naturalearth_cities"))
# polygon data
gdf_poly = gpd.read_file(gpd.datasets.get_path("naturalearth_lowres"))
gdf_poly = gdf_poly.drop('name', axis = 1)
gdf_all['LON'] = gdf_all['geometry'].x
gdf_all['LAT'] = gdf_all['geometry'].y
# subset African continent
Afr_gdf_area = gdf_poly[gdf_poly['continent'] == 'Africa'].reset_index(drop = True)
# subset European continent
Eur_gdf_area = gdf_poly[gdf_poly['continent'] == 'Europe'].reset_index(drop = True)
# function to merge point data within selected polygon area
def merge_withinboundary(gdf1, gdf2):
# spatial join data within larger boundary
gdf_out = gpd.sjoin(gdf1, gdf2, predicate = 'within', how = 'inner').reset_index(drop = True)
return gdf_out
gdf_Africa = merge_withinboundary(gdf_all, Afr_gdf_area)
gdf_Europe = merge_withinboundary(gdf_all, Eur_gdf_area)
external_stylesheets = [dbc.themes.SPACELAB, dbc.icons.BOOTSTRAP]
app = dash.Dash(__name__, external_stylesheets = external_stylesheets)
# function to return selected df for plotting
@app.callback(Output('scatter-mapbox', 'figure'),
Output('bar', 'figure'),
Input('data', 'value'),
prevent_initial_call=True)
# function to return df using smaller areas
def update_dataset(dropdown_selection):
if dropdown_selection == 'gdf_Africa':
gdf = gdf_Africa
zoom = 2
elif dropdown_selection == 'gdf_Europe':
gdf = gdf_Europe
zoom = 2
else:
gdf = gdf_all
zoom = 0
scatter_subset = px.scatter_mapbox(data_frame = gdf,
lat = 'LAT',
lon = 'LON',
zoom = zoom,
mapbox_style = 'carto-positron',
)
count = gdf['name'].value_counts()
bar_subset = px.bar(x = count.index,
y = count.values,
color = count.index,
)
return scatter_subset, bar_subset
nav_bar = html.Div([
html.P("area-dropdown:"),
dcc.Dropdown(
id='data',
value='data',
options=[{'value': 'gdf_all', 'label': 'gdf_all'},
{'value': 'gdf_Africa', 'label': 'gdf_Africa'},
{'value': 'gdf_Europe', 'label': 'gdf_Europe'}
],
clearable=False
),
])
# output 1
df = gdf_all
# output 2
#df = gdf_Africa
scatter = px.scatter_mapbox(data_frame = df,
lat = 'LAT',
lon = 'LON',
zoom = 0,
mapbox_style = 'carto-positron',
)
count = df['name'].value_counts()
bar = px.bar(x = count.index,
y = count.values,
color = count.index,
)
app.layout = dbc.Container([
dbc.Row([
dbc.Col(html.Div(nav_bar), width=2),
dbc.Col([
dbc.Row([
dbc.Col(dcc.Graph(figure = scatter, id = 'scatter-mapbox'))
]),
dbc.Row([
dbc.Col(dcc.Graph(figure = bar, id = 'bar'))
]),
], width=5),
dbc.Col([
], width=5),
])
], fluid=True)
if __name__ == '__main__':
app.run_server(debug=True, port = 8051)
答案3
得分: 0
尝试以下代码。这会修改下拉列表中的选项列表,以包括较小数据框的名称。然后,它创建一个回调函数,该函数从下拉列表中选择的值,并使用它来筛选主数据框以获取相应的较小数据框。
import pandas as pd
import geopandas as gpd
import plotly.express as px
import plotly.graph_objs as go
import dash
from dash import dcc
from dash import html
import dash_bootstrap_components as dbc
from dash.dependencies import Input, Output
gdf_area = gpd.read_file('https://raw.githubusercontent.com/openpolis/geojson-italy/master/geojson/limits_IT_municipalities.geojson')
A_gdf_area = gdf_area[gdf_area['name'].str.startswith('A', na=False)][['name', 'geometry']]
B_gdf_area = gdf_area[gdf_area['name'].str.startswith('B', na=False)][['name', 'geometry']]
C_gdf_area = gdf_area[gdf_area['name'].str.startswith('C', na=False)][['name', 'geometry']]
def merge_withinboundary(gdf1, gdf2):
gdf_out = gpd.sjoin(gdf1, gdf2, predicate='within', how='left').reset_index(drop=True)
return gdf_out
gdf_A = merge_withinboundary(A_gdf_area, gdf_area)
gdf_B = merge_withinboundary(B_gdf_area, gdf_area)
gdf_C = merge_withinboundary(C_gdf_area, gdf_area)
external_stylesheets = [dbc.themes.SPACELAB, dbc.icons.BOOTSTRAP]
app = dash.Dash(__name__, external_stylesheets=external_stylesheets)
nav_bar = html.Div([
html.P("area-dropdown:"),
dcc.Dropdown(
id='gdf',
value='A_gdf_area',
options=[{'value': 'A_gdf_area', 'label': 'A_gdf_area'},
{'value': 'B_gdf_area', 'label': 'B_gdf_area'},
{'value': 'C_gdf_area', 'label': 'C_gdf_area'}],
clearable=False
),
])
@app.callback(
Output('choropleth', 'figure'),
Input('gdf', 'value')
)
def update_choropleth(selected_gdf):
if selected_gdf == 'A_gdf_area':
gdf = gdf_A
elif selected_gdf == 'B_gdf_area':
gdf = gdf_B
else:
gdf = gdf_C
fig = px.choropleth(gdf, geojson=gdf.__geo_interface__,
color='name',
color_discrete_sequence=px.colors.qualitative.Pastel)
return fig
app.layout = dbc.Container([
dbc.Row([
dbc.Col(html.Div(nav_bar), width=2),
dbc.Col([
dbc.Row([
dbc.Col(dcc.Graph(id='choropleth'))
]),
], width=10),
])
], fluid=True)
if __name__ == '__main__':
app.run_server(debug=True, port=8051)
因此,它添加了三个新的数据框gdf_A、gdf_B和gdf_C,它们是在相应的较小数据框上调用merge_withinboundary后的结果。此外,下拉列表中的选项列表已被修改以包括名称。希望这有所帮助!
英文:
Try the below. This modifies the options list in the dropdown to include the names of the smaller data frames. Then, it creates a callback function that takes the selected value from the dropdown and uses it to filter the main data frame to get the corresponding smaller data frame.
import pandas as pd
import geopandas as gpd
import plotly.express as px
import plotly.graph_objs as go
import dash
from dash import dcc
from dash import html
import dash_bootstrap_components as dbc
from dash.dependencies import Input, Output
gdf_area = gpd.read_file('https://raw.githubusercontent.com/openpolis/geojson-italy/master/geojson/limits_IT_municipalities.geojson')
A_gdf_area = gdf_area[gdf_area['name'].str.startswith('A', na=False)][['name','geometry']]
B_gdf_area = gdf_area[gdf_area['name'].str.startswith('B', na=False)][['name','geometry']]
C_gdf_area = gdf_area[gdf_area['name'].str.startswith('C', na=False)][['name','geometry']]
def merge_withinboundary(gdf1, gdf2):
# spatial join data within larger boundary
gdf_out = gpd.sjoin(gdf1, gdf2, predicate = 'within', how = 'left').reset_index(drop = True)
return gdf_out
gdf_A = merge_withinboundary(A_gdf_area, gdf_area)
gdf_B = merge_withinboundary(B_gdf_area, gdf_area)
gdf_C = merge_withinboundary(C_gdf_area, gdf_area)
external_stylesheets = [dbc.themes.SPACELAB, dbc.icons.BOOTSTRAP]
app = dash.Dash(__name__, external_stylesheets = external_stylesheets)
nav_bar = html.Div([
html.P("area-dropdown:"),
dcc.Dropdown(
id='gdf',
value='A_gdf_area',
options=[{'value': 'A_gdf_area', 'label': 'A_gdf_area'},
{'value': 'B_gdf_area', 'label': 'B_gdf_area'},
{'value': 'C_gdf_area', 'label': 'C_gdf_area'}],
clearable=False
),
])
@app.callback(
Output('choropleth', 'figure'),
Input('gdf', 'value')
)
def update_choropleth(selected_gdf):
if selected_gdf == 'A_gdf_area':
gdf = gdf_A
elif selected_gdf == 'B_gdf_area':
gdf = gdf_B
else:
gdf = gdf_C
fig = px.choropleth(gdf, geojson=gdf.__geo_interface__,
color='name',
color_discrete_sequence=px.colors.qualitative.Pastel)
return fig
app.layout = dbc.Container([
dbc.Row([
dbc.Col(html.Div(nav_bar), width=2),
dbc.Col([
dbc.Row([
dbc.Col(dcc.Graph(id='choropleth'))
]),
], width=10),
])
], fluid=True)
if __name__ == '__main__':
app.run_server(debug=True, port=8051)
So, it adds three new data frames gdf_A, gdf_B, and gdf_C, which are the result of calling merge_withinboundary on the corresponding smaller data frames. Also, the options list in the dropdown is modified to include the names. Hope this helps!
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论