【Arcpy】基于相交关系汇集源图层属性值到目标图层目标字段中

# coding: utf-8

import arcpy
from arcpy import env
import xlrd, random, tempfile, shutil, os

"""
DESC: 
    本段代码用于处理两个图层间属性值关联汇总问题,
    即把源图层的源属性值整合、汇总后填写目标图层的目标属性中。
    至具体包括:
    1、将与目标要素相交的源要素的指定字段值拼接为字符串填写至目标字段;
    2、汇总与目标要素、源要素相交面积并汇总相交面积填写至目标字段;
    3、分类汇总目标要素及源要素相交部分,填写至目标字段。

LOG:
    [0315] 需要解决源、目的图层字段同名后地理处理重命名的问题
    [0316] 已解决,编写__renameField对重名字段做重命名处理
"""


class FieldValueTransfer:

    def __init__(self, workspace, xlsx_path):
        env.workspace = workspace
        env.overwriteOutput = True
        self.xlsx_path = xlsx_path
        
        self.temp_dir = tempfile.mkdtemp()
        self.temp_gdb = os.path.join(self.temp_dir, "temp.gdb")
        arcpy.CreateFileGDB_management(self.temp_dir, "temp.gdb")


    def deleteTempDir(self):
        """
            删除临时文件夹
        """
        shutil.rmtree(self.temp_dir)


    def __layerChecker(self, layer_name):
        """
            检查图层是否存在
            :param layer_name: 图层名
        """
        if not arcpy.Exists(layer_name):
            print "Layer " + layer_name + " not exists!"
            return False
        else:
            return True


    def __fieldCheck(self, layer_name, field_name):
        """
            检查字段是否存在
            :param layer_name: 图层名
            :param field_name: 属性名
        """
        fields_name_list = [f.name for f in arcpy.ListFields(layer_name)]
        if field_name not in fields_name_list:
            print "Field " + field_to + " not exists in Layer " + layer_name + "!"
            return False
        else:
            return True


    def __renameField(self, layer_name, field_name):
        """
            如图层存在字段,对字段重命名
            :param layer: 对比图层
            :param field_name: 原属性名
        """
        field_name_new = field_name
        fields_name_list = [f.name for f in arcpy.ListFields(layer_name)]
        if field_name_new not in fields_name_list:
            return field_name_new
        
        # 生成器
        ext_list = ["_1", "2", "_13", "_14", "_15", "_16", "_17", "_18", "_19"]
        generator_ext = (e for e in ext_list)

        # 逐个判断重命名后的字段是否重名
        while True:
            try:
                ext = next(generator_ext)
                field_name_new += ext
                if field_name_new not in fields_name_list:
                    return field_name_new
            except StopInteration:
                break

        return None


    def sumupFieldValue(self, layer_to, field_to, layer_from, field_from):
        """
            汇总相交要素属性,写入目标要素
            :param layer_to: 写入属性值的图层名
            :param field_to: 写入属性值的字段名
            :param layer_from: 属性值的来源图层
        """
        try:
            # 判断图层/字段是否存在
            if ((not self.__layerChecker(layer_to)) or 
                (not self.__layerChecker(layer_from)) or 
                (not self.__fieldCheck(layer_to, field_to)) or 
                (not self.__fieldCheck(layer_from, field_from))):
                return

            layer_name = "lyr" + str(random.random()*100000)
            arcpy.MakeFeatureLayer_management(layer_from, layer_name)

            # 查找相交并汇总属性
            with arcpy.da.UpdateCursor(layer_to, ("SHAPE@", field_to)) as updateCursor:
                for row_to in updateCursor:
                    geometry_to = row_to[0]
                    arcpy.SelectLayerByLocation_management(layer_name, "INTERSECT", geometry_to)

                    field_value = ""
                    with arcpy.da.SearchCursor(layer_name, ("SHAPE@", field_from)) as searchCursor:
                        for row_from in searchCursor:
                            # 过滤仅边界相邻的
                            geometry_from = row_from[0]
                            if (not geometry_from and
                                not geometry_to and 
                                geometry_to.touches(geometry_from)):
                                continue

                            # 不为空且与其他要素属性值不重复
                            if row_from[1] and len(str(row_from[1]).strip()) > 0 and (str(row_from[1]).strip() not in field_value):
                                field_value += (str(row_from[1]).strip() + unicode("、", "utf-8"))  # unicode编码,解决python中文字符无法连接问题

                        if len(field_value) > 0:
                            field_value = field_value[0:-1]
                            # print field_value
                            row_to[1] = field_value
                            updateCursor.updateRow(row_to)

            arcpy.Delete_management(layer_name)
        except Exception as e:
            print "--- sumupFieldValue ---"
            print arcpy.GetMessages()
            print e


    def sumupIntersectArea(self, layer_to, field_to, layer_from):
        """
            汇总相交图形面积
            :param layer_to: 写入属性值的图层名
            :param field_to: 写入属性值的字段名
            :param layer_from: 属性值的来源图层
        """
        try:
            # 判断图层/字段是否存在
            if ((not self.__layerChecker(layer_to)) or 
                (not self.__layerChecker(layer_from)) or 
                (not self.__fieldCheck(layer_to, field_to))):
                return

            # 相交分析
            output_name = self.temp_gdb + "/" + layer_to + "_" + layer_from + "_Intersect"
            if not arcpy.Exists(output_name):
                arcpy.Intersect_analysis([layer_to, layer_from], output_name, "ALL", "", "")

            # 统计分析
            intable = output_name
            outtable = output_name + "_Statistic"
            statsFields = [["Shape_Area", "SUM"]]
            casefield = "FID_" +  layer_to

            arcpy.Statistics_analysis(intable, outtable, statsFields, casefield)

            # 更新属性值
            with arcpy.da.SearchCursor(outtable, ("FID_" + layer_to, "SUM_Shape_Area")) as searchCursor:
                for srow in searchCursor:
                    expression = "OBJECTID=" + str(srow[0])
                    with arcpy.da.UpdateCursor(layer_to, (field_to), where_clause = expression) as updateCursor:
                        for row_to in updateCursor:
                            row_to[0] = round(srow[1], 2)
                            updateCursor.updateRow(row_to)

        except Exception as e:
            print "--- sumupIntersectArea ---"
            print arcpy.GetMessages()
            print e


    def classifyAndSumupIntersectArea(self, layer_to, field_to, layer_from, field_from):
        """
            分类汇总相交图形面积
            :param layer_to: 写入属性值的图层名
            :param field_to: 写入属性值的字段名
            :param layer_from: 属性值的来源图层
            :param field_from: 属性值的来源字段
        """
        try:
            # 判断图层/字段是否存在
            if ((not self.__layerChecker(layer_to)) or 
                (not self.__layerChecker(layer_from)) or 
                (not self.__fieldCheck(layer_to, field_to)) or 
                (not self.__fieldCheck(layer_from, field_from))):
                return

            # 相交分析
            output_name = self.temp_gdb + "/" + layer_to + "_" + layer_from + "_Intersect"
            if not arcpy.Exists(output_name):
                arcpy.Intersect_analysis([layer_to, layer_from], output_name, "ALL", "", "")

            # 解决数据源属性名在目标土层中已定义问题
            field_from = self.__renameField(layer_to, field_from)
            if not field_from:
                print "Field name " + field_from + " is duplicated in Layer " + layer_to
                return 

            # 统计分析
            intable = output_name
            outtable = output_name + "_Statistic"
            statsFields = [["Shape_Area", "SUM"]]
            casefield = ["FID_" +  layer_to, field_from]

            arcpy.Statistics_analysis(intable, outtable, statsFields, casefield)

            # # 更新属性值
            # with arcpy.da.UpdateCursor(layer_to, ("OBJECTID", field_to)) as updateCursor:
            #     for urow in updateCursor:
            #         expression = "FID_" + layer_to + "=" + str(urow[0])

            #         # 每个要素对应的多行统计结果
            #         with arcpy.da.SearchCursor(outtable, (field_from, "SUM_Shape_Area"), where_clause=expression) as searchCursor:
            #             field_value = ""
            #             for srow in searchCursor:
            #                 srow_field_name = srow[0]
            #                 srow_area = srow[1]
            #                 field_value += (srow_field_name + ": " + str(round(srow_area, 2)) + "㎡、")

            #             # 如果存在多行结果
            #             if len(field_value) > 0:
            #                 field_value = field_value[0:-1][0:255]
            #                 urow[1] = field_value
            #                 updateCursor.updateRow(urow)

            # 更新属性值
            field_value_dict = {}   # 使用字典拼接结果字符串
            with arcpy.da.SearchCursor(outtable, ("FID_" + layer_to, field_from, "SUM_Shape_Area")) as searchCursor:
                for srow in searchCursor:
                    srow_fid = srow[0]
                    srow_field_name = srow[1]
                    srow_area = srow[2]
                    
                    # print srow_field_name
                    if srow_field_name is not None:
                        field_value = (srow_field_name + ": " + str(round(srow_area, 2)) + unicode("㎡、", "utf-8"))
                    else:
                        field_value = ("Null: " + str(round(srow_area, 2)) + unicode("㎡、", "utf-8"))
                    
                    if str(srow_fid) not in field_value_dict:
                        field_value_dict[str(srow_fid)] = field_value
                    else:
                        field_value_dict[str(srow_fid)] += field_value

            for key, val in field_value_dict.items():
                expression = "OBJECTID=" + key
                with arcpy.da.UpdateCursor(layer_to, (field_to), where_clause=expression) as updateCursor:
                    for urow in updateCursor:
                        # print val[0:-1]
                        urow[0] = val[0:-1] # 去掉最后的符号
                        updateCursor.updateRow(urow)

        except Exception as e:
            print "--- classifyAndSumupIntersectArea ---"
            print arcpy.GetMessages()
            print e
    

if __name__ == "__main__":
    try:
        workspace = unicode("C:/Users/may/Desktop/xxxxxx/xxxxxx.gdb", "utf-8")
        xlsx_path = unicode("C:/Users/may/Desktop/xxxxxx/mapper/fieldMapper0316.xlsx", "utf-8")
        # xlsx_path = unicode("C:/Users/may/Desktop/xxxxxx/mapper/fieldMapper0315.xlsx", "utf-8")
        fieldValueTransfer = FieldValueTransfer(workspace, xlsx_path)

        workbook = xlrd.open_workbook(fieldValueTransfer.xlsx_path)
        sheet = workbook.sheet_by_index(0)
        rows = sheet.nrows

        for i in range(1, rows):
            layer_to = sheet.cell(i, 0).value
            field_to = sheet.cell(i, 1).value
            layer_from = sheet.cell(i, 2).value
            field_from = sheet.cell(i, 3).value
            _type = sheet.cell(i, 4).value

            print "\n" + "*" * 20 + str(i) + "*" * 20
            print layer_to, field_to, layer_from, field_from

            if _type == 1.0:
                fieldValueTransfer.sumupFieldValue(layer_to, field_to, layer_from, field_from)
            elif _type == 2.0:
                fieldValueTransfer.classifyAndSumupIntersectArea(layer_to, field_to, layer_from, field_from)
            elif _type == 3.0:
                fieldValueTransfer.sumupIntersectArea(layer_to, field_to, layer_from)
            else:
                continue
        fieldValueTransfer.deleteTempDir()
        print "Done!"

    except Exception as e:
        print e


        

你可能感兴趣的:(ArcGIS,WebGIS,Arcpy,python,开发语言,arcgis)