这篇博文会介绍写 GMT 脚本时需要遵循的一些风格与习惯。本文的目的是,希望遵循本风格指南的 GMT 脚本能够更易读、易改、更健壮、可移植性更高。
使用脚本来执行 GMT 命令
GMT 遵循了 UNIX 的设计思想,将不同的功能分别放在不同的命令中,因而在绘图时需要执行一系列命令。
若使用命令行来执行一系列命令,很容易弄混前一个命令是什么。将所有的绘图命令放在脚本中可以很方便地重复执行一系列命令,以对绘图的细节进行微调。
除非是一两个命令就可以解决的图,否则应一律使用脚本而非命令行。
Windows 下常用的脚本是 bat,Linux 常用的是 Bash、Perl 和 Python。使用什么脚本语言完全依赖于用户个人的需求与喜好,这里以 Bash 脚本为例,其他脚本同理。
#!/bin/bash
psxy ...
pscoast ...
grdraster ...
grdimage ...
psxy ...
不要跨平台写脚本
不要在 Windows 下写 Bash 脚本然后复制到 Linux 下运行;也不要在 Linux 下写 Bat 脚本放在 Windows 下运行。这其中会遇到很多坑,包括但不限于:默认编码不同,Windows 用 GBK,Linux 用 UTF8;
换行符不同,Windows 用 \r\n ,Linux 用 \n ;
如果你真的跨平台写了脚本并遇到各种奇怪的问题时,尝试着新建一个文件,然后把脚本重新手敲一遍。
使用变量
脚本不仅仅只是将一系列命令放在一个文件而已。绘图时有很多需要在多个命令中重复使用的东西,比如设置投影方式的 -J 、设置绘图范围的 -R 、文件名 xxx.ps 。
关于如何使用变量,一般有两种定义方式,这两种方法各有利弊,尚待权衡:将参数作为变量的值
#!/bin/bashJ=M6i
R=0/360/-60/60
B=60/30
PS=map.ps
pscoast -J$J -R$R -B$B -W1p -A1000 -K > $PS
psxy -J -R -Sa0.5c -Gred -O >> $PS << EOF160 20150 30EOF
将选项和参数作为变量的值
#!/bin/bashJ=-JM6i
R=-R0/360/-60/60
B=-B60/30
PS=map.ps
pscoast $J $R $B -W1p -A1000 -K > $PS
psxy -J -R -Sa0.5c -Gred -O >> $PS << EOF160 20150 30EOF
不要省略参数
GMT 的一个特性是后面的命令可以继承前面命令的一些参数,比如前面的命令中指定了
-JM10c -R0/360/-60/60 ,后面的命令可以直接使用 -J -R 而不用重复给出更多的参数。
这样的设计减少了用户的键入。
省略参数虽然带来了一点点方便,但也可能会造成一些麻烦:写 GMT 脚本时由于需要经常修改、增添命令或调整各个命令之间的顺序。在省略了部分参数的情况
下,调整各个命令之间的顺序就可能导致 -J -R 出现在第一个,有时会造成意想不到的错误。
参数可以省略本质上是因为之前的命令将参数写到了文件 .gmtcommands 中,因而当在同
一个目录里同时运行两个相同或不同的脚本时,两个脚本就会读写同一个 .gmtcommands 文件,
进而可能导致一个脚本读到的内容是另外一个脚本写的。
因而,尽量不要省略参数。相同的参数在多个命令里要写很多遍,这样很麻烦,但是因为前面已经把这些
参数定义成变量了,所以只是多敲了几个字符而已,因此带来的好处可不少。
#!/bin/bashJ=M6i
R=0/360/-60/60
B=60/30
PS=map.ps
pscoast -J$J -R$R -B$B -W1p -A1000 -K > $PS
psxy -J$J -R$R -Sa0.5c -Gred -O >> $PS << EOF160 20150 30EOF
开始与结束
多个绘图命令会将 PS 代码依次写入到一个 PS 文件中。绘图命令的顺序有时会影响到成图的效果,
最常见的例子就是,如果先 pscoast 再 grdimage,则 grdimage 的效果就会覆盖
pscoast 的效果。因而在绘制一张稍复杂的图时,经常需要在原有的代码中增添、删除或修改
已有命令的顺序,这个时候尤其需要注意 -K 、 -O 以及重定向符号的使用。
下面的代码解决了这个问题:
#!/bin/bashJ=M6i
R=0/360/-60/60
B=60/30
PS=map.ps
# 写入 PS 文件头
psxy -J$J -R$R -T -K > $PS
# 一系列绘图命令
pscoast -J$J -R$R -B$B -W1p -A1000 -K -O >> $PS
# 写入 PS 文件尾
psxy -J$J -R$R -T -O >> $PS
此处使用了专门的两个命令用于开始和结束一个 PS 绘图。这样做的好处在于:中间的所有绘图命令都使用 -K -O >>,不必再考虑这个命令是第一个还是最后一个了,也可以随意删除或修改任何一个命令而不必担心造成其它效果。
因而,实际写绘图脚本时,先把开始和结束这两个命令写对,然后在两个命令的中间写入真正的绘图命令。每新增一个绘图命令,都可以执行一下脚本,以检查绘图效果,若效果正确,则继续添加下一个绘图命令。
使用 SI 单位制
GMT 支持 SI 单位制和 US 单位制,默认是 SI 单位制。由于 GMT 的开发者是美国人,官方的文档使用的是 US 单位制,因而国内的 GMT 用户在学习的过程中也就习惯性地使用了 US 单位制。
实际上,国内用户对于 US 单位制没有太多的概念, -X1i 远远没有 -X2.5c 直观。
SI 单位制是国际标准单位,也是中国人熟悉的单位,使用 SI 单位制会使得微调更简单。
不要依赖于 GMT 的系统设置
你所写的每一个脚本,将来都可能传给后来人使用,可能在任一台机器上使用。要保证脚本每次运行的结果完全一致,并不是一个简单的事情。
不要修改 GMT 系统设置
有些人喜欢使用特定字体,或者喜欢使用特定尺寸的纸张,这可以通过修改 $GMTHOME/share/conf 下的一堆系统配置文件来实现。但是,不要这样做,这会导致脚本在别人的机器上跑出来完全不一样的结果。
不要省略单位
当使用 -JM10 时,GMT 会默认使用当前的系统默认单位(一般来说是 c,也就是厘米),当脚本在另一台系统默认单位为 i 的机器上运行时,绘图的结果会完全不同。
default 文件的使用
不要手动修改 default 文件!
GMT 中提供了 gmtset 命令可以用于修改缺省参数,比如标题的字体、大小等等。该命令会在当前工作目录下生成一个 .gmtdefaults4 文件,进而影响到接下来绘图命令的执行效果。
合理的使用方式如下:
#!/bin/bash
# 用 gmtset 修改默认参数
gmtset BASEMAP_TYPE plain
# 绘图
psxy ...
pscoast ...
psxy ...
# 删除参数文件
rm .gmt*
在脚本的最后 rm .gmt* 至少删除了两个临时文件,一个是 .gmtcommands,其记录了
通用选项的一些信息,另一个是 .gmtdefaults4,记录了当前的缺省参数。
删除这些文件的原因在于:临时文件,应该删除。
脚本已经执行完毕,不应该遗留下无用的文件。
保留 .gmtdefaults4 文件,可能会导致下次执行脚本时产生不同的效果。例如,脚本中首先使用了默认字体 0,然后绘制了一部分图,再使用 gmtset 修改字体为字体 1,又绘制了一部分图,若忘记删除 .gmtdefaults4 文件,则该文件会成为下次执行脚本时的默认参数文件,导致默认字体变成 1,因而出现不同的绘图效果。
有这样一种可怕的情况:假如你在 $HOME 下执行了 gmtset 命令,然后画了一个简单的图,但是却忘记删除 $HOME 下生成的 .gmtdefaults4 文件,这会影响到其它目录中几乎所有 GMT 脚本的执行效果,而且这个问题很难排查。要避免这种情况的发生需要遵循几个原则:尽量不要在 $HOME 下执行 GMT 命令(可能会产生临时文件,难以清理)
尽量不要使用命令行执行 GMT 命令(因为你很可能会忘记你刚刚执行过哪些命令)
使用 gmtset 的脚本,最后一定要记得删除 .gmtdefaults4
-P 选项的使用
只有第一个绘图命令中的 -P 选项是起作用的,所以不需要在每个绘图命令里都使用 -P 选项,
当然若是每个绘图命令都使用了 -P 选项也没有问题,只是不够简洁而已。
两种推荐的使用方式:在开始 PS 文件时使用该选项:
#!/bin/bashJ=M20c
R=0/360/-60/60
B=60/30
PS=map.ps
psxy -J$J -R$R -T -K -P > $PS
pscoast -J$J -R$R -B$B -W1p -A1000 -K -O >> $PS
psxy -J$J -R$R -T -O >> $PS
修改 PAGE_ORIENTATION ,不使用 -P 选项
#!/bin/bashJ=M20c
R=0/360/-60/60
B=60/30
PS=map.ps
gmtset PAGE_ORIENTATION portrait
psxy -J$J -R$R -T -K > $PS
pscoast -J$J -R$R -B$B -W1p -A1000 -K -O >> $PS
psxy -J$J -R$R -T -O >> $PS
rm .gmt*
不要滥用 - B 选项
-B 选项用于绘制边框并控制边框的绘制效果。
即每个使用 -B 选项的命令都会绘制一次边框,在没有使用 -X 和 -Y 的情况下,多个命令重复使用 -B 选项会绘制多次边框,但由于边框是重合的,所以会看不出来区别。
对于 -B 选项,合理的用法是仅在第一个命令中使用。
verbose 模式
GMT 命令的输出信息常用于在写脚本时判断命令执行是否正确,而在真正执行时过多的输出信息反而会扰乱用户的屏幕输出。合理的使用 verbose 模式的方式有三种:写脚本时每个命令都加上 -V 选项,待确认脚本正确无误之后删除所有 -V 。
定义 Verbose 变量
#!/bin/bash
J=M20c
R=0/360/-60/60
B=60/30
PS=map.ps
V=-V # 调试时用这个
#V= # 调试完成用这个
psxy -J$J -R$R -T -K -P $V > $PS
pscoast -J$J -R$R -B$B -W1p -A1000 -K -O $V >> $PS
psxy -J$J -R$R -T -O $V >> $PS
修改缺省参数
#!/bin/bashJ=M20c
R=0/360/-60/60
B=60/30
PS=map.ps
gmtset VERBOSE TRUE
psxy -J$J -R$R -T -K > $PS
pscoast -J$J -R$R -B$B -W1p -A1000 -K -O >> $PS
psxy -J$J -R$R -T -O >> $PS
rm .gmt*
从使用上的简洁来看,最简单的是第三种方法。
慎用 - X 和 - Y
使用这两个选项会导致坐标原点的移动。因而使用的时候需要相当慎重。除极个别的情况外, -X 和 -Y 选项应该仅在绘制组合图(即一张图多个子图)时使用;
对于非组合图,也可以在第一个绘图命令中使用 -Xc -Yc 使得整个绘图框架位于纸张的中央;
不要仅仅为了将某个符号或文字移动到某个位置就使用这两个选项,如果真的有这种需求的话,应该使用绝对坐标 -Xa1c -Ya1c ,其仅影响当前命令的绘图位置。
网格文件后缀
GMT 主要使用 netCDF 格式作为网格数据的格式,其标准后缀名为 .nc 。
需要注意以下两个事实:GMT 不会对后缀进行检测,所以后缀是什么都不重要
GMT 之前的版本中曾经自定义了一种网格数据格式,并使用后缀 .grd,因而很多脚本中都使用了 .grd 作为后缀。
修订历史2014-05-13:初稿;
2014-05-16:关于 “网格文件后缀” 的说明;
2015-03-17:不要跨平台写脚本;
2015-08-08:省略参数可能导致的两种问题;