18. Custom Plot Symbols¶
18.1. Background¶
The GMT tools plot and plot3d are capable of using custom symbols as alternatives to the built-in, standard geometrical shapes such as circles, triangles, and many others. On the command line, custom symbols are selected via the -Sksymbolname[/size] symbol selection, where symbolname refers
An Encapsulated PostScript File named
symbolname.eps
A special symbol definition file called
symbolname.def
Either type of file must be available via the standard GMT user paths. EPS symbols are widely available on the Internet or can be created, even with GMT. If all you want to do is to use an EPS file as a custom symbol, then selecting the option -Sk is all you need to do. For using an EPS file as part of a more general custom symbol, for instance to allow rotation, then you will find more information provided below.
Several custom symbol definitions comes included with GMT (see Figure Custom symbols)
Here is the source script for the figure above:
grep -v '^#' "${GMT_SOURCE_DIR}"/share/custom/gmt_custom_symbols.conf | $AWK '{print $1}' > tt.lis
n=$(cat tt.lis | wc -l)
width=0.85
n_cols=6
n_rows=7
n_rows_p1=7
fs=9
dy=0.15
n_pages=$(gmt math -Q $n $n_cols DIV CEIL $n_rows_p1 SUB 0 MAX $n_rows DIV CEIL 1 ADD =)
p=0
s=0
while [ $p -lt $n_pages ]; do
p=$(expr $p + 1)
if [ $p -eq 1 ]; then
max_rows=$n_rows_p1
else
max_rows=$n_rows
fi
n_rows_to_go=$(gmt math -Q $n $s SUB $n_cols DIV CEIL $max_rows MIN =)
H=$(gmt math -Q $n_rows_to_go 1 $dy ADD MUL =)
rm -f tt.lines tt.symbols tt.text tt.bars
touch tt.lines tt.symbols tt.text tt.bars
c=0
while [ $c -lt $n_cols ]; do
c=$(expr $c + 1)
cat << EOF >> tt.lines
> vertical line
$c 0
$c $H
EOF
done
r=0
while [ $r -lt $n_rows_to_go ]; do # Loop over the rows that will fit this page
r=$(expr $r + 1)
yt=$(gmt math -Q $n_rows_to_go $r SUB 1.0 $dy ADD MUL 0.5 $dy MUL ADD =)
ys=$(gmt math -Q $yt 1.0 $dy ADD 0.5 MUL ADD =)
ysb=$(gmt math -Q $ys 0.5 SUB =)
ytb=$(gmt math -Q $yt 0.5 $dy MUL SUB =)
cat << EOF >> tt.lines
> base of symbol line
0 $ysb
$n_cols $ysb
> base of text line
0 $ytb
$n_cols $ytb
EOF
c=0
while [ $c -lt $n_cols ] && [ $s -lt $n ]; do # Loop over this row, but watch for end of symbols
c=$(expr $c + 1)
s=$(expr $s + 1)
x=$(gmt math -Q $c 1 SUB 0.5 ADD =)
symbol=$(sed -n ${s}p tt.lis)
echo "$x $ys k${symbol}" >> tt.symbols
name=$(echo $symbol | tr 'a-z' 'A-Z')
echo "$x $yt $name" >> tt.text
echo "$x $yt $width $dy" >> tt.bars
done
done
gmt begin GMT_App_N_$p
gmt set GMT_THEME cookbook
gmt plot -R0/$n_cols/0/$H -Jx${width}i tt.lines -Wthick -B0
gmt plot -S${width}i -W0.5p tt.symbols -Ggray
gmt plot -Sri -Gblack tt.bars
# Shorten the spelling of QR_TRANSPARENT to QR_TRANSP to fit the figure
sed -e 's/TRANSPARENT/TRANSP/' < tt.text | gmt text -F+f${fs}p,white
gmt end show
done
You may find it convenient to examine some of these and use them as a starting point for your own design; they can be found in GMT’s share/custom directory. In addition to the ones listed in Figure Custom symbols you can use the symbol QR to place the GMT QR Code that links to www.generic-mapping-tools.org; alternatively use QR_transparent to not plot the background opaque white square.
18.2. The macro language¶
To make your own custom plot symbol, you will need to design your own
*.def files. This section defines the language used to build custom
symbols. You can place these definition files in your current directory
or in your ~/.gmt
user directory. When designing the symbol you are working
in a relative coordinate system centered on (0,0). This point will be
mapped to the actual location specified by your data coordinates.
Furthermore, your symbol should be constructed within the domain
\({-\frac{1}{2},+\frac{1}{2},-\frac{1}{2},+\frac{1}{2}}\), resulting
in a 1 by 1 relative canvas area. This 1 x 1 square will be scaled to your
actual symbol size when plotted. However, there are no requirement that
all your design fit inside this domain. This command will produce a nice
template for you to draw your design:
gmt basemap -R-0.5/0.5/-0.5/0.5 -JX15c -Bpa0.1fg0.02 -Bsg0.1 --GRID_PEN_PRIMARY=faint -pdf template
18.2.1. Comment lines¶
Your definition file may have any number of comment lines, defined to begin with the character #. These are skipped by GMT but provides a mechanism for you to clarify what your symbol does.
18.2.2. Symbol variables¶
Simple symbols, such as circles and triangles, only take a single parameter: the symbol size, which is either given on the command line (via -Sk) or as part of the input data. However, more complicated symbols that involve angles, or conditional tests, may require more parameters. If your custom symbol requires more than the implicit single size parameter you must include the line
N: n_extra_parameters [types]
before any other macro commands. It is an optional statement in that n_extra_parameters will default to 0 unless explicitly set. By default the extra parameters are considered to be quantities that should be passed directly to the symbol machinery. However, you can use the types argument to specify different types of parameters and thus single out parameters for pre-processing. The available types are
a Geographic azimuth (positive clockwise from north toward east). Parameters identified as azimuth will first be converted to map angle (positive counter-clockwise from horizontal) given the current map projection (or simply via 90-azimuth for Cartesian plots). We ensure the angles fall in the 0-360 range and any macro test can rely on this range.
l Length, i.e., an additional length scale (in cm, inch, or point as per PROJ_LENGTH_UNIT) in addition to the given symbol size.
o Other, i.e., a numerical quantity to be passed to the custom symbol unchanged.
r rotation angles (positive counter-clockwise from horizontal). We ensure the angles fall in the 0-360 range and any macro test can rely on this range.
s String, i.e., a single column of text to be placed by the l command. Use octal \040 to include spaces to ensure the text string remains a single word.
To use the extra parameters in your macro you address them as $1, $2, etc. There is no limit on how many parameters your symbol may use. To access the trailing text in the input file you use $t and for a particular word (number k = 0, 1, …) in the trailing text you use $tk.
18.2.3. Macro commands¶
The custom symbol language contains commands to rotate the relative coordinate system, draw free-form polygons and lines, change the current fill and/or pen, place text, and include basic geometric symbols as part of the overall design (e.g., circles, triangles, etc.). The available commands are listed in Table custsymb. Note that all angles in the arguments can be provided as variables while the remaining parameters are constants.
Name |
Code |
Purpose |
Arguments |
---|---|---|---|
arc |
A |
Append circular arc to existing path |
\(x_c, y_c, d, \alpha_1, \alpha_2\) |
drawto |
D |
Draw line from previous point |
\(x, y\) |
moveto |
M |
Set a new anchor point |
\(x_0, y_0\) |
rotate |
O |
Rotate the coordinate system |
\(\alpha\)[a] |
EPS |
P |
Place an Encapsulated PostScript file |
\(x, y, size, name\) |
stroke |
S |
Stroke existing path only |
|
texture |
T |
Change current pen and fill |
|
star |
a |
Plot a star |
\(x, y, size\) |
circle |
c |
Plot a circle |
\(x, y, size\) |
diamond |
d |
Plot a diamond |
\(x, y, size\) |
ellipse |
e |
Plot an ellipse |
\(x, y, \alpha\),major,minor |
octagon |
g |
Plot an octagon |
\(x, y, size\) |
hexagon |
h |
Plot a hexagon |
\(x, y, size\) |
invtriangle |
i |
Plot an inverted triangle |
\(x, y, size\) |
rotrectangle |
j |
Plot an rotated rectangle |
\(x, y, \alpha, width, height\) |
letter |
l |
Plot a letter |
\(x, y, size, string\) |
marc |
m |
Plot a math arc (no heads) |
\(x, y, r, \alpha_1, \alpha_2\) |
pentagon |
n |
Plot a pentagon |
\(x, y, size\) |
rect |
r |
Plot a rectangle |
\(x, y, width, height\) |
roundrect |
R |
Plot a rounded rectangle |
\(x, y, width, height, radius\) |
square |
s |
Plot a square |
\(x, y, size\) |
triangle |
t |
Plot a triangle |
\(x, y, size\) |
wedge |
w |
Plot a wedge |
\(x, y, d, \alpha_1, \alpha_2\) |
cross |
x |
Plot a cross |
\(x, y, size\) |
y-dash |
y |
Plot a y-dash |
\(x, y, size\) |
x-dash |
- |
Plot a x-dash |
\(x, y, size\) |
plus |
+ |
Plot a plus sign |
\(x, y, size\) |
Note for O: if an a is appended to the angle then \(\alpha\) is considered to be a map azimuth; otherwise it is a Cartesian map angle. The a modifier does not apply if the angle is given via a variable, in which case the type of angle has already been specified via N: above and already converged before seen by O. Finally, the O command can also be given the negative of a variable, e.g., -$2 to undo a rotation, if necessary.
18.2.4. Symbol fill and outline¶
Normally, symbols, polygons and lines will be rendered using any fill and outline options you have given on the command line, similarly to how the regular built-in symbols behave. For M, T, and all the lower-case symbol codes you may optionally append specific pens (with -Wpen) and fills (with -Gpen). These options will force the use of these settings and ignore any pens and fills you may or may not have specified on the command line. Passing -G- or -W- means a symbol or polygon will have no fill or outline, respectively, regardless of what your command line settings are. Unlike pen options on the command line, a pen setting inside the macro symbol offers more control. Here, pen width is a dimension and you can specify it in three different ways: (1) Give a fixed pen width with trailing unit (e.g., -W1p,red); we then apply that pen exactly as it is regardless of the size of the symbol, (2) give a normalized pen thickness in the 0-1 range (e.g., -W0.02); at run-time this thickness will be multiplied by the current symbol size to yield the actual pen thickness, and (3) specify a variable pen thickness (e.g., -W$1,blue); we then obtain the actual pen thickness from the data record at run-time. Finally, you may indicate that a symbol or polygon should be filled using the color of the current pen instead of the current fill; do this by specifying -G+p. Likewise, you may indicate that an outline should be drawn with the color of the current fill instead of the current pen; do this by appending +g to your -W setting (which may also indicate pen thickness and texture). E.g., -W1p,-+g would mean “draw the outline with a 1p thick dashed pen but obtain the color from the current fill”.
18.2.5. Symbol substitution¶
Custom symbols that need to plot any of the standard geometric symbols (i.e., those controlled by a single size) can make the symbol code a variable. By specifying ? instead of the symbol codes a, c, d, g, h, i, n, +, s, t, x, -, or y the actual symbol code is expected to be found at the end of each data record. Such custom symbols must be invoked with -SK rather than -Sk.
18.2.6. Text substitution¶
Normally, the l macro code will place a hard-wired text string. However, you can also obtain the entire string from your input file via a single symbol variable $t that must be declared with type s (string). The string will be taken as all trialing text in your data record. To select a single word from the trailing text you just use $tk, where k starts at 0 for the first word, regardless of how many numerical columns that precede it. For each word you plan to use you must add a type s above. Words must be separated by one tab or space only. To place the dollar sign $ itself you must use octal \044 so as to not confuse the parser with a symbol variable. The string itself, if obtained from the symbol definition file, may contain special codes that will be expanded given information from the current record. You can embed the codes %X or %Y to add the current longitude (or x) and latitude (or y) in your label string. You may also use $n (n is 1, 2, etc.) to embed a numerical symbol variable as text. It will be formatted according to FORMAT_FLOAT_MAP, unless you append the modifiers +X (format as longitude via FORMAT_GEO_MAP), +Y (format as latitude via FORMAT_GEO_MAP), or +T (format as calendar time via FORMAT_DATE_MAP and FORMAT_CLOCK_MAP.
18.2.7. Text alignment and font attributes¶
Like the Sl symbol in plot, you can change the current font by appending to l the modifier +ffont [FONT_ANNOT_PRIMARY] and change the text justification by appending the modifier +jjustify [CM]. Note: Here, the font specification will only be considered for the font type and not its size (which is set separately by your size argument) or color and outline (which are set separately by -G and -W arguments). Finally, there are two ways to specify the font size. If a fixed font size is given in points (e.g,, 12p) then the text will be set at that size regardless of the symbol size specified in -S. Without the trailing p we interpret the size as a relative size in the 0-1 range and the actual font size will then scale with the symbol size, just like other symbol items.
18.2.8. Conditional statements¶
There are two types of conditional statements in the macro language: A simple condition preceding a single command, or a more elaborate if-then-elseif-else construct. In any test you may use one (and only one) of many logical operators, as listed in Table custop.
Operator |
Purpose |
---|---|
< |
Is left less than right? |
<= |
Is left less than or equal to right? |
== |
Is left equal to right? |
!= |
Is left not equal to right? |
>= |
Is left greater than or equal to right? |
> |
Is left greater than right? |
% |
Does left have a remainder with right? |
!% |
Is left an exact multiple of right? |
<> |
Is left within the exclusive range of right? |
[] |
Is left within the inclusive range of right? |
<] |
Is left within the in/ex-clusive range of right? |
[> |
Is left within the ex/in-clusive range of right? |
Above, left refers to one of your variable arguments (e.g., $1, $2) or any constant (e.g. 45, 2c, 1i) on the left hand side of the operator. On the right hand side of the operator, right is either one of your other variables, or a constant, or a range indicated by two colon-separated constants or variables (e.g., 10:50, $2:60, $3:$4, etc.). You can also use $x and $y for tests involving the current point’s longitude (or x) and latitude (or y) values, respectively. Note that any tests involving $x will not consider the periodicity of longitudes. Finally, $s can be used to access the current symbol size. Note that symbol size internally is converted to inches so any test you write that compares the size to a constant should use a constant with the appropriate unit appended (e.g., 2c). For text comparison note that case will be considered, so “A” does not equal “a”.
18.2.8.1. Simple conditional test¶
The simple if-test uses a one-line format, defined as
if left operator right then command
where left must be one of the symbol parameters, specified as $1, $2, $3, etc., or a constant. You must document what these additional parameters control. For example, to plot a small cyan circle at (0.2, 0.3) with diameter 0.4 only if $2 exceeds 45 you would write
if $2 > 45 then 0.2 0.3 0.4 c -Gcyan
Note that this form of the conditional test has no mechanism for an else branch, but this can be accomplished by repeating the test but reversing the logic for the second copy, e.g.,
if $1 > 10 then 0 0 0.5 c -Gred if $1 <= 10 then 0 0 0.5 c -Gblue
or you may instead consider the complete conditional construct below. Using a comparison between variables is similarly straightforward:
if $2 > $3 then 0.2 0.3 0.4 c -Ggreen
If you are comparing text strings then $t can be on either side of the operator and the other side would be a string constant (in quotes if containing spaces).
18.2.8.2. Complete conditional test¶
The complete conditional test uses a multi-line format, such as
The elseif (one or more) and else branches are optional. Note that the syntax is strictly enforced, meaning the opening brace must appear after then with nothing following it, and the closing brace must appear by itself with no other text, and that the elseif and else statements must have both closing and opening braces on the same line (and nothing else). If you need comments please add them as separate lines. You may nest tests as well (up to 10 levels deep), e.g.,
if $1 > 45 then { if $2 [> 0:10 then 0 0 0.5 c -Gred } elseif $1 < 15 then { if $2 [> 0:10 then 0 0 0.5 c -Ggreen } else { if $2 [> 10:20 then { 0 0 M -W1p,blue 0.3 0.3 D S 0.3 0.3 0.3 c -Gcyan } }