欧丁丁,张 琪,刘世好,曹 虹,文 汲,周 维,尹祺卿,胡春香
(1.国家林业和草原局中南调查规划设计院,长沙 410014; 2.中南林业科技大学,长沙 410004)
林业制图是林业工作中的一项基础性工作,也是向大众直观展示林业调查、规划设计成果的重要途径[1-3],但传统的林业制图工作量大、技术要求高[4-7],特别具体到每个小班的现状和设计情况的图件制作,图件数量多,制图时间周期长,严重挤占项目时间。基于此,部分学者开始思考如何进行自动出图,并寻求适合特定条件下的自动出图方法。有的学者对单个小班的出图方法进行了研究,如:李霄等[8]基于Arcgis内置站点包ArcPy对违法矿山小班的现状进行自动出图方法研究,通过建立地图模板,遍历要素的每一个小班,使用动态文本对小班现状进行描述,最后导出图片,相比人工制图节省了大量的时间;阳昭[9]利用ArcGIS10.2的数据驱动页面,对低效林改造作业设计项目小班进行了自动分幅出图,实现了林业项目专题图的自动分幅出图。虽然已有不少有关ArcGIS自动出图的研究,但目前依然存在尚未解决的问题,如:在对多个小班自动出图的时候,如何避免图面框线与小班界线相交的问题、如何在出图时自动生成小班属性简表,以及同时一张已定纸张大小的范围能内包含最多的小班,从而节约纸张并且保持出图界面的美观等。本研究将通过结合ArcGIS内置站点包ArcPy和基于Excel的编程语言VBA解决上述问题,为林业自动化制图提供一种新的方法。
长顺县位于贵州省中南部,地处乌蒙山—苗岭生态屏障中段及乌江生态保护带中段,距省城贵阳市 87 km,据黔南自治州首府都匀市178 km,北靠贵阳市及平坝县,东邻长顺县,东南抵罗甸县,西南抵紫云苗族布依族自治县,西北抵安顺,东北抵贵阳市花溪;属湿润地区的亚热带季风气候,具有山岳型气候特点,冬春干燥,夏季湿润,四季分明。
近年来,长顺县积极推进国家储备林建设进度,已完成国家储备林建设一期项目可研,为全面完成贵州省国家储备林建设任务,科学经营和精准提升长顺县森林质量,增强区域内优质木材和生态产品的供应能力,巩固全县脱贫攻坚成果,深入论证长顺县国家储备林二期建设的必要性和可行性,长顺县林业局委托国家林业和草原局中南调查规划设计院编制《长顺县国家储备林二期建设项目可行性研究报告》。
数据来源于《长顺县国家储备林二期建设项目可行性研究报告》中划定的国家储备林建设小班。
1)ArcPy简介
ArcPy是一个以成功的 arcgisscripting 模块为基础并继承了 arcgisscripting 功能进而构建而成的站点包。目的是为以实用高效的方式通过 Python 执行地理数据分析、数据转换、数据管理和地图自动化创建基础。在ArcGIS的内置Python窗口中可以导入AArcPy站点包,进行数据的处理和分析。
使用数据驱动框架进行小班自动出图的关键是小班索引图层的创建,而ArcGIS中自带的索引图层创建功能难以满足出图要求,如按乡镇出图、边框与小班界不相交等。本研究将使用ArcPy进行小班索引图层的创建。
2)VBA简介
VBA(Visual Basic for Applications)是微软公司基于VB(Visual Basic)和office开发的自动化办公语言,VBA与VB编程语言的编程语法、开发机制和语言结构几乎一致,主要能用来扩展office的应用程序功能。
小班属性是小班设计图的重要组成部分,用于补充每个小班的属性及设计情况,通常可以用动态文本和小班简表的方式实现。对于一个小班的小班设计图而言,动态文本的实现方式相对简单,文本布局可根据需求自行排版,但是不能同时显示多个小班的属性信息。使用小班简表可同时显示多个小班的属性信息,但是对于自动出图而言,如何根据小班属性表桉小班索引图层定制的分幅自动生成小班简表是需要解决的重点及难点。本研究基于Excel使用VBA编程语言自动生成小班简表。
1)图层的添加。根据国家储备林相关技术规程,使用地形图作为底图,行政界线包括乡镇界和村界,在ArcMAP中依次添加地形图、乡镇界线、村界线、设计小班和索引图层。其中:设计小班和索引图层可为任意面图层,并将其图层名称分别更改为XB_JX和qdfz,同时为qdfz添加tplj字段和page字段。
2)数据驱动页面设置。在设置数据驱动页面中,勾选启用数据驱动页面数据框为默认图层,索引图层为qdfz图层,名称字段为page,排序字段为OBJECTID字段,调整当前比例为1∶10000,然后在范围中选择居中并保持当前比例。
3)页面设置。转为布局视图,并在文件—页面和打印设置中将纸张设置为A3横向,将数据框调整为合适大小,添加公里格网,再依次添加指北针、图例和比例尺,然后插入图片,将图片锚点设置为左上定点,并能进行移动和缩放,保证图片左上顶点与数据框顶点重合,图片大小以100%缩放时能看清图片中的文字为宜,然后右击图片查看属性,在源中选择数据驱动页面的简单路径,索引图层的字段设置为tplj。
然后为小班设计图添加动态标题,在插入中选择插入标题,代码如图1所示。
图1 小班设计图标题动态文本
4)页面定义查询。右击XB_JX,在定义查询选择页面定义,启用页面定义查询页面名称字段选择page,选择匹配。这样可保证在出图时,XB_JX图层只显示与数据驱动页面中page字段属性一样的小班。
5)小班标注。利用小班标注可以将小班界与属性信息相关联,本研究使用分子式进行小班信息的标注,其中:[bz]为小班号字段。如图2所示。
图2 标注样式
6)图层范围指示器。图层范围指示器可用于标识出图范围在总范围中的位置,本研究中还可用来检验出图效果(通过查看小班界线与指示范围是否相交来检验)。实现步骤为:插入数据框、添加小班数据、添加乡镇界、更改数据显示样式、右击新建的图层选择范围指示器选中图层并勾选使用简单范围。
设置完后将出图模板保存备用。
小班索引图层创建的关键是在1∶10000的比例尺下,将完全被A3纸覆盖的小班的page字段命名为相同的值。
1)获取属性表。在ArcPy中对小班的属性进行查询分析有两种方法:一种是使用游标依次进行遍历,另一种是将属性表导出为数组进行操作,本步骤只能选择第一种方式进行,所需要的小班属性包括小班坐标点、OBJECTID、page字段和乡镇字段,在使用游标遍历小班并保存所需的属性信息至数组后,对数组按照乡镇名称进行排序,方便后面按乡镇进行位置距离的搜索。实现代码如图3所示。
图3 获取属性表代码
2)赋值属性表。将所需要的属性提取出来后,要对page字段进行赋值,对属性表中的每一行,提取每个小班的四至坐标,如果乡镇名字相同,则进行遍历,遍历分两次进行,第一层是遍历每一个小班,第二层是针对这一个小班对比乡镇内其他小班的四至坐标与本小班的四至坐标的x方向实际距离的最大值和y方向上实际距离的最大值,如果此距离均小于A3纸张的实际距离的某个比例(因为小班范围不会铺满整张A3纸),则将其page字段赋相同的值,同时,进行检测,若添加了一个新的小班后,整体的范围超过了A3纸张的实际距离的某个比例,则不赋同样的值。实现代码如下:
def fz_sxb(sxb_list,cs):
i =0
ss_list=[]
xz_list=[]
for shp in sxb_list:
x1=[]
y1=[]
if shp[2] is None:
i+=1
x_z=[]
y_z=[]
if shp[3] not in xz_list:
i=1
xz_list.append(shp[3])
for plts in shp[0]:
x1.append(plts[0])
y1.append(plts[1])
shp[2]=i
x_z=x1
y_z=y1
for row1 in sxb_list:
x2=[]
y2=[]
if row1[2] is None and row1[3]==shp[3]:
for pltss in row1[0]:
x2.append(pltss[0])
y2.append(pltss[1])
x_z=x_z+x2
y_z=y_z+y2
x_min=min(x_z)
x_max=max(x_z)
y_min=min(y_z)
y_max=max(y_z)
x_jl=x_max-x_min
y_jl=y_max-y_min
if x_jl<=(4200*float(cs)) and y_jl <=(2970*float(cs)):
row1[2]=i
else:
for x in x2:
x_z.remove(x)
for y in y2:
y_z.remove(y)
ss_list.append((shp[1],shp[2]))
return ss_list
CS表示出图范围相对A3纸的比例。
3)赋值字段。进行属性表(数组)赋值后,需将结果赋值给小班属性表的字段,本步骤使用游标进行赋值。实现代码如下:
def fz_zd(fc,field,xzfield,cs):
k_list=fz_sxb(hq_sxb(fc,field,xzfield),cs)
with arcpy.da.UpdateCursor(fc,["OID@",field,xzfield])as cursor:
for row in cursor:
for k in k_list:
if row[0]==k[0]:
row[1]=str(row[2])+"_"+str(k[1])
cursor.updateRow(row)
完成上述步骤后,将字段赋值后的小班按照page字段进行小班融合,生成数据驱动页面的索引图层。本研究将此步骤放到自动出图方法中实现(3.4节)。
1)转为Excel。将赋值字段后的小班属性表,使用转换工具箱中的表转Excel转成Excel。
2)数据清理与格式设置。导出的属性表中,包含了一些不需要显示在小班简表中的信息,需手动进行删除,同时,行列的宽度和长度、字体格式和大小等需手动进行调节。最后将page字段移动至最后一列方便图片保存。
3)使用VBA自动生成小班简表图片。首先生成page字段的唯一值字典,然后对page字段进行筛选,把筛选的内容复制成图片然后新建空白的图表,把内容粘贴进图表,再使用VBA图表的导出功能可将内容转换成图片。实现代码如下:
Sub xbjb()
Dim arr()
Dim dic
Dim pa
Dim a As Range
Dim i, j As Integer
Dim arr2()
Dim shp As Shape
Dim oChartObject As ChartObject
Dim oChart As Chart
sPath = Excel.ThisWorkbook.Path & "”
arr = Sheet2.Range("a3:" & Chr(Range("z3").End(xlToLeft).Column + 64) & Range("a65536").End(xlUp).Row)
Set dic = CreateObject("scripting.dictionary")
pa = Chr(Range("z2").End(xlToLeft).Column + 64)
arr2 = Range(pa & "3:" & pa & Range(pa & "65536").End(xlUp).Row)
For i = LBound(arr2) To UBound(arr2)
dic(arr2(i, 1)) = 1
Next
Rows("2:2").Select
Range("F2").Activate
Selection.AutoFilter
For Each d In dic.keys
ActiveSheet.Range("$A$2:" & "$" & pa & "$" & Range("a65536").End(xlUp).Row).AutoFilter Field:=Range("z3").End(xlToLeft).Column, Criteria1:=d
Range("A1:" & Chr(Range("z3").End(xlToLeft).Column + 63) & Range("a65536").End(xlUp).Row).CopyPicture Appearance:=xlScreen, Format:=xlPicture
ActiveSheet.Paste
For Each shp In Shapes
shp.Title = d
shp.Copy
Set oChartObject = ChartObjects.Add(0, 0, shp.Width, shp.Height)
oChartObject.Activate
ActiveChart.Paste
oChartObject.Chart.Export sPath & d & ".jpg"
shp.Delete
For Each oChartObject In ChartObjects
oChartObject.Delete
Next
Next
Next
End Sub
用ArcPy的融合代码生成索引图层,并为索引图层的tplj和xh字段赋值,tplj表示小班属性表所对应的绝对路径,xh表示该张小班设计图的序号。根据之前制作的出图模板,替换XB_JX及qdfz的数据源,最后结合数据驱动导出图片,保存为jpg格式。出图代码如下:
import arcpy
import os
fc=arcpy.GetParameterAsText(0)
mxd=arcpy.mapping.MapDocument((arcpy.GetParameterAsText(1)).decode("utf-8"))
excel_sjqd=arcpy.GetParameterAsText(2)
tp_luj=arcpy.GetParameterAsText(3)
tp_fbl=int(arcpy.GetParameterAsText(4))
wj_lj=os.path.dirname(mxd.filePath)
if os.path.exists(os.path.join(wj_lj,"sjqd_ys.gdb")):
arcpy.Delete_management(os.path.join(wj_lj,"sjqd_ys.gdb"))
arcpy.CreateFileGDB_management(wj_lj,"sjqd_ys")
else:
arcpy.CreateFileGDB_management(wj_lj,"sjqd_ys")
out_gdb=os.path.join(wj_lj,"sjqd_ys.gdb")
arcpy.Dissolve_management(fc, os.path.join(out_gdb,"XB_RH"),
"Page", "", "MULTI_PART",
"DISSOLVE_LINES")
arcpy.AddField_management(os.path.join(out_gdb,"XB_RH"),"xz","TEXT")
arcpy.AddField_management(os.path.join(out_gdb,"XB_RH"),"xh","TEXT")
arcpy.AddField_management(os.path.join(out_gdb,"XB_RH"),"tplj","TEXT")
with
arcpy.da.UpdateCursor(os.path.join(out_gdb,"XB_RH"),["Page","xz","xh","tplj"])as cursor:
for row in cursor:
row[1]=str(row[0]).split("_")[0]
row[2]=str(row[0]).split("_")[1]
row[3]=str(os.path.join(excel_sjqd,row[0]))+".jpg"
cursor.updateRow(row)
arcpy.Copy_management(fc,os.path.join(out_gdb,"XB_FZ"))
df=arcpy.mapping.ListDataFrames(mxd)[0]
lyr_list=arcpy.mapping.ListLayers(df)
for lyr in lyr_list:
if lyr.name == "XB_FZ" or lyr.name == "XB_RH":
arcpy.mapping.RemoveLayer(df,lyr)
if lyr.name == "qdfz":
lyr.replaceDataSource(out_gdb,"FILEGDB_WORKSPACE","XB_RH")
if lyr.name =="XB_JX":
lyr.replaceDataSource(out_gdb,"FILEGDB_WORKSPACE","XB_FZ")
if lyr.name ==os.path.basename(fc):
arcpy.mapping.RemoveLayer(df,lyr)
arcpy.RefreshActiveView
for pageNum in range(1, mxd.dataDrivenPages.pageCount + 1):
mxd.dataDrivenPages.currentPageID = pageNum
filds=["OID@","xz","xh"]
with arcpy.da.SearchCursor(os.path.join(out_gdb,"XB_RH"),filds) as cursor:
for row in cursor:
if str(row[0])==str(pageNum):
a=str(str(row[1])+"小班设计图"+str(row[2])+".jpg").decode("utf-8")
arcpy.mapping.ExportToJPEG(mxd,os.path.join(tp_luj,a),resolution=tp_fbl)
del mxd
自动化制图的代码比较复杂,进行成果分享时需对代码进行包装,本研究使用ArcGis的脚本制作工具进行代码包装,在ArcGis的目录中新建工具箱,并新建Python脚本工具,由前文步骤可知,共需新建2个Python脚本,第一个为小班字段赋值脚本,第二个为自动出图脚本。结果如图4、图5所示。
图4 字段赋值脚本
图5 自动出图脚本
小班设计图全部导出完成后,对小班设计图进行手动检查,检查小班界是否与图框相交、是否同时出现两个乡镇的小班等不符合要求的小班设计图。
经过检查后, 121张小班设计图,只有1张小班设计图由于小班简表与小班界线存在相交的现象,美观率99.2%。在具体操作中,不合格小班设计图可手动进行拖动重新出图,或者调整参数的大小或者调整页面元素的布局,从而保证小班设计图与小班简表不重叠。
从出图结果看,所有小班设计图的小班界均与图框不相交,不仅实现了按照乡镇出图,还确保了图面上小班数量的最大化,在节约出图时间的同时保证纸张数量最少。如图6所示。
根据小班的实际位置与实际A3纸张的大小确定各小班所在的页码,在保证小班界线与框线不相交的同时,确保一张A3页面中的小班个数最大化,从而减少了纸张数量。但页面元素中,小班简表的位置相对不固定,小班个数越多的页面,其小班简表所占用的范围也越大,与小班简表交叉的可能性也越高,通过设置出图相对比例参数可降低交叉的可能性。
图6 小班设计图
本研究基于ArcGis内置站点包ArcPy和VBA,从图件美观和减少纸张数的角度出发,将自动化制图分解为四个步骤,使用VBA进行索引图层的制作和自动出图、使用VBA进行小班属性简表的自动生成,较好地解决了林业专题图中多个小班自动出图所面临的一些问题,实现了林业制图的自动化和美观化,在提高工作效率的同时,不仅确保了所有图件的统一性,还保证了图件的美观。
1)使用传统的手工制作林业专题图,需要重复的进行小班简表的插入或手动更改图面文字信息、重复地导出地图,不仅过程繁琐,还容易出错,工作效率极低。本研究通过结合ArcPy和VBA,实现了林业专题图的自动出图,在保证图件美观的同时大大提升了工作效率。
2)在以往关于自动出图的研究中,大都是使用原图层作为索引图层,故一个图件上只能出一个小班,本研究使用ArcPy进行索引图层的制作,通过小班位置距离搜索功能,自动调节图上的小班范围比例,实现了多个小班自动出图。
3)与添加动态文本作为小班属性相比,本研究使用VBA生成小班属性简表作为小班属性信息,可以添加多个小班的任意属性信息,同时还能使用Excel对小班简表进行风格化处理,满足不同的需求。
本研究开发设计的自动出图工具对于提高林业制图工作效率具有很重要的现实意义,结合ArcPy和VBA,解决了以往自动出图中添加小班属性信息困难、多个小班出图繁琐等问题,能够节省大量时间用于项目成果质量的把控,具有很好的推广意义。但本研究依然存在不足,如小班简表的制作过程中,小班个数越多则小班简表的长度越长,与小班界相交的可能性越大,在设计代码的过程中由于无法提前知晓每张小班设计图中小班的个数和分布情况,故暂时未能从源头解决问题,只能调节页面布局情况和相关参数大小,在今后的研究中需进一步进行优化。