我遇到了同样的问题,我有一个解决方案。
问题是在使用p.patches 的散景中的一个错误,当补丁在边界的x 数据列表中使用nan 分隔符描述多个多边形时。以后我得想办法报告这个错误。
解决方案是改用“p.multi_polygons”。它们的多个多边形可以包含在每个条目中,而不会受到错误的影响。 https://docs.bokeh.org/en/latest/docs/reference/models/glyphs/multi_polygons.html
我没有经常使用geojson,但我使用GeoPandas。以下是使用来自https://www.naturalearthdata.com/downloads/ 的数据的工作示例:
import geopandas as gpd
from shapely.geometry.polygon import Polygon
from shapely.geometry.multipolygon import MultiPolygon
from bokeh.io import show, output_file
from bokeh.models import MultiPolygons, ColorMapper, LinearColorMapper
from bokeh.palettes import Inferno256 as palette
from bokeh.plotting import figure
from bokeh.models import ColumnDataSource
from bokeh.layouts import row, column
# Define the function I gave earlier
def get_MultiPoly(mpoly,coord_type='x'):
"""Returns the coordinates ('x' or 'y') for the exterior and interior of MultiPolygon digestible by multi_polygons in Bokeh"""
if coord_type == 'x':
i=0
elif coord_type == 'y':
i=1
# Get the x or y coordinates
c = []
if isinstance(mpoly,Polygon):
mpoly = [mpoly]
for poly in mpoly: # the polygon objects return arrays, it's important they be lists or Bokeh fails
exterior_coords = poly.exterior.coords.xy[i].tolist()
interior_coords = []
for interior in poly.interiors:
if isinstance(interior.coords.xy[i],list):
interior_coords += [interior.coords.xy[i]]
else:
interior_coords += [interior.coords.xy[i].tolist()]
c.append([exterior_coords, *interior_coords])
return c
# Define input/output files and get data, index by name
output_file("tmp.html")
map_world_gpd = gpd.read_file('map_data/ne_10m_admin_0_countries/ne_10m_admin_0_countries.shp')
map_world_gpd.set_index('NAME_EN',inplace=True)
# Parse all countries into a ColumnDataSource
N=len(map_world_gpd)
source3 = ColumnDataSource({
'x': [get_MultiPoly(map_world_gpd.iloc[i]['geometry'],'x') for i in range(0,N)],
'y': [get_MultiPoly(map_world_gpd.iloc[i]['geometry'],'y') for i in range(0,N)],
'n': [i for i in range(0,N)],
'name':[map_world_gpd.iloc[i]['NAME'] for i in range(0,N)],
})
# Plot all the countries
p = figure(title="map_exploration",match_aspect=True,aspect_ratio=2,
tooltips=[
("Name",'@name')
],
)
p.multi_polygons(xs='x',ys='y', source=source3,
fill_color={'field': 'n', 'transform': LinearColorMapper(palette=palette,low=0,high=len(source3.data['x']))},
fill_alpha=0.5, line_color="black", line_width=0.5)
show(row(p,sizing_mode = 'scale_width'))
如果您对该错误感兴趣,那么这里有一个显示该错误的示例:
map_states_gpd = gpd.read_file('map_data/cb_2019_us_state_20m.shp')
map_states_gpd.set_index('NAME',inplace=True)
map_world_gpd = gpd.read_file('map_data/ne_10m_admin_0_countries/ne_10m_admin_0_countries.shp')
map_world_gpd.set_index('NAME_EN',inplace=True)
map_world_gpd = map_world_gpd.to_crs(epsg=3395)
gsource = map_world_gpd.loc['Spain']['geometry']
output_file("tmp.html")
def get_PolyCoords(poly,coord_type='x'):
"""Returns the coordinates ('x' or 'y') of """
if coord_type == 'x':
# Get the x coordinates of the exterior
return list( poly.exterior.coords.xy[0] )
elif coord_type == 'y':
# Get the y coordinates of the exterior
return list( poly.exterior.coords.xy[1] )
def get_MultiPoly(mpoly,coord_type='x'):
"""Returns the coordinates ('x' or 'y') for the exterior and interior of MultiPolygon digestible by multi_polygons in Bokeh"""
if coord_type == 'x':
i=0
elif coord_type == 'y':
i=1
# Get the x or y coordinates
c = []
if isinstance(mpoly,Polygon):
mpoly = [mpoly]
for poly in mpoly: # the polygon objects return arrays, it's important they be lists or Bokeh fails
exterior_coords = poly.exterior.coords.xy[i].tolist()
interior_coords = []
for interior in poly.interiors:
if isinstance(interior.coords.xy[i],list):
interior_coords += [interior.coords.xy[i]];
else:
interior_coords += [interior.coords.xy[i].tolist()]
c.append([exterior_coords, *interior_coords])
return c
def get_MultiPolyCords(mpoly,coord_type='x'):
"""Returns the coordinates ('x' or 'y') of edges of a MultiPolygon exterior"""
if isinstance(mpoly,Polygon):
mpoly = [mpoly]
if coord_type == 'x':
# Get the x coordinates of the exterior
x = []
for poly in mpoly:
x.append(get_PolyCoords(poly,coord_type))
return x
elif coord_type == 'y':
# Get the y coordinates of the exterior
y = []
for poly in mpoly:
y.append(get_PolyCoords(poly,coord_type))
return y
def concat_MultiPolyCords(mpoly,coord_type='x'):
"""Returns the coordinates ('x' or 'y') of edges of a MultiPolygon exterior"""
if isinstance(mpoly,Polygon):
mpoly = [mpoly]
if coord_type == 'x':
# Get the x coordinates of the exterior
x = []
for poly in mpoly:
x.append(float('NaN'))
x = x+ get_PolyCoords(poly,coord_type)
return x
elif coord_type == 'y':
# Get the y coordinates of the exterior
y = []
for poly in mpoly:
y.append(float('NaN'))
y = y+ get_PolyCoords(poly,coord_type)
return y
def plen(mp):
if isinstance(mp,MultiPolygon):
return len(mp)
else:
return 1
def plen(mpoly):
if isinstance(mpoly,Polygon):
return 1
else:
return len(mpoly)
# Plot the patches using the same color
source1 = ColumnDataSource({
'x': get_MultiPolyCords(gsource,'x'),
'y': get_MultiPolyCords(gsource,'y'),
'n': [3]*plen(gsource),
})
# Plot the patches by combining them into one patch, separate sub patches with nan, all same color
# Note that the alpha outcome is faulty, it seems to redraw all the patches for each one
ys = [concat_MultiPolyCords(gsource,'y')]
source2 = ColumnDataSource({
'x': [concat_MultiPolyCords(gsource,'x')],
'y': [concat_MultiPolyCords(gsource,'y')],
'n': [3],
})
# Initialize our figure
p1 = figure(title="map_exploration",match_aspect=True,aspect_ratio=2)
# Plot grid
p1.patches('x', 'y', source=source1,
fill_color={'field': 'n', 'transform': LinearColorMapper(palette=palette,low=0,high=plen(gsource))},
fill_alpha=0.5, line_color="black", line_width=0.5)
p1.title.text = "Map has "+str(plen(gsource))+" polygons, patched using "+str(plen(source1.data['x']))+" patches"
# Initialize our figure
p2 = figure(title="map_exploration",match_aspect=True,aspect_ratio=2)
# Plot grid
p2.patches('x', 'y', source=source2,
fill_color={'field': 'n', 'transform': LinearColorMapper(palette=palette,low=0,high=plen(gsource))},
fill_alpha=0.5, line_color="black", line_width=0.5)
p2.title.text = "Map has "+str(plen(gsource))+" polygons, patched using "+str(plen(source2.data['x']))+" patches using NaN separators"
show(row(p1,p2,sizing_mode = 'scale_width',))
所以不要使用第二个代码,使用第一个来获得想要的结果。