散景测绘县 [英] Bokeh Mapping Counties

查看:56
本文介绍了散景测绘县的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试修改此示例包含密歇根州的县数据.简而言之,它是可行的,但是在绘制县的过程中似乎在这里和那里添加了一些额外的形状.我猜想在某些情况下(有些县有岛屿),岛屿部分需要列为单独的县",但是我不确定另一种情况,例如下层的韦恩县状态的正确部分.

I am attempting to modify this example with county data for Michigan. In short, it's working, but it seems to be adding some extra shapes here and there in the process of drawing the counties. I'm guessing that in some instances (where there are counties with islands), the island part needs to be listed as a separate "county", but I'm not sure about the other case, such as with Wayne county in the lower right part of the state.

这是我目前所拥有的照片:

Here's a picture of what I currently have:

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

Here's what I did so far:

  1. 从Bokeh的样本县数据中获取县数据只是为了获得每个州号的州缩写(我第二个主数据源只有州号).在此示例中,我将仅过滤状态号26来简化此操作.
  2. 获取状态坐标("500k"文件)在美国中按县分类人口普查站点.
  3. 使用以下代码生成密歇根州的交互式"地图.
  1. Get county data from Bokeh's sample county data just to get the state abbreviation per state number (my second, main data source only has state numbers). For this example, I'll simplify it by just filtering for state number 26).
  2. Get state coordinates ('500k' file) by county from the U.S. Census site.
  3. Use the following code to generate an 'interactive' map of Michigan.

注意:要点安装shapefile(实际上是pyshp),我想我必须从

Note: To pip install shapefile (really pyshp), I think I had to download the .whl file from here and then do 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)

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

I'm looking for a way to avoid the extra lines and shapes.

提前谢谢!

推荐答案

我对这个问题有解决方案,并且认为我什至知道为什么它是正确的.首先,让我在Google团体Bokeh讨论中展示Bryan Van de ven的话:

I have a solution to this problem, and I think I might even know why it is correct. First, let me show quote from Bryan Van de ven in a Google groups Bokeh discussion:

没有内置的支持来处理shapefile.您将不得不将数据转换为Bokeh可以理解的简单格式. (顺便说一句:做出贡献可以使处理各种GIS格式变得更加容易,这是很棒的.)

there is no built-in support for dealing with shapefiles. You will have to convert the data to the simple format that Bokeh understands. (As an aside: it would be great to have a contribution that made dealing with various GIS formats easier).

Bokeh期望补丁的格式是点的列表列表".像这样:

The format that Bokeh expects for patches is a "list of lists" of points. So something like:

  xs = [ [patch0 x-coords], [patch1 x-coords], ... ]
  ys = [ [patch1 y-coords], [patch1 y-coords], ... ]

请注意,如果面片由多个多边形组成,则当前是通过将NaN值放入子列表来表示的.因此,任务基本上是将您需要的任何形式的多边形数据转换为这种格式,然后Bokeh可以显示它.

Note that if a patch is comprised of multiple polygons, this is currently expressed by putting NaN values in the sublists. So, the task is basically to convert whatever form of polygon data you have to this format, and then Bokeh can display it.

因此,似乎您在某种程度上忽略了NaN或无法正确处理多个多边形.这是一些代码,这些代码将下载美国人口普查数据,将其解压缩,为Bokeh正确读取,并制作一个经纬度,经度,州和县的数据框.

So it seems like somehow you are ignoring NaNs or otherwise not handling multiple polygons properly. Here is some code that will download US census data, unzip it, read it properly for Bokeh, and make a data frame of lat, long, state, and county.

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

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

The inputs to this command are a local directory where you want to download the map data to and the other input is the name of the shape file. I know there are at least two available maps from the url in the function above that you could call:

map_low_res = "cb_2015_us_county_20m"
map_high_res = "cb_2015_us_county_500k"

如果美国人口普查改变了他们的网址(他们肯定会更改一天),那么您将需要更改输入文件名和url变量.因此,您可以调用

If the US census changes their url, which they certainly will one day, then you will need to change the input file name and the url variable. So, you can call the function above

map_output = get_map_data(map_low_res, ".")

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

Then you could plot it just as the code in the original question does. Add a color data column first ("county_colors" in the original question), and then set it to the source like this:

source = ColumnDataSource(map_output)

要使所有这些工作正常进行,您将需要导入库,例如请求,操作系统,itertools,shapefile,bokeh.models.ColumnDataSource等...

To make this all work you will need to import libraries such as requests, os, itertools, shapefile, bokeh.models.ColumnDataSource, etc...

这篇关于散景测绘县的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆