散景映射县

Dan*_*ty2 1 shapefile python-3.x pandas bokeh

我试图用密歇根州的县数据修改这个例子.简而言之,它是有效的,但它似乎在绘制县的过程中在这里和那里增加了一些额外的形状.我猜测在某些情况下(有岛屿的县),岛屿部分需要列为一个单独的"县",但我不确定另一个案例,例如在较低的韦恩县正确的部分国家.

这是我目前拥有的图片: 在此输入图像描述

这是我到目前为止所做的:

  1. 从Bokeh的样本县数据中获取县数据只是为了获得每个州号的州缩写(我的第二个,主数据源只有州号).对于这个例子,我将通过仅过滤状态号26来简化它.
  2. 美国人口普查网站获取各州的州立坐标('500k'文件).
  3. 使用以下代码生成密歇根州的"交互式"地图.

注意:要pip install shapefile(真的是pyshp),我想我必须从这里下载.whl文件,然后执行pip install [path to .whl file].

import pandas as pd
import numpy as np
import shapefile
from bokeh.models import HoverTool, ColumnDataSource
from bokeh.palettes import Viridis6
from bokeh.plotting import figure, show, output_notebook
shpfile=r'Path\500K_US_Counties\cb_2015_us_county_500k.shp'
sf = shapefile.Reader(shpfile)
shapes = sf.shapes()

#Here are the rows from the shape file (plus lat/long coordinates)
rows=[]
lenrow=[]
for i,j in zip(sf.shapeRecords(),sf.shapes()):
    rows.append(i.record+[j.points])
    if len(i.record+[j.points])!=10:
           print("Found record with irrular number of columns")
fields1=sf.fields[1:] #Ignore first field as it is not used (maybe it's a meta field?)
fields=[seq[0] for seq in fields1]+['Long_Lat']#Take the first element in each tuple of the list
c=pd.DataFrame(rows,columns=fields)
try:
    c['STATEFP']=c['STATEFP'].astype(int)
except:
    pass
#cns=pd.read_csv(r'Path\US_Counties.csv')
#cns=cns[['State Abbr.','STATE num']]
#cns=cns.drop_duplicates('State Abbr.',keep='first')
#c=pd.merge(c,cns,how='left',left_on='STATEFP',right_on='STATE num')
c['Lat']=c['Long_Lat'].apply(lambda x: [e[0] for e in x]) 
c['Long']=c['Long_Lat'].apply(lambda x: [e[1] for e in x])
#c=c.loc[c['State Abbr.']=='MI']
c=c.loc[c['STATEFP']==26]
#latitudex, longitude=y
county_xs = c['Lat']
county_ys = c['Long']
county_names = c['NAME']
county_colors = [Viridis6[np.random.randint(1,6, size=1).tolist()[0]] for l in aland]
randns=np.random.randint(1,6, size=1).tolist()[0]
#county_colors = [Viridis6[e] for e in randns]
#county_colors = 'b'
source = ColumnDataSource(data=dict(
    x=county_xs,
    y=county_ys,
    color=county_colors,
    name=county_names,
    #rate=county_rates,
))

output_notebook()

TOOLS="pan,wheel_zoom,box_zoom,reset,hover,save"

p = figure(title="Title", tools=TOOLS,
           x_axis_location=None, y_axis_location=None)
p.grid.grid_line_color = None

p.patches('x', 'y', source=source,
          fill_color='color', fill_alpha=0.7,
          line_color="white", line_width=0.5)

hover = p.select_one(HoverTool)
hover.point_policy = "follow_mouse"
hover.tooltips = [
    ("Name", "@name"),
    #("Unemployment rate)", "@rate%"),
    ("(Long, Lat)", "($x, $y)"),
]

show(p)
Run Code Online (Sandbox Code Playgroud)

我正在寻找一种避免额外线条和形状的方法.

提前致谢!

Ale*_*lex 5

我有一个解决这个问题的方法,我我甚至可能知道它为什么是正确的.首先,让我在Google小组Bokeh讨论中向Bryan Van de ven引用:

没有内置的支持来处理shapefile.您必须将数据转换为Bokeh理解的简单格式.(顺便说一句:如果能够更轻松地处理各种GIS格式,那将会很棒.)

Bokeh期望补丁的格式是点的"列表".所以类似于:

  xs = [ [patch0 x-coords], [patch1 x-coords], ... ]
  ys = [ [patch1 y-coords], [patch1 y-coords], ... ]
Run Code Online (Sandbox Code Playgroud)

请注意,如果修补程序由多个多边形组成,则目前通过将NaN值放在子列表中来表示.因此,任务基本上是将您拥有的任何形式的多边形数据转换为此格式,然后Bokeh可以显示它.

因此,似乎你忽略了NaN或者不能正确处理多个多边形.下面是一些代码,它们将下载美国人口普查数据,解压缩,正确读取Bokeh,并制作纬度,长度,州和县的数据框.

def get_map_data(shape_data_file, local_file_path):
    url = "http://www2.census.gov/geo/tiger/GENZ2015/shp/" + \
      shape_data_file + ".zip"
    zfile = local_file_path + shape_data_file + ".zip"
    sfile = local_file_path + shape_data_file + ".shp"
    dfile = local_file_path + shape_data_file + ".dbf"
    if not os.path.exists(zfile):
        print("Getting file: ", url)
        response = requests.get(url)
        with open(zfile, "wb") as code:
            code.write(response.content)

    if not os.path.exists(sfile):
        uz_cmd = 'unzip ' + zfile + " -d " + local_file_path
        print("Executing command: " + uz_cmd)
        os.system(uz_cmd)

    shp = open(sfile, "rb")
    dbf = open(dfile, "rb")
    sf = shapefile.Reader(shp=shp, dbf=dbf)

    lats = []
    lons = []
    ct_name = []
    st_id = []
    for shprec in sf.shapeRecords():
        st_id.append(int(shprec.record[0]))
        ct_name.append(shprec.record[5])
        lat, lon = map(list, zip(*shprec.shape.points))
        indices = shprec.shape.parts.tolist()
        lat = [lat[i:j] + [float('NaN')] for i, j in zip(indices, indices[1:]+[None])]
        lon = [lon[i:j] + [float('NaN')] for i, j in zip(indices, indices[1:]+[None])]
        lat = list(itertools.chain.from_iterable(lat))
        lon = list(itertools.chain.from_iterable(lon))
        lats.append(lat)
        lons.append(lon)

    map_data = pd.DataFrame({'x': lats, 'y': lons, 'state': st_id, 'county_name': ct_name})
    return map_data
Run Code Online (Sandbox Code Playgroud)

此命令的输入是您要将地图数据下载到的本地目录,另一个输入是形状文件的名称.我知道上面函数中的url至少有两个可用的地图你可以调用:

map_low_res = "cb_2015_us_county_20m"
map_high_res = "cb_2015_us_county_500k"
Run Code Online (Sandbox Code Playgroud)

如果美国人口普查改变了他们的网址,他们肯定会有一天,那么你需要更改输入文件名和网址变量.所以,你可以调用上面的函数

map_output = get_map_data(map_low_res, ".")
Run Code Online (Sandbox Code Playgroud)

然后你可以像原始问题中的代码那样绘制它.首先添加一个颜色数据列(原始问题中的"county_colors"),然后将其设置为源,如下所示:

source = ColumnDataSource(map_output)
Run Code Online (Sandbox Code Playgroud)

要完成所有工作,您需要导入诸如requests,os,itertools,shapefile,bokeh.models.ColumnDataSource等库...