Script to Create a Map with Defined Width and Height

I’m sharing this basic script (probably for my future self) that creates a map by specifying its width and height. The maximum longitude is calculated using mapproject and then used to generate the map. In this example, the Mercator map is 16 cm × 9 cm.

Script

Lon_Min=-100
Lon_Max=-60
Lat_Min=10
Width=16c
Height=9c
Projection=M

gmt begin map png
  # Get Lon_Max for desired height
  Lat_Max=$(gmt mapproject -J$Projection$Width -R${Lon_Min}/${Lon_Max}/${Lat_Min}/89.9999 -Wx$Width/$Height -o1) 
  gmt coast -R${Lon_Min}/${Lon_Max}/${Lat_Min}/${Lat_Max} -G200 -Sdodgerblue2 -N1/0.2,-
  #gmt mapproject -Wh # Check map height
gmt end
3 Likes

mapproject is too often overlooked. It seems a good post to remind doc readers that -J also has these flags :

  • Append +dh to the given [dimension] to specify map height.
  • Append +du to the given [dimension] to select the minimum map dimension.
  • Append +dl to the given [dimension] to select the maximum map dimension

Thanks @PlanetGus!

I think I don’t full understand +du and +dl. When are these modifiers useful? For oblique maps?

Well, given a specific projection and region you got an intrinsic aspect ratio

  • [default] dw : you specify the exact width
  • dh : you specify the exact height
  • du : allocates the dimension to the smallest between width and height
  • dl : allocates the dimension to the greatest between width and height

For example, Argentina has a greater N-S extent than E-W, so
dw = dl and du = dh.

And if you specify a square region, dw=dl=du=dh.

2 Likes

This is an extremely useful feature that I was unaware of.

Tim, are you like Paul that used to wake up the chickens?
(I’ll reply to the other thread when I have some news to provide)

I’m in UTC+13 hours timezone. It’s morning here.

And no, I don’t wake up the chickens. They wake me up - people’s chickens just wander around wherever. And the roosters don’t restrict themselves to making noise in morning, they go all day at times!

Is there a way to do this for an oblique mercator projection?

I think so. You probably will need to use mapproject -Wo I think. Have you try? What problem did you get?

One other useful feature is +r appended to the -R region.

Normally the format is -Rwest/east/south/north, which is fine for projections like Mercator where the parallels and meridians are horizontal and vertical. But for other projections one ends up with non-rectangular maps. +r solves this. The format is -Rbllon/bllat/trlon/tllat+r where blon/bllat and trlon/trlat are the coordinates of the bottom left and top-right corners.

Here are examples I was just looking at (just the projection relevant part of the code is shown - there was other data preparation I’m not showing):

gmt grdimage t2m.nc -JT-68/0/20 -R-78/-47/-58/-24+r

yields as nice rectangular map:

gmt grdimage t2m.nc -JT-68/0/20 -R-78/-58/-47/-24

yields a not so nice map:

At the very least the dh modifier works:

gmt grdimage t2m.nc -JT-68/0/20+dh -R-78/-47/-58/-24+r

yields a map 20cm high instead of 20cm wide (the above two examples were 20cm wide). You can tell by comparing the map title against the actual map.

Thanks @timhume!

How can you set the width AND the heigth of map like that? What values do I have to put here -R-78/-47/-58/-24+r to get a 20 cm wide and 25 cm height map (or any other value)?

It would be nice to have an option like GIS. You could specify a projection, a center, and an azimuth of a meridian or whatever is needed, and then also specify map dimensions (width and height), and then GMT calculates the region parameters.

Hi Esteban, your script looks solid for defining custom map dimensions! One quick fix: you’re manually setting Lon_Max=-60, but then recalculating it using mapproject — instead, set an initial Lat_Max, then compute Lon_Max dynamically to fit your desired width and height using -Wn instead of -Wx. Also, be sure to check the output of mapproject is captured correctly (e.g., using $(…) with correct quoting) to avoid parsing issues.

Possibly related A smarter selection of the region when using -R...r with non-cartesian projections · Issue #3099 · GenericMappingTools/gmt · GitHub
And there is a mention of an implemented solution (but did not revisit this to revive memory).

I’m not sure. I tried your initial example but changing the projection to transverse Mercator, and it didn’t seem correct. But what you’re doing with Mercator would be extremely useful to be generalised to all map projections.

The obvious use case is when you have paper of a particular size (or a region on the paper), and you want your map to fit this real-estate nicely.

I have made a bash function which computes the region for a map of user specified width and height, which should work on all map projections. If someone can simplify the function it would be good - it’s a bit convoluted at the moment.

The usage of the function is this:

rectreg proj scale clon clat wid hgt [bnds]

where

  • proj is the GMT map projection (e.g. ‘m’ for Mercator, s-180/-90/ for polar stereographic at the South Pole)
  • scale is the map scale (e.g. 1:10000000) at (clon,clat)
  • clon is the central longitude of the map on the paper (degrees East)
  • clat is the central latitude of the map on the paper (degrees North)
  • wid is the width of the map on the paper (cm)
  • hgt is the height of the map on the paper (cm)
  • bnds is the optional domain to perform computations on. It defaults to the whole globe [-Rg], but some projections (e.g. Mercator) need a smaller domain because they are undefined at certain places on the globe. See the examples

The return value of the function is a GMT region string of this format:
-Rbllon/bllat/trlon/trlat+r

And here are some examples:

/Users/tim/ecmwf> cat rectangle
#!/usr/bin/env bash

function rectreg(){
	# Usage: rectreg proj scale clon clat wid hgt [bnds]
	#
	# Where	proj	= map projection in GMT format (e.g. m for Mercator)
	#			scale	= map scale (e.g. 1:1000000)
	#			clon	= central longitude (degrees East)
	#			clat	= central latitude (degrees North)
	#			wid	= width of map (cm)
	#			hgt	= height of map (cm)
	#			bnds	= optional maximum domain to perform calculations on [-Rg]
	bnds="${7:-"-Rg"}"
	gmt mapproject -J${1}${2} ${bnds} <<- END | awk -v w=${5} -v h=${6} \
		'{printf("%f\t%f\n%f\t%f\n", $1-w/2, $2-h/2, $1+w/2, $2+h/2)}' | \
		gmt mapproject -J${1}${2} ${bnds} -I	| awk '
			(NR==1){ printf("-R%f/%f/", $1, $2);}
			(NR==2){ printf("%f/%f+r\n", $1, $2);}'
	${3}		${4}
	END
}

gmt begin testmap1 png
	proj="m"
	scale="1:10000000"
	clon=-177
	clat=-21
	width=15
	height=15
	bnds="-R-360/0/-85/85"
	gmt coast -Df -J${proj}${scale} -BWSen -Bpxf1a5g5 -Bpyf1a5g5 \
		$(rectreg "${proj}" "${scale}" "${clon}" "${clat}" "${width}" "${height}" "${bnds}") \
		-Wthinnest,black -Ggreen -Scyan
gmt end show

gmt begin testmap2 png
	proj="t-177/0/"
	scale="1:10000000"
	clon=-177
	clat=-21
	width=15
	height=10
	bnds="-R-350/0/-90/90"
	gmt coast -Df -J${proj}${scale} -BWSen -Bpxf1a5g5 -Bpyf1a5g5 \
		$(rectreg "${proj}" "${scale}" "${clon}" "${clat}" "${width}" "${height}" "${bnds}") \
		-Wthinnest,black -Ggreen -Scyan
gmt end show

gmt begin testmap3 png
	proj="s-180/-90/"
	scale="1:50000000"
	clon=-180
	clat=-90
	width=15
	height=10
	gmt coast -Df -J${proj}${scale} -BWSen -Bpxf1a5g5 -Bpyf1a5g5 \
		$(rectreg "${proj}" "${scale}" "${clon}" "${clat}" "${width}" "${height}" "${bnds}") \
		-Wthinnest,black -Ggreen -Scyan
gmt end show

And here are the example plots

Here is a better bash function. The central longitudes and latitudes are no longer needed because they can be specified in the GMT projection option (e.g. -Jm-177/-21/1:10000000)

function rectreg(){
    # Usage: rectreg proj scale wid hgt
    #
    proj="${1}"         # Map projection in GMT format (e.g. m for Mercator).
    scale="${2}"        # Map scale (e.g. 1:1000000).
    wid="${3}"          # Width of map (cm).
    hgt="${4}"          # Height of map (cm).

    projreg=$(echo "${scale##*:}" "${wid}" "${hgt}" | \
		awk '{printf("-R%f/%f+ue", $1*$2/200, $1*$3/200)}')
    gmt mapproject -J${proj}${scale} ${projreg} -WO
}

The above function makes use of the functionality where one can specify the region in projected units (the +ue modifier to -R)

I do suspect the monstrosity of a function presented in the previous post may be a bit more general. For example if you have a polar stereographic projection, but you want the centre of the map (when printed) to be somewhere other than the pole, but you still want the standard longitude and latitude of the projection to be at the pole.

1 Like

Tim,

I stole your first bash function (you are absolutely right about its beauty :smiley:) and turned it into a GMT.jl function. Unfortunately (or the contrary, don’t know) it uncovered a -Baf bug

julia> R, J = mapsize2region(proj="s-180/-90/", scale="1:50000000", clon=-180, clat=-90, width=15, height=10)
("56.30993247/-51.13501850/-123.69006753/-51.13501850+r", "s-180/-90/1:50000000")

julia> coast(R=R, J=J, shore=1, show=true, Vd=1)
        pscoast  -R56.30993247/-51.13501850/-123.69006753/-51.13501850+r -Js-180/-90/1:50000000 -Baf -BWSen -W1 -Da -P -K > c:\TMP/GMTjl_j.ps

Note that the -21 in -Jm-177/-21/1:10000000 means the parallel of preserved scale in the Mercator projection, and not the map center for a Mercator map with preserved scale at the equator.

Yes. Which is why I think the first more horrible function might be better.

I played around with the -C option to get false northings and eastings, but didn’t get to the bottom of it.