我正在绘制一个单独的 hexbin 图和 json 边界文件。尽管 hexbin 网格与边界文件重叠。我只对展示非洲大陆感兴趣。我的目标是切断或子集 hexbin 网格within 非洲大陆。因此不应在边界文件之外显示任何网格正方形。有没有办法使用 Plotly 来实现这一点?
import numpy as np
import pandas as pd
import plotly.express as px
import plotly.graph_objs as go
import plotly.figure_factory as ff
import geopandas as gpd
import json
data = pd.DataFrame({
'LAT': [1,5,6,7,5,6,7,5,6,7,5,6,7,12,-40,50],
'LON': [10,10,11,12,10,11,12,10,11,12,10,11,12,-20,40,50],
})
gdf_poly = gpd.read_file(gpd.datasets.get_path("naturalearth_lowres"))
gdf_poly = gdf_poly.drop('name', axis = 1)
Afr_gdf_area = gdf_poly[gdf_poly['continent'] == 'Africa'].reset_index(drop = True)
fig = ff.create_hexbin_mapbox(data_frame=data,
lat="LAT",
lon="LON",
nx_hexagon=25,
opacity=0.4,
labels={"color": "Point Count"},
mapbox_style='carto-positron',
zoom = 1
)
fig.update_layout(mapbox={
"layers": [
{"source": json.loads(Afr_gdf_area.geometry.to_json()),
"below": "traces",
"type": "fill",
"color": "orange",
"opacity" : 0.1,
"line": {"width": 1}
},
],
})
fig.show()
预期的输出是切断或剪掉非洲满足感之外的方块,这是橙色的。
如果你往里面看
fig.data[0]
,它是一个Choroplethmapbox
,有几个字段,包括customdata
和geojson
。 geojson 包含绘制 hexbins 所需的所有信息,包括坐标和每个六边形的唯一id
。 customdata 是一个形状为 [n_hexbins x 3]
的数组,其中数组的每个元素都包含 id 和用于绘制确定每个 hexbin 颜色的数值。
'customdata': array([[0.0, '-0.3490658516205964,-0.7648749219440846', 0],
[0.0, '-0.3490658516205964,-0.6802309514438665', 0],
[0.0, '-0.3490658516205964,-0.5955869809436484', 0],
...,
[0.0, '0.8482300176421051,0.8010385323099501', 0],
[0.0, '0.8482300176421051,0.8856825028101681', 0],
[0.0, '0.8482300176421051,0.9703264733103861', 0]], dtype=object),
'geojson': {'features': [{'geometry': {'coordinates': [[[-20.00000007,
-41.31174966478728],
[-18.6000000672,
-40.70179509236059],
[-18.6000000672,
-39.464994178287064],
[-20.00000007,
-38.838189880150665],
[-21.4000000728,
-39.464994178287064],
[-21.4000000728,
-40.70179509236059],
[-20.00000007,
-41.31174966478728]]],
'type': 'Polygon'},
'id': '-0.3490658516205964,-0.7648749219440846',
'type': 'Feature'},
{'geometry': {'coordinates': [[[-20.00000007,
-37.56790013078226],
[-18.6000000672,
-36.924474103794715],
[-18.6000000672,
-35.62123099996148],
[-20.00000007,
-34.96149172026768],
[-21.4000000728,
-35.62123099996148],
[-21.4000000728,
-36.924474103794715],
[-20.00000007,
-37.56790013078226]]],
'type': 'Polygon'},
'id': '-0.3490658516205964,-0.6802309514438665',
'type': 'Feature'},
{'geometry': {'coordinates
...
要选择指定边界内的 hexbins,我们可以从 plotly 生成的 fig.data[0] 中的 customdata 和 geojson 中提取信息开始,并创建一个 geopandas 数据框。然后我们可以创建一个名为
hexbins_in_afr
的新 geopandas 数据框,它是我们新的 hexbin 的 gdf 和 Afr_gdf_area
之间的内部连接(这样我们就可以删除 Afr_gdf_area 之外的所有 hexbin)。
我们从
geojson
和hexbins_in_afr
中提取customdata
信息后,我们可以在fig.data[0]
中显式设置以下字段:
fig.data[0]['geojson']['features'] = new_geojson
fig.data[0]['customdata'] = hexbins_in_afr['customdata']
这里是经过必要修改的代码:
import numpy as np
import pandas as pd
import plotly.express as px
import plotly.graph_objs as go
import plotly.figure_factory as ff
import geopandas as gpd
from geopandas.tools import sjoin
from shapely.geometry import Polygon
import json
data = pd.DataFrame({
'LAT': [1,5,6,7,5,6,7,5,6,7,5,6,7,12,-40,50],
'LON': [10,10,11,12,10,11,12,10,11,12,10,11,12,-20,40,50],
})
gdf_poly = gpd.read_file(gpd.datasets.get_path("naturalearth_lowres"))
gdf_poly = gdf_poly.drop('name', axis = 1)
Afr_gdf_area = gdf_poly[gdf_poly['continent'] == 'Africa'].reset_index(drop = True)
fig = ff.create_hexbin_mapbox(data_frame=data,
lat="LAT",
lon="LON",
nx_hexagon=25,
opacity=0.4,
labels={"color": "Point Count"},
mapbox_style='carto-positron',
zoom = 1
)
gdf = gpd.GeoDataFrame({
'customdata': fig.data[0]['customdata'].tolist(),
'id':[item['id'] for item in fig.data[0]['geojson']['features']],
'geometry':[Polygon(item['geometry']['coordinates'][0]) for item in fig.data[0]['geojson']['features']]
})
gdf.set_crs(epsg=4326, inplace=True)
hexbins_in_afr = sjoin(gdf, Afr_gdf_area, how='inner')
def get_coordinates(polygon):
return [[list(i) for i in polygon.exterior.coords]]
hexbins_in_afr['coordinates'] = hexbins_in_afr['geometry'].apply(lambda x: get_coordinates(x))
## create a new geojson that matches the structure of fig.data[0]['geojson']['features']
new_geojson = [{
'type': 'Feature',
'id': id,
'geometry': {
'type': 'Polygon',
'coordinates': coordinate
}
} for id, coordinate in zip(hexbins_in_afr['id'],hexbins_in_afr['coordinates'])]
fig.data[0]['geojson']['features'] = new_geojson
fig.data[0]['customdata'] = hexbins_in_afr['customdata']
fig.update_layout(mapbox={
"layers": [
{"source": json.loads(Afr_gdf_area.geometry.to_json()),
"below": "traces",
"type": "fill",
"color": "orange",
"opacity" : 0.1,
"line": {"width": 1}
},
],
})
fig.show()