有效地找到200k多边形的一阶邻居


14

对于208781个人口普查区块组中的每一个,我想检索其所有一阶邻居的FIPS ID。我已经下载了所有TIGER边界,并合并到一个1GB的shapefile中。

我尝试了一个ArcPython脚本,该脚本在其核心使用SelectLayerByLocation用于BOUNDARY_TOUCHES,但是每个块组都花费了1秒以上的时间,这比我想要的要慢。即使在我将SelectLayerByLocation搜索限制为阻止处于相同状态的组之后,也是如此。我找到了此脚本,但是它在内部也使用SelectLayerByLocation,因此速度没有提高。

该解决方案不一定是基于Arc的-尽管我对使用Python编码最自在,但我也愿意接受其他软件包。


2
从9.3版开始,空间统计工具箱中已有工具可以执行此操作。从10.0开始,它们非常有效。我记得在类似大小的shapefile上执行了类似的操作(一个状态中的所有),该操作在30分钟内完成,其中15个仅用于磁盘I / O,而这是两年前在速度较慢的计算机上进行的。也可以访问Python源代码。
ub

您使用了空间统计中的哪个地理处理工具?
dmahr

1
我忘记了它的名字;它专门用于创建多边形邻居关系表。帮助系统鼓励您运行任何基于邻居的空间统计工具之前创建此表,以使这些工具不必在每次运行时即时重新计算此信息。至少在9.x版本中,一个重大限制是输出为.dbf格式。对于无法正常工作的大型输入shapefile,在这种情况下,您必须将操作分解为多个部分,或者修改Python代码以更好的格式输出。
ub


对,就是那样。Python代码充分利用了内部ArcGIS功能(使用空间索引),从而使算法非常快速。
whuber

Answers:


3

如果您可以访问ArcGIS 10.2 for Desktop(或更早版本),则可以使用“ 多边形邻域(分析)”工具:

创建具有基于多边形连续性(重叠,重合的边或节点)的统计信息的表。

多边形邻居

现在可以使这项任务容易得多。


谢谢,PolyGeo。我已经更新了可接受的答案,因此“多边形邻居”工具的曝光度更高。尽管我不确定大型数据集的可伸缩性如何比较,但它绝对比我的手动基于Python的方法更强大。
dmahr 2014年

我目前正在使用10.3,但在我的shapefile中,它的〜300K人口普查块失败。
soandos

@soandos听起来像一个新问题可能值得研究/询问。
PolyGeo

8

对于避免使用ArcGIS的解决方案,请使用pysal。您可以使用以下方法直接从shapefile获取权重:

w = pysal.rook_from_shapefile("../pysal/examples/columbus.shp")

要么

w = pysal.queen_from_shapefile("../pysal/examples/columbus.shp")

前往文档以获取更多信息。


3

只是一个更新。以下Whuber的意见后,我发现,生成空间权重矩阵简单地使用Python循环和字典来确定邻居。我复制了以下过程。

第一部分遍历每个块组的每个顶点。它创建一个以顶点坐标为键的字典,并创建一个以该坐标为顶点的块组ID列表。请注意,这需要拓扑整齐的数据集,因为只有完美的顶点/顶点重叠才会注册为邻居关系。幸运的是,人口普查局的TIGER块组shapefile在这方面是可以的。

第二部分再次遍历每个块组的每个顶点。它创建一个块组的ID作为键和块组的邻居的ID作为值的字典。

# Create dictionary of vertex coordinate : [...,IDs,...]
BlockGroupVertexDictionary = {}
BlockGroupCursor = arcpy.SearchCursor(BlockGroups.shp)
BlockGroupDescription = arcpy.Describe(BlockGroups.shp)
BlockGroupShapeFieldName = BlockGroupsDescription.ShapeFieldName
#For every block group...
for BlockGroupItem in BlockGroupCursor :
    BlockGroupID = BlockGroupItem.getValue("BKGPIDFP00")
    BlockGroupFeature = BlockGroupItem.getValue(BlockGroupShapeFieldName)
    for BlockGroupPart in BlockGroupFeature:
        #For every vertex...
        for BlockGroupPoint in BlockGroupPart:
            #If it exists (and isnt empty interior hole signifier)...
            if BlockGroupPoint:
                #Create string version of coordinate
                PointText = str(BlockGroupPoint.X)+str(BlockGroupPoint.Y)
                #If coordinate is already in dictionary, append this BG's ID
                if PointText in BlockGroupVertexDictionary:
                    BlockGroupVertexDictionary[PointText].append(BlockGroupID)
                #If coordinate is not already in dictionary, create new list with this BG's ID
                else:
                    BlockGroupVertexDictionary[PointText] = [BlockGroupID]
del BlockGroupItem
del BlockGroupCursor


#Create dictionary of ID : [...,neighbors,...]
BlockGroupNeighborDictionary = {}
BlockGroupCursor = arcpy.SearchCursor(BlockGroups.shp)
BlockGroupDescription = arcpy.Describe(BlockGroups.shp)
BlockGroupShapeFieldName = BlockGroupDescription.ShapeFieldName
#For every block group
for BlockGroupItem in BlockGroupCursor:
    ListOfBlockGroupNeighbors = []
    BlockGroupID = BlockGroupItem.getValue("BKGPIDFP00")
    BlockGroupFeature = BlockGroupItem.getValue(BlockGroupShapeFieldName)
    for BlockGroupPart in BlockGroupFeature:
        #For every vertex
        for BlockGroupPoint in BlockGroupPart:
            #If it exists (and isnt interior hole signifier)...
            if BlockGroupPoint:
                #Create string version of coordinate
                PointText = str(BlockGroupPoint.X)+str(BlockGroupPoint.Y)
                if PointText in BlockGroupVertexDictionary:
                    #Get list of block groups that have this point as a vertex
                    NeighborIDList = BlockGroupVertexDictionary[PointText]
                    for NeighborID in NeighborIDList:
                        #Don't add if this BG already in list of neighbors
                        if NeighborID in ListOfBGNeighbors:
                            pass
                        #Add to list of neighbors (as long as its not itself)
                        elif NeighborID != BlockGroupID:
                            ListOfBGNeighbors.append(NeighborID)
    #Store list of neighbors in blockgroup object in dictionary
    BlockGroupNeighborDictionary[BlockGroupID] = ListOfBGNeighbors

del BlockGroupItem
del BlockGroupCursor
del BlockGroupVertexDictionary

事后看来,我意识到第二部分可以使用其他方法,而无需再次遍历shapefile。但这就是我使用的方法,即使一次用于数千个块组,它也能很好地工作。我还没有尝试过与整个美国做它,但它可以执行对整个状态。


2

一种替代方法是使用PostgreSQL和PostGIS。我已经问过几个有关如何在此站点上执行类似计算的问题:

我发现有一条陡峭的学习曲线可以弄清楚该软件的各个部分如何组合在一起,但是我发现它对于在大型矢量层上进行计算非常有用。我已经对数百万个多边形进行了一些最近的邻居计算,并且与ArcGIS相比已经非常快了。


1

只是一些评论... esri / ArcGIS方法当前使用字典来保存信息,但是核心计算是使用多边形邻域工具在C ++中完成的。该工具生成一个表,该表包含连续性信息以及可选属性(例如共享边界的长度)。如果要存储并随后重复使用该信息,则可以使用“生成空间权重矩阵”工具。您也可以在WeightsUtilities中使用此功能来生成带有连续性信息的字典[random access]:

contDict = polygonNeighborDict(inputFC, masterField, contiguityType = "ROOK")

其中inputFC =任意类型的面要素类,masterField是{“ ROOK”,“ QUEEN”}中整数和contiguityType的“唯一ID”字段。

有在ESRI努力跳过Python用户表格方面,直接进入到一个iterator这将使许多使用情况远远快。R中的PySAL和spdep软件包是绝佳的选择[请参阅radek的答案]。我认为您需要使用shapefile作为这些软件包中的数据格式,此格式与该线程输入格式一致。不确定它们如何处理重叠的多边形以及多边形内的多边形。生成SWM以及我描述的功能会将这些空间关系计为“ ROOK”和“ QUEEN”邻居。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.