编程语言
首页 > 编程语言> > Python和ArcGIS自动化制图完全指南(四):自动制图

Python和ArcGIS自动化制图完全指南(四):自动制图

作者:互联网

Python和ArcGIS自动化制图完全指南(四):自动制图

前言:在完成了《指南》第二章和第三章后,终于来到了制图。运用 python 和 arcpy 自动化完成 mxd 模板图层定义查询语句的自动更新、图框的自动居中、比例尺的校正、另存mxd等操作。

关键字:python、arcpy、mxd

PAGESIZE 字段是 定义查询 语句中使用的字段。作用是给每个 mxd 模板分配任务。

文章目录


banner4

1.限制 MXD 模板

《指南》第三章做的努力在这里得到了显现,在第三章中,我们根据大小、面积等规则给每个制图单位分配了合适的 mxd 模板,其信息就储存在 PAGESIZE 字段中,其值就是给该制图单位分配的模板的名字。

如下图所示,巴中市的 PAGESIZE 值是1180x900,表示给巴中市分配的制图模板是是1180x900.mxd.

对应关系

但是细心的读者可能已经发现了,目前为止,仅仅只是更新了 mappingIndex 图层的 PAGESIZE 字段,而每一个 mxd 模板都是没有限制的,它可以生成我们所有17个地级市的地图,这显然是不对的。

在这里我们需要对每一个 mxd 模板文件作出限制。

所以每一个 mxd 模板文件都要在 MappingIndex 图层中设置定义查询语句。而查询语句就是模板名称。

如下所示:

模板文件查询语句

如果还是不太明白,读者可以手动去给 MappingIndex 图层设置定义查询语句,看看会发生什么。

代码C1如下:

# -*- coding:utf-8 -*-
# Author: LiaoChenchen
from __future__ import absolute_import
from __future__ import unicode_literals
from __future__ import print_function
from __future__ import division
import arcpy
import os


"""__________________________________________________________________________"""
"""____________________________global_values_________________________________"""
# 地址
mxd_template = r"E:\doc\Scratch\tempMXDS"  # 模板文件位置
output_dir = r"E:\doc\Scratch\out"  # 输出位置
gdb_path = r"E:\doc\Scratch\arcpy指南.gdb"  # 数据库地址
# 重要常量
FIELD = "CITY" # 检索字段
MI = "MappingIndex" # 制图索引文件名称
SCALE = 200000 # 制图的比例尺

"""____________________________global_values_________________________________"""
"""__________________________________________________________________________"""
arcpy.env.overwriteOutput = True
arcpy.env.workspace = gdb_path

class MakeMXD(object):
    
    def __init__(self,mapdocument, layers_names, mappindex_name, query_fielf, scale=None):
        """
        :param mapdocument: {Object} MXD文件对象
        :param layers_names: {List} 需要设置定义查询语句图层的名称列表
        :param mappindex_name: {String} 索引图层名字;MappingIndex
        :param query_fielf: {String} 定义查询使用的字段名;CITY
        :param scale: {Int} 比例尺
        """
        self.mxd = mapdocument
        self.df = arcpy.mapping.ListDataFrames(self.mxd)[0]
        self.layers = layers_names
        self.index_name = mappindex_name
        self.field = query_fielf
        self.scale = scale
        
        self.mapindex_lyr = arcpy.mapping.ListLayers(self.mxd,self.index_name)[0]
        
        self.mapping_index_query()
        self.make_mxd()
        
        del self.mxd
       
    def mapping_index_query(self): # ▶注释一◀
        """
        给 MappingIndex 图层设置定义查询语句; PAGESIZE = '1080x700'
        :return:
        """
        map_path = self.mxd.filePath 
        name = os.path.splitext(os.path.basename(map_path))[0] # 1080x700 ▶注释二◀
        definition_query = ["PAGESIZE"," = ","'",name,"'"]
        self.size = name
        self.mapindex_lyr.definitionQuery = "".join(definition_query) # ▶注释三◀

▶注释一◀:目前这里我们只需要关注方法 mapping_index_query,上面的初始化方法等拉通梳理一遍后再看。

▶注释二◀:name 就是模板文件的名字(不包括后缀名)。

▶注释三◀:"".join(LIst) 比一般的字符串拼接速度快。


2.遍历制图单位

mxd 模板文件进行限制处理后,就需要把与该模板匹配的制图单位导出来了。

一个 mxd 模板通常对应了几个制图单位。

比如 1080x1300.mxd 这个模板对应着达州市、绵阳市、雅安市三个制图单位。所以我们要遍历循环来处理,说白话就是一个一个处理。

2.1定义查询

代码C2如下:

class MakeMXD(object):
    
   ... # ▶注释一◀
        
    def make_mxd(self):
        with arcpy.da.SearchCursor(self.mapindex_lyr, self.field) as cursor:
            for row in cursor: 
                name = row[0]
                self.define_query(name) # 定义查询
                self.center_scale(name) # 居中
                self.change_txt(name) # 修改文本
                self.label_query(name) # 标注查询语句
                arcpy.SelectLayerByAttribute_management(self.mapindex_lyr, "CLEAR_SELECTION") # 取消该图层的所有选择选择项目
                self.saveacopy(name) # 另存
    
    def define_query(self, value):
        """
        定义查询
        :param value: {String/Int/Float} 用于定义查询的值
        :return: None
        """
        for layer in self.layers:
            lyr = arcpy.mapping.ListLayers(self.mxd, layer)[0] 
            d_q = ['"',self.field,'"'," = ","'",value,"'"] # ▶注释二◀
            lyr.definitionQuery = "".join(d_q)
    

▶注释一◀:上文的代码。

▶注释二◀:self.field 即字段 CITY;定义查询语句例如:CITY = '达州市’

2.2图层居中

使该制图单位居于布局的中央

代码C3如下:

class MakeMXD(object):
    
    ...
    
    def center_scale(self, name):
        """
        使图框居中并设置比例尺
        :param name: {String/Int/Float} 用于查询语句的值
        :return: None
        """
        where_clause = "{} = '{}'".format(self.field, name)
        arcpy_slba = arcpy.SelectLayerByAttribute_management # ▶注释一◀
        arcpy_slba(self.mapindex_lyr, "NEW_SELECTION", where_clause)
        self.df.extent = self.mapindex_lyr.getSelectedExtent()
        if self.scale:
            self.df.scale = self.scale

▶注释一◀:选中当前的制图单位,然后居中。self.df.extent 指图框的范围。

2.3修改文本

将原地图标题如:XX市铁路交通分布演示草图 修改成 成都市铁路交通分布演示草图

class MakeMXD(object):
	...
    def change_txt(self, name):
        # 修改文本
        for elm in arcpy.mapping.ListLayoutElements(self.mxd, 'TEXT_ELEMENT'):
            if elm.text == "XX市铁路交通分布演示草图":
                elm.text = "XX市铁路交通分布演示草图".replace("XX市", name)

2.4标注查询语句

每一个地级市都设置了标注以显示名称,如果当前制图单位就是该地级市,可以不用再显示该地级市的标注。

如下图的中间的资阳市,就不显示标注:

标注显示效果

使特定的区域标注不显示,代码C4如下:

class MakeMXD(object):
    ...
    
    def label_query(self,name):
        # 设置标注的查询语句
        lyr_label = arcpy.mapping.ListLayers(self.mxd, "市级区域")[0]
        if lyr_label.supports("LABELCLASSES"):
            query = ["NOT","( ", self.field, "=", "'", name, "'", " )"] # NOT( CITY = '巴中市' )  # ▶注释一◀
            for lblClass in lyr_label.labelClasses:
                lblClass.SQLQuery = "".join(query)

▶注释一◀:查询语句如这样:NOT( CITY = ‘资阳市’)

表示不显示资阳市的标注。

2.5.另存 mxd

另存一个 mxd 文件,比如:达州市.mxd;绵阳市.mxd等。

class MakeMXD(object):
    ...
    
    def saveacopy(self, name):
        # 另存
        self.mxd.saveACopy(output_dir+'/'+name+'.mxd')
        print("Complete <name: {} size: {}> ".format(name, self.size))

3.完整代码

# -*- coding:utf-8 -*-
# ---------------------------------------------------------------------------
# Author: LiaoChenchen
# Created on: 2021/1/30 17:04
# Reference:
"""
Description: 自动制图
Usage:
"""
# ---------------------------------------------------------------------------
from __future__ import absolute_import
from __future__ import unicode_literals
from __future__ import print_function
from __future__ import division
import arcpy
import os

"""__________________________________________________________________________"""
"""____________________________global_values_________________________________"""
# 地址
mxd_template = r"E:\doc\Scratch\tempMXDS"  # 模板文件位置
output_dir = r"E:\doc\Scratch\out"  # 输出位置
gdb_path = r"E:\doc\Scratch\arcpy指南.gdb"  # 数据库地址
# 重要常量
FIELD = "CITY" # 检索字段
MI = "MappingIndex" # 制图索引文件名称
SCALE = 200000 # 制图的比例尺

"""____________________________global_values_________________________________"""
"""__________________________________________________________________________"""
arcpy.env.overwriteOutput = True
arcpy.env.workspace = gdb_path


class MakeMXD(object):
    
    def __init__(self,mapdocument, layers_names, mappindex_name, query_fielf, scale=None):
        """
        :param mapdocument: {Object} MXD文件对象
        :param layers_names: {List} 需要设置定义查询语句图层的名称列表
        :param mappindex_name: {String} 索引图层名字;MappingIndex
        :param query_fielf: {String} 定义查询使用的字段名;CITY
        :param scale: {Int} 比例尺
        """
        self.mxd = mapdocument
        self.df = arcpy.mapping.ListDataFrames(self.mxd)[0]
        self.layers = layers_names
        self.index_name = mappindex_name
        self.field = query_fielf
        self.scale = scale
        
        self.mapindex_lyr = arcpy.mapping.ListLayers(self.mxd,self.index_name)[0]
        
        self.mapping_index_query()
        self.make_mxd() # ▶注释一◀
        
        del self.mxd
    
    def mapping_index_query(self):
        """
        给 MappingIndex 图层设置定义查询语句; PAGESIZE = '1080x700'
        :return:
        """
        map_path = self.mxd.filePath
        name = os.path.splitext(os.path.basename(map_path))[0] # 1080x700
        definition_query = ["PAGESIZE"," = ","'",name,"'"]
        self.size = name
        self.mapindex_lyr.definitionQuery = "".join(definition_query)
        # self.mxd.saveACopy(r"E:\doc\Scratch\out\er.mxd")
    
    def make_mxd(self):
        with arcpy.da.SearchCursor(self.mapindex_lyr, self.field) as cursor: # ▶注释二◀
            for row in cursor: 
                name = row[0]
                self.define_query(name) # 定义查询
                self.center_scale(name) # 居中
                self.change_txt(name) # 修改文本
                self.label_query(name) # 标注查询语句
                arcpy.SelectLayerByAttribute_management(self.mapindex_lyr, "CLEAR_SELECTION") # 取消该图层的所有选择选择项目 # ▶注释三◀
                self.saveacopy(name) # 另存
    
    def define_query(self, value):
        """
        定义查询
        :param value: {String/Int/Float} 用于定义查询的值
        :return: None
        """
        for layer in self.layers:
            lyr = arcpy.mapping.ListLayers(self.mxd, layer)[0]
            d_q = ['"',self.field,'"'," = ","'",value,"'"]
            # lyr.definitionQuery = '"' + FIELD + '"' + " = " + "'" + value + "'"
            lyr.definitionQuery = "".join(d_q)
    
    def center_scale(self, name):
        """
        使图框居中并设置比例尺
        :param name: {String/Int/Float} 用于查询语句的值
        :return: None
        """
        where_clause = "{} = '{}'".format(self.field, name)
        arcpy_slba = arcpy.SelectLayerByAttribute_management
        arcpy_slba(self.mapindex_lyr, "NEW_SELECTION", where_clause)
        self.df.extent = self.mapindex_lyr.getSelectedExtent()
        if self.scale:
            self.df.scale = self.scale
    
    def change_txt(self, name):
        # 修改文本
        for elm in arcpy.mapping.ListLayoutElements(self.mxd, 'TEXT_ELEMENT'):
            if elm.text == "XX市铁路交通分布演示草图":
                elm.text = "XX市铁路交通分布演示草图".replace("XX市", name)
    
    def label_query(self,name):
        # 设置标注的查询语句
        lyr_label = arcpy.mapping.ListLayers(self.mxd, "市级区域")[0]
        if lyr_label.supports("LABELCLASSES"):
            query = ["NOT","( ", self.field, "=", "'", name, "'", " )"] # NOT( CITY = '巴中市' )
            for lblClass in lyr_label.labelClasses:
                lblClass.SQLQuery = "".join(query)
    
    def saveacopy(self, name):
        # 另存
        self.mxd.saveACopy(output_dir+'/'+name+'.mxd')
        print("Complete <name: {} size: {}> ".format(name, self.size))


# 运行窗口
if __name__ == '__main__':
    for a_mxd in [x for x in os.listdir(mxd_template) if ".mxd" or ".MXD" in x]:
        mxd_fullpath = os.path.join(mxd_template, a_mxd)
        mxd = arcpy.mapping.MapDocument(mxd_fullpath)
        MakeMXD(mxd, ["roads","railways","landuse","natural","buildings"], MI, FIELD, SCALE) #  ▶注释四◀

▶注释一◀:该方法必须放到 mapping_index_query 方法的后面,要先限制了 mxd 模板后再进行批量的导出 mxd 文件。

▶注释二◀:遍历一个 mxd 模板中所有可以制作的制图单位。

▶注释三◀:取消居中操作时选中的要素。

▶注释四◀:该类的第二的参数是一个列表,其中元素是需要进行定义查询语句的图层名称。比如:[“roads”,“railways”,“landuse”,“natural”,“buildings”]

《指南》第三章和第四章代码是可以合并到一起运行的,下载见最下面。


4.效果展示

运行视频:

部分导出的图片展示:

成都市

成都市

自贡市

自贡市

内江市

内江市


5.关于输出图片

关于如何批量导出地图,甚至是多进程批量导出地图的教程在公众号就能找到。欢迎关注微信公众号查看。

结尾


有疑问欢迎留言询问
原创不易,请勿私自转载
原创不易,请勿私自转载

了解更多文章,关注我的微信
小
小2

源文件下载(包括第三章和第四章合并后的源代码):

下载后解压即为 .py 后缀文件。

链接:https://pan.baidu.com/s/1dxAs9m8MotfeQXagjjJw8g
提取码:fm1z

一次性下载本指南全部资料请关注公众号并回复:自动化制图,以获取下载链接!

标签:__,name,Python,self,ArcGIS,query,mxd,arcpy,制图
来源: https://blog.csdn.net/rsLanZai/article/details/113717957