使用 python dash-leaflet 高效过滤 GeoJSON 组件
Efficient filtering of GeoJSON components with python dash-leaflet
我正在使用 Dash Leaflet 地图组件 dash-leaflet 进行交互式地图可视化。
我的目标是通过破折号组件(例如 dcc.Slider
)的值过滤大型 GeoJSON 组件(dl.GeoJSON
)。
我目前的做法如下:
import random
import dash
import dash_html_components as html
import dash_leaflet as dl
import dash_leaflet.express as dlx
import dash_core_components as dcc
from dash.dependencies import Input, Output
# Create some markers.
points = [dict(lat=55.5 + random.random(), lon=9.5 + random.random(), value=random.random()*100) for i in range(100)]
data = dlx.dicts_to_geojson(points)
app = dash.Dash()
app.layout = html.Div([
dl.Map([
dl.TileLayer(),
dl.GeoJSON(id="data-id", data=data)
], center=(56, 10), zoom=8, style={'height': '50vh'}),
html.Div([
html.H5('Filtering'),
dcc.Slider(id='my-slider', min=0, max=100, step=1, value=100),
html.Div(id='slider-output-container')
], style={'width': '30%'}),
])
@app.callback(
Output('slider-output-container', 'children'),
Output('data-id', 'data'),
[Input('my-slider', 'value')])
def update_output(value):
points_new = [p for p in points if p['value'] <= value]
data_new = dlx.dicts_to_geojson(points_new)
return 'You have selected value "{}"'.format(value), data_new
if __name__ == '__main__':
app.run_server()
在此示例中,GeoJSON 组件“data-id”的数据对象通过根据输入值过滤点列表并返回新的破折号组件“my-slider”的值进行过滤使用 dlx.dicts_to_geojson
函数创建的 geoJSON 对象:
points_new = [p for p in points if p['value'] <= value]
data_new = dlx.dicts_to_geojson(points_new)
这是过滤 geoJSON 对象的正确方法吗?
我可以想象有更好的方法可以在客户端使用 GeoJSON 组件的 options
-feature 和 javascript 函数来定义这样的过滤器函数,但我不知道怎么样。
我很感激我能得到的任何 advice/code 例子。
虽然可以过滤 Python 中的数据,但它会根据数据大小引入显着的网络开销(每次过滤器更改时,数据都会从服务器传输到客户端)。如果你做过滤客户端,你只需要传输一次数据,即性能差异可能是巨大的。
客户端过滤可以通过添加 JavaScript 资产(即放置在资产文件夹中的 .js 文件)实现,过滤功能按照 the documentation,
window.myNamespace = Object.assign({}, window.myNamespace, {
mySubNamespace: {
filter_features: function(feature, context) {
// code should return true if feature is included, otherwise false
const value = context.props.hideout['value']
...
}
}
});
要应用过滤,将函数句柄传递给过滤函数到 GeoJSON
组件,
import dash_leaflet as dl
from dash_extensions.javascript import Namespace
...
ns = Namespace("myNamespace ", "mySubNamespace")
dl.GeoJSON(id="geojson", options=dict(filter=ns("filter_features"), ...)
最后,通过使过滤器依赖于 hideout
道具(如上面的示例代码所示),您可以通过回调更新此道具来实现交互性,
@app.callback(Output("geojson", "hideout"), ...)
def update(...):
...
return {"value": value}
编辑:根据评论中的要求,这是一个独立的小示例,演示仅使用客户端逻辑进行交互式 geojson 过滤,
import dash_html_components as html
import dash_leaflet as dl
import dash_core_components as dcc
import dash_leaflet.express as dlx
from dash import Dash
from dash.dependencies import Output, Input
from dash_extensions.javascript import assign
# A few cities in Denmark.
cities = [dict(name="Aalborg", lat=57.0268172, lon=9.837735),
dict(name="Aarhus", lat=56.1780842, lon=10.1119354),
dict(name="Copenhagen", lat=55.6712474, lon=12.5237848)]
# Create drop down options.
dd_options = [dict(value=c["name"], label=c["name"]) for c in cities]
dd_defaults = [o["value"] for o in dd_options]
# Generate geojson with a maker for each city and name as tooltip.
geojson = dlx.dicts_to_geojson([{**c, **dict(tooltip=c['name'])} for c in cities])
# Create javascript function that filters on feature name.
geojson_filter = assign("function(feature, context){return context.props.hideout.includes(feature.properties.name);}")
# Create example app.
app = Dash()
app.layout = html.Div([
dl.Map(children=[
dl.TileLayer(),
dl.GeoJSON(data=geojson, options=dict(filter=geojson_filter), hideout=dd_defaults, id="geojson")
], style={'width': '100%', 'height': '50vh', 'margin': "auto", "display": "block"}, id="map"),
dcc.Dropdown(id="dd", value=dd_defaults, options=dd_options, clearable=False, multi=True)
])
# Link drop down to geojson hideout prop (could also be done with a normal callback).
app.clientside_callback("function(x){return x;}", Output("geojson", "hideout"), Input("dd", "value"))
if __name__ == '__main__':
app.run_server()
注意需要dash-extensions==0.0.55
.
我正在使用 Dash Leaflet 地图组件 dash-leaflet 进行交互式地图可视化。
我的目标是通过破折号组件(例如 dcc.Slider
)的值过滤大型 GeoJSON 组件(dl.GeoJSON
)。
我目前的做法如下:
import random
import dash
import dash_html_components as html
import dash_leaflet as dl
import dash_leaflet.express as dlx
import dash_core_components as dcc
from dash.dependencies import Input, Output
# Create some markers.
points = [dict(lat=55.5 + random.random(), lon=9.5 + random.random(), value=random.random()*100) for i in range(100)]
data = dlx.dicts_to_geojson(points)
app = dash.Dash()
app.layout = html.Div([
dl.Map([
dl.TileLayer(),
dl.GeoJSON(id="data-id", data=data)
], center=(56, 10), zoom=8, style={'height': '50vh'}),
html.Div([
html.H5('Filtering'),
dcc.Slider(id='my-slider', min=0, max=100, step=1, value=100),
html.Div(id='slider-output-container')
], style={'width': '30%'}),
])
@app.callback(
Output('slider-output-container', 'children'),
Output('data-id', 'data'),
[Input('my-slider', 'value')])
def update_output(value):
points_new = [p for p in points if p['value'] <= value]
data_new = dlx.dicts_to_geojson(points_new)
return 'You have selected value "{}"'.format(value), data_new
if __name__ == '__main__':
app.run_server()
在此示例中,GeoJSON 组件“data-id”的数据对象通过根据输入值过滤点列表并返回新的破折号组件“my-slider”的值进行过滤使用 dlx.dicts_to_geojson
函数创建的 geoJSON 对象:
points_new = [p for p in points if p['value'] <= value]
data_new = dlx.dicts_to_geojson(points_new)
这是过滤 geoJSON 对象的正确方法吗?
我可以想象有更好的方法可以在客户端使用 GeoJSON 组件的 options
-feature 和 javascript 函数来定义这样的过滤器函数,但我不知道怎么样。
我很感激我能得到的任何 advice/code 例子。
虽然可以过滤 Python 中的数据,但它会根据数据大小引入显着的网络开销(每次过滤器更改时,数据都会从服务器传输到客户端)。如果你做过滤客户端,你只需要传输一次数据,即性能差异可能是巨大的。
客户端过滤可以通过添加 JavaScript 资产(即放置在资产文件夹中的 .js 文件)实现,过滤功能按照 the documentation,
window.myNamespace = Object.assign({}, window.myNamespace, {
mySubNamespace: {
filter_features: function(feature, context) {
// code should return true if feature is included, otherwise false
const value = context.props.hideout['value']
...
}
}
});
要应用过滤,将函数句柄传递给过滤函数到 GeoJSON
组件,
import dash_leaflet as dl
from dash_extensions.javascript import Namespace
...
ns = Namespace("myNamespace ", "mySubNamespace")
dl.GeoJSON(id="geojson", options=dict(filter=ns("filter_features"), ...)
最后,通过使过滤器依赖于 hideout
道具(如上面的示例代码所示),您可以通过回调更新此道具来实现交互性,
@app.callback(Output("geojson", "hideout"), ...)
def update(...):
...
return {"value": value}
编辑:根据评论中的要求,这是一个独立的小示例,演示仅使用客户端逻辑进行交互式 geojson 过滤,
import dash_html_components as html
import dash_leaflet as dl
import dash_core_components as dcc
import dash_leaflet.express as dlx
from dash import Dash
from dash.dependencies import Output, Input
from dash_extensions.javascript import assign
# A few cities in Denmark.
cities = [dict(name="Aalborg", lat=57.0268172, lon=9.837735),
dict(name="Aarhus", lat=56.1780842, lon=10.1119354),
dict(name="Copenhagen", lat=55.6712474, lon=12.5237848)]
# Create drop down options.
dd_options = [dict(value=c["name"], label=c["name"]) for c in cities]
dd_defaults = [o["value"] for o in dd_options]
# Generate geojson with a maker for each city and name as tooltip.
geojson = dlx.dicts_to_geojson([{**c, **dict(tooltip=c['name'])} for c in cities])
# Create javascript function that filters on feature name.
geojson_filter = assign("function(feature, context){return context.props.hideout.includes(feature.properties.name);}")
# Create example app.
app = Dash()
app.layout = html.Div([
dl.Map(children=[
dl.TileLayer(),
dl.GeoJSON(data=geojson, options=dict(filter=geojson_filter), hideout=dd_defaults, id="geojson")
], style={'width': '100%', 'height': '50vh', 'margin': "auto", "display": "block"}, id="map"),
dcc.Dropdown(id="dd", value=dd_defaults, options=dd_options, clearable=False, multi=True)
])
# Link drop down to geojson hideout prop (could also be done with a normal callback).
app.clientside_callback("function(x){return x;}", Output("geojson", "hideout"), Input("dd", "value"))
if __name__ == '__main__':
app.run_server()
注意需要dash-extensions==0.0.55
.