回调以获取几何数据子集 – Dash Plotly

huangapple go评论116阅读模式
英文:

Callback to subset geometry data - dash Plotly

问题

以下是翻译的内容:

  1. Ideally, the dropdown bar will be used to input the desired point data to be visualized within the figures.
    理想情况下,下拉菜单将用于输入所需的点数据,以在图表中进行可视化显示。

  2. import geopandas as gpd
    导入geopandas库并使用别名gpd。

  3. import plotly.express as px
    导入plotly.express库并使用别名px。

  4. import dash
    导入dash库。

  5. from dash import dcc
    从dash库导入dcc模块。

  6. from dash import html
    从dash库导入html模块。

  7. import dash_bootstrap_components as dbc
    导入dash_bootstrap_components库并使用别名dbc。

  8. point data
    点数据

  9. polygon data
    多边形数据

  10. subset African continent
    子集 - 非洲大陆

  11. subset European continent
    子集 - 欧洲大陆

  12. function to merge point data within selected polygon area
    用于合并所选多边形区域内的点数据的函数

  13. external_stylesheets = [dbc.themes.SPACELAB, dbc.icons.BOOTSTRAP]
    外部样式表

  14. app = dash.Dash(name, external_stylesheets=external_stylesheets)
    创建Dash应用程序

  15. function to return selected df for plotting
    用于返回用于绘图的选定数据框的函数

  16. area-dropdown:
    区域下拉菜单:

  17. id='data',
    ID为'data'

  18. value='data',
    值为'data'

  19. options=[{'value': 'gdf_all', 'label': 'gdf_all'}, {'value': 'gdf_Africa', 'label': 'gdf_Africa'}, {'value': 'gdf_Europe', 'label': 'gdf_Europe'}],
    选项包括三个值和标签

  20. output 1
    输出 1

  21. output 2
    输出 2

  22. df = gdf_all
    数据框为gdf_all

  23. df = gdf_Africa
    数据框为gdf_Africa

  24. scatter = px.scatter_mapbox(data_frame=df, lat='LAT', lon='LON', zoom=2, mapbox_style='carto-positron')
    创建scatter图表对象

  25. bar = px.bar(x=count.index, y=count.values, color=count.index)
    创建bar图表对象

  26. app.layout = dbc.Container([...], fluid=True)
    设置应用程序布局

  27. if name == 'main':
    如果是主程序入口点:

  28. 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:

回调以获取几何数据子集 – Dash Plotly

Output 2:

回调以获取几何数据子集 – Dash Plotly

答案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)

回调以获取几何数据子集 – Dash Plotly

答案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)

回调以获取几何数据子集 – Dash Plotly

英文:

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)

回调以获取几何数据子集 – Dash Plotly

答案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!

huangapple
  • 本文由 发表于 2023年2月24日 11:27:14
  • 转载请务必保留本文链接:https://go.coder-hub.com/75552364.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定