10 风格指南

在用GMT绘图的时候,遵循一定的风格指南,可以让画图的过程更轻松,让脚本更易读、易改、更健壮、可移植性更高。

10.1 使用脚本来执行GMT命令

GMT遵循了UNIX的设计思想,将不同的功能分别放在不同的命令中,因而在绘图时需要执行一系列命令。

若使用命令行来执行一系列命令,很容易弄混前一个命令是什么。将所有的绘图命令放在脚本中可以很方便地重复执行一系列命令,以对绘图的细节进行微调。

除非是一两个命令就可以解决的图,否则应一律使用脚本而非命令行。

Windows下常用的脚本是bat,Linux常用的是Bash、Perl和Python。使用什么脚本语言完全依赖于用户个人的需求与喜好,这里以Bash脚本为例,其他脚本同理。

#!/bin/bash

gmt psxy ...
gmt pscoast ...
gmt grdimage ...
gmt psxy ...

10.2 不要跨平台写脚本

不要在Windows下写Bash脚本然后复制到Linux下运行;也不要在Linux下写Bat脚本放在 Windows下运行。这其中会遇到很多坑,包括但不限于:

  • 默认编码不同,Windows用GBK,Linux用UTF8;
  • 换行符不同,Windows用 \r\n ,Linux用 \n

如果你真的跨平台写了脚本并遇到各种奇怪的问题时,尝试着新建一个文件,然后把脚本重新手敲一遍。

10.3 使用变量

脚本不仅仅只是将一系列命令放在一个文件而已。绘图时有很多需要在多个命令中重复使用的东西,比如设置投影方式的 -J 、设置绘图范围的 -R 、文件名 xxx.ps

关于如何使用变量,一般有两种定义方式,这两种方法各有利弊,尚待权衡:

  1. 将参数作为变量的值

    #!/bin/bash
    J=M6i
    R=0/360/-60/60
    Bx=60
    By=30
    PS=map.ps
    
    gmt pscoast -J$J -R$R -B$Bx -B$By -W1p -A1000 -K > $PS
    gmt psxy -J -R -Sa0.5c -Gred -O >> $PS << EOF
    160 20
    150 30
    EOF
    
  2. 将选项和参数作为变量的值

    #!/bin/bash
    J=-JM6i
    R=-R0/360/-60/60
    B=-Bx60 -By30
    PS=map.ps
    
    gmt pscoast $J $R $B -W1p -A1000 -K > $PS
    gmt psxy -J -R -Sa0.5c -Gred -O >> $PS << EOF
    160 20
    150 30
    EOF
    

10.4 不要省略参数

GMT的一个特性是后面的命令可以继承前面命令的一些参数,比如前面的命令中指定了 -JM10c -R0/360/-60/60 ,后面的命令可以直接使用 -J -R 而不用重复给出更多的参数。这样的设计减少了用户的键入。

省略参数虽然带来了一点点方便,但也可能会造成一些麻烦:

  1. 写GMT脚本时由于需要经常修改、增添命令或调整各个命令之间的顺序。在省略了部分参数的情况下,调整各个命令之间的顺序就可能导致 -J -R 出现在第一个,有时会造成意想不到的错误。
  2. 参数可以省略本质上是因为之前的命令将标准选项的参数写到了GMT历史文件 gmt.history 中,因而当在同一个目录里同时运行两个相同或不同的脚本时,两个脚本就会读写同一个 gmt.history 文件,进而可能导致一个脚本读到的内容是另外一个脚本写的。

因而,尽量不要省略参数。相同的参数在多个命令里要写很多遍,这样很麻烦,但是因为前面已经把这些参数定义成变量了,所以只是多敲了几个字符而已,因此带来的好处可不少。

#!/bin/bash
J=M6i
R=0/360/-60/60
Bx=x60
By=y30
PS=map.ps

gmt pscoast -J$J -R$R -B$Bx -B$By -W1p -A1000 -K > $PS
gmt psxy -J$J -R$R -Sa0.5c -Gred -O >> $PS << EOF
160 20
150 30
EOF

10.5 开始与结束

多个绘图命令会将PS代码依次写入到一个PS文件中。绘图命令的顺序有时会影响到成图的效果,最常见的例子就是,如果先 pscoastgrdimage ,则 grdimage 的效果就会覆盖 pscoast 的效果。因而在绘制一张稍复杂的图时,经常需要在原有的代码中增添、删除或修改已有命令的顺序,这个时候尤其需要注意 -K-O 以及重定向符号的使用。

下面的代码解决了这个问题:

#!/bin/bash
J=M6i
R=0/360/-60/60
Bx=x60
By=y30
PS=map.ps

# 写入PS文件头
gmt psxy -J$J -R$R -T -K > $PS

# 一系列绘图命令
gmt pscoast -J$J -R$R -B$Bx -B$By -W1p -A1000 -K -O >> $PS

# 写入PS文件尾
gmt psxy -J$J -R$R -T -O >> $PS

此处使用了专门的两个命令用于开始和结束一个PS绘图。这样做的好处在于:中间的所有绘图命令都使用 -K -O >> ,不必再考虑这个命令是第一个还是最后一个了,也可以随意删除或修改任何一个命令而不必担心造成其它效果。

因而,实际写绘图脚本时,先把开始和结束这两个命令写对,然后在两个命令的中间写入真正的绘图命令。每新增一个绘图命令,都可以执行一下脚本,以检查绘图效果,若效果正确,则继续添加下一个绘图命令。

10.6 命令中选项的顺序

GMT 命令对各个选项的顺序是没有规定的,所以理论上选项之间怎么排序都可以。但对于大多数命令而言,选项遵循一定的顺序可以减少错误的发生。

推荐的选项顺序是:

gmt 模块名 输入文件 -J -R -B ... -X -Y -K -O >> PSfile

总结其规则如下:

  1. 如果当前命令需要一个输入文件,则将输入文件紧跟在模块名的后面
  2. -J-R 选项紧跟在输入文件的后面
  3. -K -O 位于 重定向符号 >> 之前
  4. -X-Y 选项放在 -K -O 之前
  5. 其余选项则放在 ... 所在位置

10.7 使用SI单位制

GMT支持SI单位制和US单位制,默认是SI单位制。由于GMT的开发者是美国人,官方的文档使用的是US单位制,因而国内的GMT用户在学习的过程中也就习惯性地使用了US单位制。

实际上,国内用户对于US单位制没有太多的概念, -X1i 远远没有 -X2.5c 直观。SI单位制是国际标准单位,也是中国人熟悉的单位,使用SI单位制会使得微调更简单。

10.8 不要依赖于GMT的系统设置

你所写的每一个脚本,将来都可能传给后来人使用,可能在任一台机器上使用。要保证脚本每次运行的结果完全一致,并不是一个简单的事情。

10.8.1 不要省略单位

当使用 -JM10 时,GMT会默认使用当前的系统默认单位(一般来说是 c ,也就是厘米),当脚本在另一台系统默认单位为 i 的机器上运行时,绘图的结果会完全不同。

10.8.2 conf文件的使用

不要手动修改 gmt.conf 文件!

GMT中提供了 gmtset 模块可以用于修改缺省参数,比如标题的字体、大小等等。该命令会在当前工作目录下生成一个 gmt.conf 文件,进而影响到接下来绘图命令的执行效果。

合理的使用方式如下:

#!/bin/bash

# 用gmtset修改默认参数
gmt gmtset MAP_FRAME_TYPE plain

# 绘图
gmt psxy ...
gmt pscoast ...
gmt psxy ...

# 删除参数文件
rm gmt.*

在脚本的最后 rm gmt.* 删除了两个临时文件,一个是 gmt.history ,其记录了标准选项的命历史,另一个是 gmt.conf ,记录了当前的参数。

删除这些文件的原因在于:

  • 临时文件,应该删除
  • 脚本已经执行完毕,不应该遗留下无用的文件
  • 保留 gmt.conf 文件,可能会导致下次执行脚本时产生不同的效果

有这样一种可怕的情况:假如你在 $HOME 下执行了 gmtset 命令,然后画了一个简单的图,但是却忘记删除 $HOME 下生成的 gmt.conf 文件,这会影响到其它目录中几乎所有GMT脚本的执行效果,而且这个问题很难排查。要避免这种情况的发生需要遵循几个原则:

  1. 尽量不要在 $HOME 下执行GMT命令(可能会产生临时文件,难以清理)
  2. 尽量不要使用命令行执行GMT命令(因为你很可能会忘记你刚刚执行过哪些命令)
  3. 使用 gmtset 的脚本,最后一定要记得删除 gmt.conf

10.9 -P选项的使用

只有第一个绘图命令中的 -P 选项是起作用的,所以不需要在每个绘图命令里都使用 -P 选项,当然若是每个绘图命令都使用了 -P 选项也没有问题,只是不够简洁而已。

两种推荐的使用方式:

  1. 在开始PS文件时使用该选项:

    #!/bin/bash
    J=M20c
    R=0/360/-60/60
    Bx=x60
    By=y30
    PS=map.ps
    
    gmt psxy -J$J -R$R -T -K -P > $PS
    gmt pscoast -J$J -R$R -B$Bx -B$By -W1p -A1000 -K -O >> $PS
    gmt psxy -J$J -R$R -T -O >> $PS
    rm gmt.*
    
  2. 修改 PS_PAGE_ORIENTATION ,不使用 -P 选项

    #!/bin/bash
    J=M20c
    R=0/360/-60/60
    Bx=x60
    By=y30
    PS=map.ps
    
    gmt set PS_PAGE_ORIENTATION portrait
    gmt psxy -J$J -R$R -T -K > $PS
    gmt pscoast -J$J -R$R -B$Bx -B$By -W1p -A1000 -K -O >> $PS
    gmt psxy -J$J -R$R -T -O >> $PS
    rm gmt.*
    

10.10 不要滥用-B选项

-B 选项用于绘制边框并控制边框的绘制效果,即每个使用 -B 选项的命令都会绘制一次边框,在没有使用 -X-Y 的情况下,多个命令重复使用 -B 选项会绘制多次边框,但由于边框是重合的,所以会看不出来区别。

对于 -B 选项,合理的用法是仅在第一个命令中使用。

10.11 verbose模式

GMT命令的输出信息常用于在写脚本时判断命令执行是否正确,而在真正执行时过多的输出信息反而会扰乱用户的屏幕输出。合理的使用verbose模式的方式有三种:

  1. 写脚本时每个命令都加上 -V 选项,待确认脚本正确无误之后删除所有 -V

  2. 定义Verbose变量

    #!/bin/bash
    
    J=M20c
    R=0/360/-60/60
    Bx=x60
    By=y30
    PS=map.ps
    V=-V      # 调试时用这个
    #V=       # 调试完成用这个
    
    gmt psxy -J$J -R$R -T -K -P $V > $PS
    gmt pscoast -J$J -R$R -B$Bx -B$By -W1p -A1000 -K -O $V >> $PS
    gmt psxy -J$J -R$R -T -O $V >> $PS
    rm gmt.*
    
  3. 修改缺省参数

    #!/bin/bash
    J=M20c
    R=0/360/-60/60
    Bx=x60
    By=y30
    PS=map.ps
    
    gmt gmtset GMT_VERBOSE TRUE
    gmt psxy -J$J -R$R -T -K > $PS
    gmt pscoast -J$J -R$R -B$Bx -B$By -W1p -A1000 -K -O >> $PS
    gmt psxy -J$J -R$R -T -O >> $PS
    rm gmt.*
    

从使用上的简洁来看,最简单的是第三种方法。

10.12 慎用-X和-Y

使用这两个选项会导致坐标原点的移动。因而使用的时候需要相当慎重。

  1. 除极个别的情况外, -X-Y 选项应该仅在绘制组合图(即一张图多个子图)时使用
  2. 对于非组合图,也可以在第一个绘图命令中使用 -Xc -Yc 使得整个绘图框架位于纸张的中央
  3. 不要仅仅为了将某个符号或文字移动到某个位置就使用这两个选项,如果真的有这种需求的话,应该使用绝对坐标 -Xa1c -Ya1c ,其仅影响当前命令的绘图位置

10.13 网格文件后缀

GMT主要使用netCDF格式作为网格数据的格式,其标准后缀名为 .nc

需要注意以下两个事实:

  1. GMT不会对后缀进行检测,所以后缀是什么都不重要
  2. GMT之前的版本中曾经自定义了一种网格数据格式,并使用后缀 .grd ,因而很多脚本中都使用了 .grd 作为后缀。