我正在尝试使用密歇根州的县数据修改this example。简而言之,它正在发挥作用,但它似乎在绘制县的过程中在这里和那里增加了一些额外的形状。我猜测在某些情况下(有岛屿的县),岛屿部分需要被列为一个单独的县#34;但我不确定另一个案例,如在韦恩县的右下角部分。
这是我到目前为止所做的:
注意:要pip install shapefile(真的是pyshp),我想我必须从here下载.whl文件,然后执行pip install [.white文件的路径]。
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)
我正在寻找避免额外线条和形状的方法。
提前致谢!
答案 0 :(得分: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], ... ]
请注意,如果修补程序由多个多边形组成,则目前通过将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
此命令的输入是您要将地图数据下载到的本地目录,另一个输入是形状文件的名称。我知道上面函数中的url至少有两个可用的地图你可以调用:
map_low_res = "cb_2015_us_county_20m"
map_high_res = "cb_2015_us_county_500k"
如果美国人口普查改变了他们的网址,他们肯定会有一天,那么你需要更改输入文件名和网址变量。所以,你可以调用上面的函数
map_output = get_map_data(map_low_res, ".")
然后你可以像原始问题中的代码那样绘制它。首先添加一个颜色数据列(原始问题中的“county_colors”),然后将其设置为源,如下所示:
source = ColumnDataSource(map_output)
要完成所有工作,您需要导入诸如requests,os,itertools,shapefile,bokeh.models.ColumnDataSource等库...
答案 1 :(得分:1)