ArcGIS Python脚本入门:从地理处理自动化到批量缓冲裁剪

发布时间:2026/11/3 3:12:09
ArcGIS Python脚本入门:从地理处理自动化到批量缓冲裁剪

简介:《ArcGIS中Python脚本的使用》是一份面向零基础GIS学习者的入门文档,围绕ArcGIS环境下Python脚本的编写与调用展开,帮助读者从语法基础过渡到地理处理自动化。内容先梳理数学运算、字符串、模块导入、自定义函数、条件与循环、输入输出等Python必备知识,再进入arcpy实践,涉及地理处理对象创建、工具帮助查询、参数列表、Toolbox与别名调用,以及在ModelBuilder中嵌入脚本、用PythonWin调试和借助Describe、Exists获取数据信息、使用地图代数等环节。资源为1个docx文档,压缩包约490KB,目录按主题分节,便于按基础语法与ArcGIS应用两条线索检索学习。已有1494人学习下载,适合希望用脚本提升空间数据处理效率、建立GIS编程基础的初学者逐步练习与查阅。

1. 先搞懂一件事:脚本不是给程序员准备的

“我连 Excel 公式都记不住,学得会吗?”带新人的时候,这句话我听得最多。其实 ArcGIS 中 Python 脚本的使用,和你想象的“写代码”差得挺远——它更像把你已经会的那套点击操作,提前录成一张菜单,下次喊一声“照这个来”就行。最常见的场景是这样:手上有几十份数据,全要重投影、裁剪到同一片区域、再导出,一份一份点,一晚上就耗进去了;写成脚本,按下回车去倒杯水,回来就完事。这门手艺适合天天跟地图数据打交道、又不想把时间耗在机械点击上的人,不要求你先成为程序员,只要求你愿意花一个下午,把最常用的那几步走通。

2. 先补最少的 Python:够用就行的四件事

很多人卡在第一步,不是因为难,而是因为一打开教程就看到几百页语法,直接被劝退。你不需要把 Python 学完再回来碰地图,只要掌握四样东西——算数、字符串、模块与函数、流程控制——就能写出第一个能干活的脚本。下面这几节,每一节都只讲“在地理处理里真的会用到”的那部分,其余的先放着。

2.1 数学运算符和字符串,GIS 里天天在碰

先说运算符。加+、减-、乘*、除/、取余%、乘方**,这六个够你用到很久以后。有一条规则要记牢:参与运算的数里只要有一个是浮点型(带小数点的),结果就是浮点型;全是整型,结果才是整型。

这里藏着一个老教材常让人困惑的地方。旧版本的 Python 里5/2得到的是2,因为两个整数相除会直接截掉小数;新版本里5/2得到2.5。你在网上翻到的示例代码如果算出来和你不一样,八成就是这个原因。稳妥的做法是:需要小数结果时,把其中一个数写成5.0/2或者5/2.0,一眼就能看懂,不用猜。

取余%在地理里特别有用。比如方位角加 180 度表示反向,但结果可能超过 360,用% 360一转就回到正常范围:

az = 270 new_az = az + 180 print(new_az % 360) # 输出 90,方向从正西翻到正东

再说字符串。字符串就是一段文字,下标从 0 开始数。这个“从 0 开始”让很多人第一次接触时犯迷糊,记住“第一个字符的位置是 0”就行。切片用冒号,s[1:3]表示从第 1 个位置取到第 3 个位置之前,不含第 3 个;s[-1]表示倒数第一个。

s = "landuse" print(s[0]) # l 第一个字符 print(s[2:4]) # nd 从第 2 位取到第 3 位,不含第 4 位 print(s[-4:]) # duse 最后四个字符 print(s.upper()) # LANDUSE print(s + "_2024") # landuse_2024 加号用来拼接

真正天天要用的,是路径处理。Windows 路径里的反斜杠\在 Python 里有特殊含义,\n是换行、\t是制表符,所以"C:\gisdata\test"会被解释成一堆乱七八糟的东西。两种写法都行:要么在字符串前面加个r,让它老老实实当普通字符;要么干脆用正斜杠,ArcGIS 完全认。

path = r"C:\gisdata\demo\landuse.shp" print(path) # C:\gisdata\demo\landuse.shp print("C:/gisdata/demo/landuse.shp") # 正斜杠一样能用 parts = path.split("\\") # 按反斜杠拆开,得到路径片段 print(parts) # ['C:', 'gisdata', 'demo', 'landuse.shp'] print(path[-4:]) # .shp 用切片取扩展名

我建议你现在就打开编辑器,把上面两段原样敲一遍。字符串这东西看十遍不如敲一遍,敲完你会发现它就是个“可以按位置切片的文本”,没那么玄。

2.2 模块、def、if/while/for:把动作串成流水线

Python 自带一大堆现成工具,它们被分门别类放在“模块”里,用之前先import一下。地理处理里出场率最高的是这几个:math管三角函数和对数,os和os.path管文件和文件夹,random管随机数,等你进入下一章还会加上arcpy。

import math print(math.log10(100)) # 2.0 以 10 为底的对数 print(math.pi) # 3.141592653589793 print(math.hypot(3, 4)) # 5.0 已知两条直角边求斜边

注意三角函数吃的是弧度不是角度,math.sin(45)算出来的不是你以为的那个数。转换写法是角度 * math.pi / 180,这个换算后面还会用到。

def用来把一段常用逻辑打包成一个名字,之后反复调用。它有点像你在 Excel 里自定义了一个函数,只不过这里你自己定义:

import math def to_radians(angdeg): return angdeg * math.pi / 180 def to_degrees(angrad): return angrad * 180 / math.pi print(math.sin(to_radians(45))) # 0.7071... print(to_degrees(math.acos(0.5))) # 60.0

return后面就是这段逻辑的产出,调用时把它赋给一个变量就能接着用。

流程控制是脚本真正开始“聪明”的地方。if是“满足条件才做”,while是“条件还在就一直做”,for是“把一堆东西挨个过一遍”。三个结构有个共同点:行尾带冒号,下一行开始缩进,缩进的那几行才算“在这个条件下执行”。

import os ws = r"C:\gisdata\demo" names = os.listdir(ws) # 把文件夹里的东西全列成一个列表 shps = [] # 先准备一个空列表,用来装筛选结果 for n in names: if n.endswith(".shp"): # 只要以 .shp 结尾的 shps.append(n) for s in shps: print(s)

这段代码干的事很朴素:列出目录内容,挨个判断扩展名,把符合的塞进新列表,最后打印出来。GIS 里最典型的一类自动化就是它的放大版——“对文件夹里每一个要素类都做同一件事”。for加if这个组合,你只要能看懂,后面的脚本基本都能读下来。

2.3 第一个能跑的例子:算一次正午太阳高度角

概念讲完,跑点真东西。做山体阴影(hillshade)的时候,需要给工具两个参数:太阳高度角和方位角。这两个值跟纬度、太阳倾角有关。倾角在夏至约 23.44 度,春秋分是 0 度,冬至是 -23.44 度。

def sun_position(lat, decl): """输入纬度和太阳倾角,返回正午太阳高度角和方位角""" sunangle = 90 - lat + decl azimuth = 180 if sunangle > 90: # 太阳翻到天顶另一侧了,要折回来 sunangle = 180 - sunangle azimuth = 0 return sunangle, azimuth angle, az = sun_position(30, 23.44) print("正午太阳高度角:", angle) print("方位角:", az)

逐句说一下。sun_position接收两个参数,第一行把高度角算出来,第二行先给方位角一个默认值 180;if判断的是“算出来的角度超过 90 度”这种情况,超过说明太阳已经越过天顶,需要做一次 180 减的翻折,同时方位角改成 0。最后一行一次返回两个值,调用时用两个变量接住。

想让脚本从命令行接参数,而不是把数字写死,可以改造成这样:

import sys lat = float(sys.argv[1]) # 第一个参数:纬度 decl = float(sys.argv[2]) # 第二个参数:太阳倾角 print(sun_position(lat, decl))

sys.argv就是运行脚本时跟在后面的那串参数,argv[0]是脚本自己的名字,所以从[1]开始取。外面传进来的是文字,用float()转成数字才能参与计算,这一步忘了写,是新手最常见的出错点之一。

顺便提一句版本差异:网上很多老教程里写的是print 'hello',不带括号,那是旧版本语法,新版本必须写成print('hello')。你抄代码时发现报错,先看这一处。

3. 让脚本认得地图:地理处理对象怎么创建

Python 基础和地图之间还差一座桥。这座桥叫“地理处理对象”,说白了就是脚本里那个“能调用 ArcToolbox 所有工具的代理”。你在 ArcToolbox 里点过的每一个工具,脚本里都能通过它调起来,参数顺序也基本对得上。

3.1 三行代码拿到那个“总开关”

老版本里,这个对象要单独创建,写法是gp = arcgisscripting.create();后来版本更新,模块改名叫arcpy,import进来之后直接就能用,不用再手动造对象。你现在入门,直接认arcpy就行,看到老代码里的arcgisscripting知道那是同一个东西的旧名字即可。

import arcpy arcpy.env.workspace = r"C:\gisdata\demo" # 工作空间:脚本默认在哪找数据 arcpy.env.overwriteOutput = True # 允许覆盖同名输出,省掉一堆确认

arcpy.env是一组“环境设置”,管的是脚本运行的默认条件。workspace设好之后,你在工具参数里写"landuse"就够了,不用每次写完整路径;overwriteOutput设成True,重复跑脚本时才不会因为“输出已存在”这种原因中断。

提示:随 ArcGIS 一起装的那个 Python 不要自己去升级。环境和模块是配套的,升完版本对不上,脚本会连import arcpy都过不去。这一条是很多人的教训。

3.2 Lists:把工作空间里的数据一个个点出来

批量处理的前提是“先知道有哪些数据”。arcpy提供了一组List开头的方法:ListFeatureClasses列要素类,ListFields列字段,ListRasters列栅格,ListWorkspaces列工作空间。它们的返回结果就是一个 Python 列表,可以直接丢进for里遍历。

import arcpy arcpy.env.workspace = r"C:\gisdata\demo" # 只列出以 w 开头、类型为多边形的要素类 for fc in arcpy.ListFeatureClasses("w*", "Polygon"): print(fc) # 列出某个数据的字段名和字段类型 for field in arcpy.ListFields("landuse"): print(field.name, field.type)

第一个参数是通配符,"w*"表示只要 w 开头的,换成"*"就是全部;不写这个参数同样等于全部。第二个参数限定几何类型,"Polygon"、"Point"、"Line"、"Polyline"这些。

Describe则是“查户口”,把一份数据的底细读出来。数据类型、几何类型、坐标系、字段结构,它都能告诉你:

dsc = arcpy.Describe("landuse") print(dsc.dataType) # 比如 RasterDataset / Shapefile print(dsc.shapeType) # 比如 Polygon print(dsc.spatialReference.name) # 坐标系名称

这两样合起来,就是各种批量脚本的骨架:先List出名单,再for循环,循环体里Describe判断一下条件,然后对符合条件的动手。

3.3 工具名后面那个小尾巴:工具箱与别名

真正调用工具时,写法是“工具名 + 下划线 + 别名”。别名代表这个工具属于哪个工具箱,因为不同工具箱里可能有一模一样名字的工具。最典型的就是裁剪:Analysis工具箱里有一个Clip,Data Management工具箱里也有一个,功能相近但接受的数据类型不同。脚本里没有图形界面点选,只能靠别名区分。

别名对应的工具箱常见用途
analysisAnalysis裁剪、相交、缓冲区
managementData Management复制要素、投影、字段操作
conversionConversion格式转换,如导出 shp
saSpatial Analyst栅格计算、坡度、山影
cartographyCartography制图综合、注记
import arcpy arcpy.env.workspace = r"C:\gisdata\demo" arcpy.env.overwriteOutput = True # 把 coverage 里的线转成 shp arcpy.CopyFeatures_management("streams/arc", "streams.shp") # 对线做 200 米缓冲 arcpy.Buffer_analysis("streams.shp", "stbuff200.shp", "200 Meters") # 用缓冲结果去裁剪地质图 arcpy.Clip_analysis("geol.shp", "stbuff200.shp", "geolstr200.shp")

三行代码干了三件事:转格式、建缓冲区、裁剪。参数顺序基本和工具对话框里从上到下一致,最后一个永远是输出。"200 Meters"这种带单位的写法在涉及距离时很稳,能避免你工作空间坐标系单位不同导致算错的尴尬。

如果你连续调用同一个工具箱里的工具,可以先设一次默认工具箱,后面就省掉别名:

arcpy.env.workspace = r"C:\gisdata\demo" arcpy.env.toolbox = "Analysis" arcpy.Buffer("streams.shp", "stbuff200.shp", "200 Meters") arcpy.Clip("geol.shp", "stbuff200.shp", "geolstr200.shp")

注意:缩进、参数顺序、别名后缀,这三点是脚本报错的高发区。跑不通的时候先回头核对这三处。

4. 第一个完整脚本:批量缓冲再裁剪

前面都是零件,这一节把它们装成一台机器。这个脚本要做的事很具体:工作目录下所有线要素,逐条做 200 米缓冲,然后用缓冲结果去裁剪同一份地质图,输出带上原名做前缀。你照着改路径就能跑。

4.1 动手之前先问一句“数据在不在”

脚本和手动操作最大的区别在于:手动时你一眼能看到数据不在,脚本会闷头跑到底再崩。所以养成一个习惯——在正式处理前先确认数据存不存在。arcpy.Exists()就是干这个的,返回真或假,配合if用:

import arcpy arcpy.env.workspace = r"C:\gisdata\demo" if arcpy.Exists("streams.shp"): print("数据找到了") dsc = arcpy.Describe("streams.shp") print("几何类型:", dsc.shapeType) else: print("找不到数据,检查工作空间设置")

别看这几行简单,它能把“路径写错”“文件被挪走”“扩展名漏写”这几类问题挡在正式处理之前。我第一次写批量脚本时没加这一步,跑了十几分钟才发现第一份数据就没读到,全部输出都是空的,白等一场。

4.2 一段能直接改着用的完整脚本

下面这段把List、for、Describe、Exists和具体工具全用上了,逻辑是顺序展开的,从上到下读一遍就懂:

import arcpy import os # 1. 设置运行环境 arcpy.env.workspace = r"C:\gisdata\demo" arcpy.env.overwriteOutput = True clip_source = "geol.shp" # 用来裁剪的地质图 out_dir = r"C:\gisdata\demo\out" # 输出目录 # 2. 先确认裁剪用的源数据存在 if not arcpy.Exists(clip_source): print("裁剪源不存在,脚本退出") else: # 3. 列出所有线要素类 lines = arcpy.ListFeatureClasses("*", "Line") print("共找到", len(lines), "个线要素类") # 4. 逐个处理 for fc in lines: base = os.path.splitext(fc)[0] # 去掉扩展名,拿来做前缀 buf_name = base + "_buf200.shp" # 缓冲结果 out_name = base + "_clip.shp" # 裁剪结果 arcpy.Buffer_analysis(fc, buf_name, "200 Meters") arcpy.Clip_analysis(clip_source, buf_name, out_name) print("已完成:", out_name)

几个关键点拆开说。os.path.splitext(fc)[0]把文件名和扩展名分开,取[0]就是不含扩展名的那部分,用它拼新名字,输出才不会互相覆盖。ListFeatureClasses("*", "Line")里的"Line"限定了只要线要素,点、面自动跳过,省掉在循环里写判断。循环体里的三行——拼名字、缓冲、裁剪——每一轮都换一份输入,输出名字跟着变,整批跑完不会覆盖。

参数想改的话,位置很明确:把"200 Meters"改成你要的距离;把"Line"改成"Polygon"或"Point"就换成处理别的几何类型;clip_source换一份数据就是另一套流程。这就是脚本比手动强的地方——改一个数,整批全跟着变,不用重新点一遍。

4.3 跑挂了看哪里:Python 窗口的调试习惯

脚本第一次跑不通太正常了,关键是要会看提示。最省事的调试场地是软件自带的 Python 窗口,可以一行一行敲、一行一行看结果。做法是:先把import arcpy和环境设置那两行敲进去,然后单独敲arcpy.ListFeatureClasses()看一眼列表是不是空的;空的就是工作空间有问题。再单独敲一句arcpy.Describe("landuse")看字段对不对得上,最后才把循环放进去。

几个排查套路:

  • 报“不存在”相关的错,八成是路径或扩展名,先在arcpy.Exists()里问一句。
  • 报“参数无效”,先看参数顺序,输出是不是放在了最后。
  • 报“已存在同名输出”,说明overwriteOutput没设成True。
  • 什么都没报但结果不对,把中间结果留着别删,逐个打开看,别一口气跑到底。

调试时把print()塞满不丢人。老手也这么干,只是他们知道在哪些位置塞。我一般固定在三个位置打印:循环开始打当前处理的是谁、工具调用前打参数、工具调用后打完成提示。跑一次就知道卡在哪一步。

5. 新手最容易翻车的五个地方:一份排查清单

下面这五条,是我见过频率最高的翻车现场。每条按“什么现象、为什么、怎么办”写,遇到问题时可以对号入座。

5.1 路径里那几个反斜杠,把脚本带沟里了

现象:脚本报错说找不到文件,但你把路径复制到资源管理器里明明能打开。

原因:字符串里的\是转义符开头,\t、\n会被当成制表符和换行,路径被悄悄改成了别的东西,你看到的“路径”和 Python 拿到的根本不是同一个。

解决:路径前加r,写成r"C:\gisdata\demo";或者全部换成正斜杠"C:/gisdata/demo"。养成写路径就加r的习惯,能省掉大量莫名其妙的报错。

5.2 脚本里的中文注释,让老代码直接罢工

现象:代码从别处复制过来,只加了一行中文注释,运行就报编码错误,或者输出变成一堆乱码。

原因:脚本文件的编码和解释器读取时用的编码对不上,中文被当成非法字符。老版本对这一点尤其敏感。

解决:源文件统一存成 UTF-8;如果环境不认,就把注释改成英文,或者只在调试阶段临时加中文。稳妥起见,正式保存的脚本里尽量用英文注释,中文写在你的笔记里。

5.3 绕了两小时,其实是工具重名

现象:调用Clip报参数类型不对,可你照着帮助文档写的,一个字没差。

原因:同名工具存在于不同工具箱,你调用到的那个接受的输入类型跟你给的不一样,比如它要的是 coverage,你给的是 shp。

解决:工具名后面带别名,明确指到工具箱。要裁剪矢量用Clip_analysis,要栅格裁剪走sa那套。拿不准就查一次帮助,看工具的别名后缀。

5.4 循环跑完了,输出只有一份

现象:for循环明明转了好多次,控制台也打印了好几次“已完成”,但输出文件夹里只有一个文件。

原因:输出文件名写死了,每轮都往同一个名字覆盖,最后一轮的结果把前面的全盖掉了。

解决:输出名用输入名拼出来。像os.path.splitext(fc)[0] + "_buf200.shp"这样,每份数据对应一个名字。写完循环先打印一遍输出名字列表,确认它们不重复。

5.5 把自带的 Python 升了级,环境直接崩

现象:昨天还能跑的脚本,今天import arcpy就报错。

原因:随手把软件自带的 Python 更新到了新版本,地理处理模块和解释器版本不匹配。

解决:不要动自带的解释器。要装别的库,先确认它和当前版本兼容,或者用虚拟环境隔离。这一条属于“早知道就好了”的类型。

6. 会跑之后练什么:把脚本变成工具箱里的一个按钮

脚本能跑通,只是入门到站,不是终点。接下来最值得练的一件事,是把脚本挂成工具——让你的脚本有一个图标,出现在工具箱里,别人不用看代码也能用,参数从对话框填。做法不复杂:给脚本加上接收参数的几行代码(用arcpy.GetParameterAsText(0)这类写法取输入),保存成.py文件,然后在工具箱里新建一个脚本工具,把路径指过去,逐个定义参数顺序和类型。做完之后,这段代码就不再是你一个人的黑匣子,团队里谁都能点。

再往后,按这个顺序练比较顺:

阶段练什么大概花多久
第一周把第 4 章那段批量脚本改成处理面要素两三个晚上
第二周加参数输入,做成工具箱里的脚本工具一个周末
第三周学游标,读写属性表里的字段值三四个晚上
第四周学地图代数,把栅格计算写进脚本一周左右

游标这块要提前说一句:它分读取和写入两种,读的时候很安全,写的时候一定要小心,一旦写错字段,原数据是改不回来的。练的时候先复制一份数据,在副本上折腾,别拿正在用的数据试手。

还有个小技巧值得早点养成:每写一个脚本,先在开头留三行注释,写清楚“这个脚本输入什么、输出什么、什么时候写的”。过三个月你自己回头看,这三行能救你一次。

我刚开始写的第一批脚本,全是一个个临时文件,名字叫test1.py、test2.py、test_new.py,一个月后一个都不敢删,因为不知道里面是什么。从那以后我每次新建脚本,都强制先写清输入输出注释、存到固定目录、文件名带上用途和日期。这个习惯看着笨,但真的省事。希望帮到你。

本文还有配套的精品资源,点击获取