gmt
gmt copied to clipboard
Function to allow easier annotations outside the map region
The original idea comes from my old post (in Chinese) 8 years ago. The recent issue report https://github.com/GenericMappingTools/pygmt/issues/2001 and forum question reminds me this tricky again. So I would like to see if it can be done in PyGMT or even GMT.
The problem
If someone wants to plot a map and make annotations outside the map, he will face at least two difficulties:
- The annotation is outside the map region. It's impossible to specify its location using a valid geographic coordinate system, although it's possible to use
text
module's-D
option to offset the annotations. - It's difficult to plot a line that crosses the map boundary (related to https://github.com/GenericMappingTools/gmt/issues/5766)
The GMT CLI solution
The solution is actually easy. We just need to define a new, bigger Cartesian frame, write annotations and draw lines in the new frame, and revert to the original frame when finished.
Here is a working solution using GMT CLI:
gmt begin map png
gmt coast -Rg -JH20c -Ggray -Slightblue
############################################################
# Define a Cartesian frame.
# Now all locations are given with respect to this frame.
gmt plot -R0/25/0/15 -Jx1c -Ba1f1g1 -T
echo 21 10 ANNOTATION | gmt text -F+jBL
echo 14.5 7.5 21 10 | gmt plot -Sv0.3c+s+be+gblue -W1p,blue
# Revert to the original frame
gmt plot -Rg -JH20c -T
############################################################
# now all locations are given with respect to the original geographic frame
echo -93 37 | gmt plot -Sa0.5c -Gred
gmt end show
As you can see, here I define a new Cartesian map frame using -R0/25/0/15 -Jx1c
, so the frame is 25 cm x 15 cm. I also use -Ba1f1g1
to draw the gridlines so that I can easily determine the location of "annotation" and start/end points of the vector. The intermediate figure looks like this:
In the final figure, we don't want to see the gridlines and the Cartesian frame. So we just need to remove -Ba1f1g1
. Thus, the final script is:
gmt begin map png
gmt coast -Rg -JH20c -Ggray -Slightblue
############################################################
# Define a Cartesian frame.
# Now all locations are given with respect to this frame.
gmt plot -R0/25/0/15 -Jx1c -Ba1f1g1 -T
echo 21 10 ANNOTATION | gmt text -F+jBL
echo 14.5 7.5 21 10 | gmt plot -Sv0.3c+s+be+gblue -W1p,blue
# Revert to the original frame
gmt plot -Rg -JH20c -T
############################################################
# now all locations are given with respect to the original geographic frame
echo -93 37 | gmt plot -Sa0.5c -Gred
gmt end show
Proposing a new module for GMT
The above syntax is straight forward but it's not elegant, because we still have to revert to the original frame. Perhaps GMT can add a new module (e.g., ruler
or annot
) to simplify this. With this new module, the new syntax will look like:
gmt begin map png
gmt coast -Rg -JH20c -Ggray -Slightblue
############################################################
gmt ruler begin -S25/15 -Ba1f1g1
echo 21 10 ANNOTATION | gmt text -F+jBL
echo 14.5 7.5 21 10 | gmt plot -Sv0.3c+s+be+gblue -W1p,blue
gmt ruler end
############################################################
# now all locations are given with respect to the original geographic frame
echo -93 37 | gmt plot -Sa0.5c -Gred
gmt end show
The PyGMT version
The equivalent PyGMT syntax will look like this:
import pygmt
fig = pygmt.Figure()
fig.coast(region="g", projection="H20c", land="gray", water="lightblue")
with fig.ruler(width=25, height=15, showframe=True):
fig.text(x=21, y=10, text="ANNOTATION")
fig.plot(data=[14.5, 7.5, 21, 10], style="v0.3c+s+be+gblue", pen="1p,blue")
fig.plot(x=-93, y=37, style="a0.5c", color="red")
fig.show()
It's easy to wrap the proposed GMT's ruler
module. If if ruler
is not implemented in GMT, the Figure.ruler()
function is still doable in PyGMT. The only difficult I can see is how to remember the original map projection and region parameter before entering Figure.ruler
, so that we can revert to the original frame when exiting Figure.ruler
.
It seems @joa-quim also implemented a similar feature in GMT.jl (https://www.generic-mapping-tools.org/GMTjl_doc/examples/misc/mix_paper_geog/) recently.
@PaulWessel Do you think it should be done in GMT or not?
Shit, I had written half a page in response and whoops somehow GitHub decided to clear the box...
I will write more later but the essence of my comment was, yes, we need a module with begin/end. My suggestion was gmt cartesian but ruler is OK too. I think paper is to nondescript. I think no clipping is default, and -Cg (clip to geographic area) and -Cr Clip to rectangular area are exclusive options. Not sure if any other option is required?
I'm transferring the issue to the upstream GMT repository.
Close this?