【问题标题】:How to handle overlaps in shapefile with Bokeh color map如何使用 Bokeh 颜色映射处理 shapefile 中的重叠
【发布时间】:2022-01-20 18:43:22
【问题描述】:

我想使用 Bokeh 生成叶绿素图。 我已经为法国各部门及其人口准备好了我的数据集。我还下载了法国部门 shapefile。

在第一次试用后,我发现我的托盘被错误地应用于部门(有些比其他人少的颜色更深)。

我发现这很奇怪,并为所有部门设置相同的人口只是为了检查,我发现并非所有部门都具有相同的颜色!在我的代码下面找到

data = gdf.join(df)
# apply same population per department
data.population = 5678

geo_src = bm.GeoJSONDataSource(geojson=data.to_json())

# set up a log colormap
cmap = bm.LogColorMapper(
    palette=bokeh.palettes.Blues9[::-1], # reverse the palette
)


# define web tools
TOOLS = "pan,wheel_zoom,box_zoom,reset,hover,save"

# set up bokeh figure
p = figure(
    title="Population", 
    tools=TOOLS,
    toolbar_location="below",
    x_axis_location=None, 
    y_axis_location=None, 
    width=800, 
    height=800
)

# remove the grid
p.grid.grid_line_color = None

# core part !
p.patches(
    'xs', 'ys', 
    fill_alpha=0.7, 
    fill_color={'field': 'population', 'transform': cmap},
    line_color='black', 
    line_width=0.5, 
    source=geo_src
)

# show plot
show(p)

查看结果,

我的猜测是那些较暗的部门有重叠的形状,而 Bokeh 应用了两倍的人口,使它们更暗...

我试图找到一种从 shapefile 中删除重叠的方法(目前还没有成功),但我想知道有没有办法配置散景以要求它不要总结重叠?

【问题讨论】:

    标签: bokeh shapefile


    【解决方案1】:

    我遇到了同样的问题,我有一个解决方案。

    问题是在使用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',))
    

    所以不要使用第二个代码,使用第一个来获得想要的结果。

    【讨论】:

      【解决方案2】:

      好吧,我终于明白我做错了什么。

      这不是关于重叠或类似的东西(我使用 QGIS 来确认没有重叠)。 相反,我注意到比其他部门更暗的部门实际上被分成了几部分!

      事情就是这样;在应用 Bokeh 补丁时,我使用的 fill_alpha 小于 1。我只需将此参数设置为 1,这样无论该部门是由什么组成的,颜色都是相同的!

      p.patches(
          'xs', 'ys', 
          fill_alpha=1, 
          fill_color={'field': 'population', 'transform': cmap},
          line_color='black', 
          line_width=0.5, 
          source=geo_src
      )
      

      【讨论】:

        猜你喜欢
        • 2019-02-20
        • 1970-01-01
        • 2013-02-14
        • 2022-07-21
        • 2018-08-04
        • 2021-08-28
        • 2014-10-04
        • 1970-01-01
        • 2021-12-28
        相关资源
        最近更新 更多