python在arcgis中案例开发(空间求交、连接以及excel数据导出)

arcgis开发python视频教程

https://edu.csdn.net/course/detail/25535

 

这是使用python写的第二工具了,可以说这门计算机语言也是初次接触,还好使用过c、 c#、JAVA等计算机语言,所以在使用python的使用也不是完全找不到北。

这是国庆之前做的一个项目。首先我来说一下我所用到的数据:山最高点(shapefile点数据)、山底座数据(shapefile面数据,属于一座山底部范围面)、一个区域内的地形数据(有喀斯特冲积平原、丘陵等等多种类型数据),该数据为shapefile面数据,地理国情数据(各种地类,水田,旱地之类的shapefile面数据)。

我最终需要提交的成果是按照山底座、地形数据、地理国情数据利用arcgis提供的求交函数,将结果按照地形分类统计该地形类具有的各种地理国情要素分别占有总面积分别为多少以excel数据表格导出。按照arcgis在arcpy提供的api接口可以将三种数据进行求交,但是需要注意求交的顺序,如果某些数据放在求交数组的最开始有可能会在求交的过程中丢失一些字段。我们在这里根据需求,需要山地形数据名称、地理国情要素编码字段用于地形分类、和所属哪种地类(根据地类代码进行归类)。按照山底座数据,山地形数据、国情数据一起求交之后可获取相交数据,然后再利用arcpy的游标,遍历求交后的属性shapefile数据,利用@shapearea标识求得对应属性面的面积,再利用python的字典类型,分别存储各种山地形数据。按照判断字典中是否将已经归类的地形数据类型加载到字典中的模式,即如果山地形数据地类没有,则以山地形名称作为键和该山地形内的某一个国情地类面积作为值添加对应的键值数据,如果已经加载有了之前的数据,那么只要重新提取之前某种山地类的数据,然后将新值与该值累加,删掉字典中对应的键值数据,又重新加载对应键值该山地类数据即可。整个过程编写代码,经过测试,开发的插件在求交方面运行比较花费时间,大约需要6到7分钟,而统计山地形数据部分并不需要花太多的时间。

该基于arcgis插件工具在运行中,由于本人是利用山地形是否有山顶点,然后将键值存放到python的字典中。所以再后来的统计过程中,将各种地理国情地类要素一一相加后,并没有和山底座面积相等。经过排查,是两方面的问题导致的。第一个原因是,地类代码没有完全列举,导致在统计的时候,程序将不在列举范围内的国情地类数据累计起来。第二个原因是,统计的山顶点数据中有些地形数据是没有相应的山顶点的,比如在水域数据中的水库地形就没有山顶点,导致在国情数据的统计中少了某一无山顶点类型的山地形数据。在插件工具开发过程中结合了数据,进行了国情要素代码的补充,以及地形数据的键值追加,最后才使得国情要素数据相加后,总面积与山底座数据面积相等。

在excel表格导出方面,是借助了python的xlwt类库,对excel进行表格的创建以及填写对应值。这里有遇到一些问题就是,一定要注意编码,我们这需要将编码转为UTF-8数据。为什么这么说,这会在字符串的判断中,如果按照其他计算机语言,C#、JAVA之类思想来判断是否字符相同,是极易出错的。再一个就是,使用阿拉伯数字插入到xlwt创建的表格似乎不报错,但是如果是ascil码,那就会报错了。好了说了这么多,我们来看一下,最后开发的工具界面,我相信这才是每一个从事地理信息的GISer最为迫切与激动的。

python在arcgis中案例开发(空间求交、连接以及excel数据导出)_第1张图片

以上的工具是利用之前讲解的pycharm在arcpy开发中arcgis工具箱打包进行了打包,具体代码在arcgis中打包流程,大家也可以查阅相关的资料。下面来说明一下提供的源代码。

首先是利用了空间连接函数SpatialJoin_analysis,判断某山地形类中所具有的山头(山顶点)数。然后在利用Intersect_analysis函数进行求交判断。最终在选择的【最终处理结果】文件夹中分别创建了shp、txt、xls文件夹,保存了处理的中间文件shapefile,统计的txt文件、以及表格数据xls文件。可参看如下图所示。

python在arcgis中案例开发(空间求交、连接以及excel数据导出)_第2张图片

其中txt的统计,本质就是讲xls的内容写到txt文件而已,无他。下面来分别看一下txt和xls都是些什么数据结果。

python在arcgis中案例开发(空间求交、连接以及excel数据导出)_第3张图片

python在arcgis中案例开发(空间求交、连接以及excel数据导出)_第4张图片

下面来看最为核心的部分,就是代码了。首先大家可以看一下,python中字典、类、函数,以及文件的创建与删除相关知识。以及在软件设计的时候,最好是将变化的部分给抽离出来,我这里将我们需要的地理国情要素地理代码作为变化部分给抽离出来,这样是方便对地理代码的配置,以及错误的检查,当然这里谈不上模式,只是编程中的一些经验积累而已。

然后下面的这一部分代码就是关于,arcgis中python代码如何打包的相应配置,这对于初学arcpy的伙伴而言,可以借鉴一下,当然更多参数方面的配置,大家还是可以结合之前的博客。好了,不管怎么说,大家可以细细去体会代码其中所要表达的意思。至此整个使用python方面在本次arcgis开发就说明完了,可能有些小伙伴还不知所云。可以私底下交流。

功能核心代码:

 

# coding:utf-8
import arcpy
import xlrd
import xlwt
import sys
import os
reload(sys)
sys.setdefaultencoding('utf8')

def debug(messages, txt):
    if messages is None:
        print txt
    else:
        messages.addMessage(txt)

class LandInfo:
    # 山头个数
    STCNT=0
    # 种植土地
    ZZTD=0
    # 林草覆盖
    LCFG=0
    # 房屋建筑区
    FWJZQ=0
    # 铁路与道路
    TLYDL=0
    # 构筑物
    GZW=0
    # 人工挖掘地
    RGWJD=0
    # 荒漠与裸露地
    FMYLLD=0
    #水域
    SY=0;

ZZTDListCode=['0100','0110','0120','0130','0131','0132','0133','0140','0150','0160','0170','0180','0190','0191','0192','0193','0210','0211','0212','0213','0220','0230','0240','0250','0260','0290','0291','0292','0293']#种植土地代码
LCFGListCode=['0300','0310','0311','0312','0313','0320','0321','0322','0323','0330','0340','0350','0360','0370','0380','0390','0391','0392','0393','03A0','03A1','03A2','03A3','03A4','03A9','0410','0411','0412','0413','0420','0421','0422','0424','0429'] # 林草覆盖代码
FWJZQListCode=['0500','0510','0511','0512','0520','0521','0522','0530','0540','0541','0542','0543','0544','0550']#房屋建筑区代码
TLYDLListCode=['0600','0610','0601']#铁路与道路代码
GZWListCode=['0700','0710','0711','0712','0713','0714','0715','0716','0717','0718','0719','0720','0721','0740','0750','0760','0761','0762','0763','0769','0770','0780','0790']#构筑物代码
RGWJDListCode=['0800','0810','0811','0812','0813','0814','0815','0819','0820','0821','0822','0829','0830','0831','0832','0833','0839','0890']#人工挖掘地代码
FMYLLDListCode=['0900','0910','0920','0930','0940','0950']#荒漠与裸露地代码
SYListCode=['1000','1001','1012','1050']#水域代码

#ZZTDListCode=['0210','0211','0212','0213','0220','0230','0240','0250','0260','0290','0291','0292','0293','0100','0110','0120']#种植土地
#LCFGListCode=['0310','0311','0312','0313','0320','0321','0330','0340','0360','0370','0380','0400','0410','0411','0412','0413','0420','0421','0422','0424','0429']# 林草覆盖代码
#FWJZQListCode=['0510','0511','0512','0520','0521','0522','0530','0540','0541','0542','0543','0544','0550','']

#计算面积
def caculateArea(exportData,flagSXLB,flagCode,flagArea,ZZTDListCode,LCFGListCode,FWJZQListCode,TLYDLListCode,
                 GZWListCode,RGWJDListCode,FMYLLDListCode,SYListCode):
    #有些地形并没有山头,重追加key
    if flagSXLB not in exportData.keys():
        myLandInfo = LandInfo()
        exportData[flagSXLB] = myLandInfo
        whichClassCode(myLandInfo, flagCode, flagArea, ZZTDListCode, LCFGListCode, FWJZQListCode, TLYDLListCode,
                       GZWListCode, RGWJDListCode, FMYLLDListCode, SYListCode)
        exportData[flagSXLB] = myLandInfo
    else:
      for key, value in exportData.items():
         if key==flagSXLB:
            myLandInfo=value
            del exportData[key]
            whichClassCode(myLandInfo,flagCode,flagArea,ZZTDListCode,LCFGListCode,FWJZQListCode,TLYDLListCode,
                 GZWListCode,RGWJDListCode,FMYLLDListCode,SYListCode)
            exportData[key]=myLandInfo
            break

#判断属于哪个地类
def whichClassCode(landInfo,flagCode,flagArea,ZZTDListCode,LCFGListCode,FWJZQListCode,TLYDLListCode,
                 GZWListCode,RGWJDListCode,FMYLLDListCode,SYListCode):
    if isSpecificLand(ZZTDListCode,flagCode):
        landInfo.ZZTD=landInfo.ZZTD+flagArea


    elif isSpecificLand(LCFGListCode,flagCode):
        landInfo.LCFG=landInfo.LCFG+flagArea

    elif isSpecificLand(FWJZQListCode,flagCode):
        landInfo.FWJZQ=landInfo.FWJZQ+flagArea

    elif isSpecificLand(TLYDLListCode,flagCode):
        landInfo.TLYDL=landInfo.TLYDL+flagArea

    elif isSpecificLand(GZWListCode,flagCode):
        landInfo.GZW=landInfo.GZW+flagArea

    elif isSpecificLand(RGWJDListCode,flagCode):
        landInfo.RGWJD=landInfo.RGWJD+flagArea

    elif isSpecificLand(FMYLLDListCode,flagCode):
        landInfo.FMYLLD=landInfo.FMYLLD+flagArea

    elif isSpecificLand(SYListCode,flagCode):
        landInfo.SY=landInfo.SY+flagArea
    else:
        0

#删除某一目录下的所有文件
def delDirAllFiles(dir):
    if os.path.exists(dir):
        files = list_all_files(dir)
        for tempfile in files:
            os.remove(tempfile)

def list_all_files(rootdir):
    _files = []
    list = os.listdir(rootdir)  # 列出文件夹下所有的目录与文件
    for i in range(0, len(list)):
        path = os.path.join(rootdir, list[i])
        if os.path.isdir(path):
            _files.extend(list_all_files(path))
        if os.path.isfile(path):
            _files.append(path)
    return _files

def text_create(fileName,dir):
    delDirAllFiles(dir)
    full_path=dir+fileName+".txt"
    file = open(full_path, 'w')
    file.close()
    return full_path

#判断是否为特定地类
def isSpecificLand(listDLCode,flagCode):
       isSpecific=False
       for tmpCode in listDLCode:
         if tmpCode==flagCode:
             isSpecific=True
             break
       return isSpecific


def insertExcelData(strSXLB,row,worksheet,landInfo):
    worksheet.write(row, 0, strSXLB.encode("utf-8"))  # 山形类别
    areaTotal=landInfo.ZZTD+landInfo.LCFG+landInfo.FWJZQ+landInfo.TLYDL+landInfo.GZW+landInfo.RGWJD+landInfo.FMYLLD+landInfo.SY
    worksheet.write(row,1,landInfo.STCNT)#山头数
    worksheet.write(row,2, areaTotal)    #山头面积
    worksheet.write(row,3, areaTotal)#地类总面积
    worksheet.write(row,4, landInfo.ZZTD)#种植土地
    worksheet.write(row,5, landInfo.LCFG)#林草覆盖
    worksheet.write(row,6, landInfo.FWJZQ)#房屋建筑区
    worksheet.write(row,7, landInfo.TLYDL)#铁路与道路
    worksheet.write(row,8, landInfo.GZW)#构筑物
    worksheet.write(row,9, landInfo.RGWJD)#人工堆掘地
    worksheet.write(row,10, landInfo.FMYLLD)#荒漠与裸露地表
    worksheet.write(row,11, landInfo.SY)#水域



def caculteFinalArea(landInfo,totalLandinfo):
    totalLandinfo.STCNT=totalLandinfo.STCNT+landInfo.STCNT
    totalLandinfo.ZZTD=totalLandinfo.ZZTD+landInfo.ZZTD
    totalLandinfo.LCFG=totalLandinfo.LCFG+landInfo.LCFG
    totalLandinfo.FWJZQ=totalLandinfo.FWJZQ+landInfo.FWJZQ
    totalLandinfo.TLYDL=totalLandinfo.TLYDL+landInfo.TLYDL
    totalLandinfo.GZW=totalLandinfo.GZW+landInfo.GZW
    totalLandinfo.RGWJD=totalLandinfo.RGWJD+landInfo.RGWJD
    totalLandinfo.FMYLLD=totalLandinfo.FMYLLD+landInfo.FMYLLD
    totalLandinfo.SY=totalLandinfo.SY+landInfo.SY

def mkdir(path):
    #path = path.strip()
    #path = path.rstrip("\\")

    if not os.path.isdir(path):
        print "xxx"
    isExists = os.path.exists(path)
    if not isExists:
        os.makedirs(path)
        print path + ' 创建成功'
        return True
    else:
        print path + ' 目录已存在'
        return False







def do(stdData,stmData,sxData,gqData,rootPath):
    messages = None
    debug(messages,"get root folder is "+rootPath)
    shpRoot = rootPath + "/shp/"
    xlsRootPath = rootPath + "/xls/"
    txtRootPath = rootPath + "/txt/"

    debug(messages,"get shpRoot folder is "+shpRoot)
    debug(messages,"get xlsRootPath folder is "+xlsRootPath)
    debug(messages,"get txtRootPath folder is "+txtRootPath)

    mkdir(shpRoot)
    mkdir(xlsRootPath)
    mkdir(txtRootPath)

    insertGQShp = shpRoot + "insertGQshp.shp"
    insertSTShp = shpRoot + "insertSTshp.shp"

    delDirAllFiles(shpRoot)
    insertGQShp = shpRoot + "insertGQshp.shp"
    insertSTShp = shpRoot + "insertSTshp.shp"

    insertSTSXShp = shpRoot + "insertSTSXshp.shp"
    #删掉txt目录下所有的文件
    delDirAllFiles(txtRootPath)
    txtPath = text_create("result", txtRootPath)
    #输出数据
    exportData = {}

    #连接求交
    arcpy.SpatialJoin_analysis(sxData,stdData,insertSTShp,
                               join_operation="JOIN_ONE_TO_ONE",
                               join_type="KEEP_COMMON",
                               match_option="INTERSECT",
                               search_radius="#",
                               distance_field_name="#")

    cursor = arcpy.da.SearchCursor(insertSTShp, ['NAME','Join_Count'])
    for row in cursor:
        flagSXLB = row[0]
        flagSXLB=flagSXLB.encode("utf-8")

        flagCNT = row[1]
        if flagSXLB not in exportData.keys():
            landInfo=LandInfo()
            landInfo.STCNT=flagCNT
            exportData[flagSXLB]=landInfo
        else:
            tmpLandInfo=exportData[flagSXLB]
            del exportData[flagSXLB]
            tmpLandInfo.STCNT=tmpLandInfo.STCNT+flagCNT
            exportData[flagSXLB]=tmpLandInfo

    f=file(txtPath,"a+")# 追加的方式读写
    f.write("------------------------山头点个数统计开始------------\n")
    for key,value in exportData.items():
        #print "山形类型:"+key
        f.write("山形类型::" + str(key) + "\n")
        tmpSTCNT=str(value.STCNT)
        #print "山头数量:"+tmpSTCNT
        f.write("山头数量:" + tmpSTCNT + "\n")

    f.write("------------------------山头点个数统计结束------------\n")
    f.close()



    arcpy.Intersect_analysis([stmData, gqData,sxData], insertGQShp, "ALL", "#", "INPUT")#山头面数据和国情数据
    cursor=arcpy.da.SearchCursor(insertGQShp, ["NAME", 'CC', "SHAPE@AREA", "OID@"])

    for row in cursor:
        flagSXLB = row[0]
        flagSXLB = flagSXLB.encode("utf-8")
        flagCode = row[1]
        flagArea = row[2]

        caculateArea(exportData,flagSXLB,flagCode,flagArea,ZZTDListCode,LCFGListCode,FWJZQListCode,TLYDLListCode,
                     GZWListCode,RGWJDListCode,FMYLLDListCode,SYListCode)

    f=file(txtPath,"a+")# 追加的方式读写
    f.write("------------------------山头面积统计开始------------\n")
    for key,value in exportData.items():
        kLandInfo=value
        f.write(str(key)+"种植土地面积为:" + str(kLandInfo.ZZTD) + "\n")
        f.write(str(key)+"林草覆盖面积为:" + str(kLandInfo.LCFG) + "\n")
        f.write(str(key)+"房屋建筑区面积为:" + str(kLandInfo.FWJZQ) + "\n")
        f.write(str(key)+"铁路与道路面积为:" + str(kLandInfo.TLYDL) + "\n")
        f.write(str(key)+"构筑物面积为:" + str(kLandInfo.GZW) + "\n")
        f.write(str(key)+"人工挖掘地面积为:" + str(kLandInfo.RGWJD) + "\n")
        f.write(str(key)+"荒漠与裸露地面积为:" + str(kLandInfo.FMYLLD) + "\n")
        f.write(str(key)+"水域面积为:" + str(kLandInfo.SY) + "\n")
        f.write("------------------------------------\n")
    f.write("------------------------山头面积统计结束------------\n")
    f.close()

    workbook = xlwt.Workbook(encoding='utf-8')
    worksheet = workbook.add_sheet('My Sheet')
    #表头部分
    worksheet.write(0,1,"山头数".encode("utf-8"))
    worksheet.write(0,2,"山头面积".encode("utf-8"))
    worksheet.write(0,3,"地类总面积".encode("utf-8"))
    worksheet.write(0,4,"种植土地".encode("utf-8"))
    worksheet.write(0,5,"林草覆盖".encode("utf-8"))
    worksheet.write(0,6,"房屋建筑区".encode("utf-8"))
    worksheet.write(0,7,"铁路与道路".encode("utf-8"))
    worksheet.write(0,8,"构筑物".encode("utf-8"))
    worksheet.write(0,9,"人工挖掘地".encode("utf-8"))
    worksheet.write(0,10,"荒漠与裸露地表".encode("utf-8"))
    worksheet.write(0,11,"水域".encode("utf-8"))

    index=1

    #最后的面积统计
    totalLandinfo=LandInfo()
    for key,value in exportData.items():
        mLandInfo=value
        strSXLB=str(key)
        strSXLB=strSXLB.encode("utf-8")
        insertExcelData(strSXLB,index,worksheet,mLandInfo)
        caculteFinalArea(mLandInfo,totalLandinfo)
        index=index+1

    #插入最后一行统计数据
    insertExcelData("合计",index,worksheet,totalLandinfo)

    #删除xls根目录下所有的文件
    delDirAllFiles(xlsRootPath)
    workbook.save(xlsRootPath+'result.xls')

打包代码

# coding:gbk
import arcpy

from StatisticsAreaClass import do


class Toolbox(object):
    def __init__(self):
        """Define the toolbox (the name of the toolbox is the name of the
        .pyt file)."""
        self.label = "Toolbox"
        self.alias = ""

        # List of tool classes associated with this toolbox
        self.tools = [Tool]


class Tool(object):
    def __init__(self):
        """Define the tool (tool name is the name of the class)."""
        self.label = "xxxx大数据中心"
        self.description = "xxxx大数据中心"
        self.canRunInBackground = False

    def getParameterInfo(self):
        """Define parameter definitions"""
        stdPath = arcpy.Parameter(
            displayName="山顶点数据",
            name="stdPath",
            datatype="GPFeatureLayer",
            parameterType="Required",
            direction="Input"
        )
        stfwxPath = arcpy.Parameter(
            displayName="山头范围线",
            name="stfwxPath",
            datatype="GPFeatureLayer",
            parameterType="Required",
            direction="Input"
        )
        sxPath = arcpy.Parameter(
            displayName="山形图层",
            name="sxPath",
            datatype="GPFeatureLayer",
            parameterType="Required",
            direction="Input"
        )
        gqPath = arcpy.Parameter(
            displayName="国情要素图层",
            name="gqPath",
            datatype="GPFeatureLayer",
            parameterType="Required",
            direction="Input"
        )

        resultFolder = arcpy.Parameter(
            displayName="最终处理结果",
            name="resultFolder",
            datatype="Folder",
            parameterType="Required",
            direction="Input"
        )
        #params = None
        params = [stdPath, stfwxPath, sxPath, gqPath, resultFolder]
        return params

    def isLicensed(self):
        """Set whether tool is licensed to execute."""
        return True

    def updateParameters(self, parameters):
        """Modify the values and properties of parameters before internal
        validation is performed.  This method is called whenever a parameter
        has been changed."""
        return

    def updateMessages(self, parameters):
        """Modify the messages created by internal validation for each tool
        parameter.  This method is called after internal validation."""
        return

    def execute(self, parameters, messages):
        """The source code of the tool."""
        stdPath = parameters[0].valueAsText
        stfwxPath = parameters[1].valueAsText
        sxPath = parameters[2].valueAsText
        gqPath = parameters[3].valueAsText
        resultFolder = parameters[4].valueAsText
        do(stdPath, stfwxPath, sxPath, gqPath, resultFolder)
        return


                                                                            更多内容,请关注公众号

                                                                 

你可能感兴趣的:(插件开发,arcgis,python)