#!/usr/bin/env python3
'''An alternative way to draw parallels and meridians with basemap.
Basemap is a toolkit of matplotlib used to plot geographic maps.
With this function you can:
* Draw the latitude/longitude grid easily in one line of code, specifying the
lat/lon intervals.
* Use rcParams, so all your figures will look more consistent.
* Specify the label pad in points (instead of projection units).
* Place the labels indicating which margins will be used.
'''
import numpy as np
def latlon_grid(bmap, lon_int, lat_int, labels='lb', **kwargs):
'''Draws a lat-lon grid in an easy way.
Some default values are taken from rcParams instead of 'black' (color) and
1.0 (linewidth) which is the default in Basemap.
In Basemap, the label pad is computed in projection units. Now you can use
the keyword argument 'labelpad' to control this separation in points. If
not specified then this value is taken from rcParams.
Arguments:
bmap -- Basemap object.
lon_int, lat_int -- Difference in degrees from one longitude or latitude to
the next.
labels -- String specifying which margins will be used to write the labels.
If None, no label will be shown.
It is assummed that left/right margins (i.e. Y axes) correspond
to latitudes and top/bottom (X axes) to longitudes. It is valid
every combination of the characters 't' | 'b' | 'l' | 'r'
(top|bottom|left|right).
Ex: 'lrb' means that the longitude values will appear in bottom
margin and latitudes in left and right.
**kwargs -- Other arguments to drawparallels, drawmeridians and plt.text.
labelpad has units of points.
'''
# Proccesses arguments and rcParams for defult values
if 'color' not in kwargs:
kwargs['color'] = plt.rcParams['grid.color']
if 'linewidth' not in kwargs:
kwargs['linewidth'] = plt.rcParams['grid.linewidth']
if 'labelpad' in kwargs:
padx = pady = kwargs['labelpad']
del kwargs['labelpad']
else:
pady = plt.rcParams['xtick.major.pad']
padx = plt.rcParams['ytick.major.pad']
if 'size' in kwargs:
xfontsize = yfontsize = kwargs['size']
del kwargs['size']
elif 'fontsize' in kwargs:
xfontsize = yfontsize = kwargs['fontsize']
del kwargs['fontsize']
else:
xfontsize = plt.rcParams['xtick.labelsize']
yfontsize = plt.rcParams['ytick.labelsize']
# Vectors of coordinates
lon0 = bmap.lonmin // lon_int * lon_int
lat0 = bmap.latmin // lat_int * lat_int
lon1 = bmap.lonmax // lon_int * lon_int
lat1 = bmap.latmax // lat_int * lat_int
nlons = (lon1 - lon0) / lon_int + 1
nlats = (lat1 - lat0) / lat_int + 1
assert nlons / int(nlons) == 1, nlons
assert nlats / int(nlats) == 1, nlats
lons = np.linspace(lon0, lon1, int(nlons))
lats = np.linspace(lat0, lat1, int(nlats))
# If not specified then computes de label offset by 'labelpad'
xos = yos = None
if 'xoffset' in kwargs:
xos = kwargs['xoffset']
if 'yoffset' in kwargs:
yos = kwargs['yoffset']
if xos is None and yos is None:
# Page size in inches and axes limits
fig_w, fig_h = plt.gcf().get_size_inches()
points = plt.gca().get_position().get_points()
x1, y1 = tuple(points[0])
x2, y2 = tuple(points[1])
# Width and height of axes in points
w = (x2 - x1) * fig_w * 72
h = (y2 - y1) * fig_h * 72
# If the aspect relation is fixed then compute the real values
if bmap.fix_aspect:
aspect = bmap.aspect * w / h
if aspect > 1:
w = h / bmap.aspect
elif aspect < 1:
h = w * bmap.aspect
# Offset in projection units (meters or degrees)
xos = padx * (bmap.urcrnrx - bmap.llcrnrx) / w
yos = pady * (bmap.urcrnry - bmap.llcrnry) / h
# Set the labels
latlabels = [False] * 4
lonlabels = [False] * 4
if labels is not None:
pst = {'l': 0, 'r': 1, 't': 2, 'b': 3}
lst = {'l': latlabels, 'r': latlabels, 't': lonlabels, 'b': lonlabels}
for i in labels.lower():
lst[i][pst[i]] = True
# Draws the grid
bmap.drawparallels(lats, labels=latlabels, fontsize=yfontsize,
xoffset=xos, yoffset=yos, **kwargs)
bmap.drawmeridians(lons, labels=lonlabels, fontsize=xfontsize,
xoffset=xos, yoffset=yos, **kwargs)
# TEST
if __name__ == '__main__':
import matplotlib.pyplot as plt
import mpl_toolkits.basemap as basemap
# Some rcParams (example)
plt.rc('grid', linewidth=0.5, color='g')
plt.rcParams['xtick.major.pad'] = 6
plt.rcParams['ytick.major.pad'] = 12
plt.rcParams['xtick.labelsize'] = 'small'
plt.rcParams['ytick.labelsize'] = 'x-small'
# Basemap
m = basemap.Basemap(projection='merc', llcrnrlon=-120, llcrnrlat=30,
urcrnrlon=120, urcrnrlat=70)
# Plots a figure to compare
plt.subplot(211)
x = np.linspace(-10, 10, 10000)
y = np.sin(x)
plt.plot(x, y)
plt.grid()
plt.title('Example of figure using only rc values')
# Draws the grid (using the function)
plt.subplot(212)
latlon_grid(m, 30, 10, labels='lb', dashes=[1, 3])
m.drawcoastlines()
plt.title('Using latlon_grid()')
plt.show()
Diff to Previous Revision
--- revision 1 2013-01-05 14:41:52
+++ revision 2 2013-01-05 15:59:24
@@ -8,8 +8,8 @@
* Draw the latitude/longitude grid easily in one line of code, specifying the
lat/lon intervals.
-* Use rcParams, so all your figures will look more consistents.
-* Specify the label pad in points (instead of fraction of width/height).
+* Use rcParams, so all your figures will look more consistent.
+* Specify the label pad in points (instead of projection units).
* Place the labels indicating which margins will be used.
'''
@@ -23,10 +23,9 @@
Some default values are taken from rcParams instead of 'black' (color) and
1.0 (linewidth) which is the default in Basemap.
- In Basemap, the label pad is computed applying a factor to the dimensions
- of the figure. Now you can use the keyword argument 'labelpad' to control
- this separation in points. If not specified, this value is taken from
- rcParams.
+ In Basemap, the label pad is computed in projection units. Now you can use
+ the keyword argument 'labelpad' to control this separation in points. If
+ not specified then this value is taken from rcParams.
Arguments:
@@ -35,14 +34,14 @@
the next.
labels -- String specifying which margins will be used to write the labels.
If None, no label will be shown.
- It is assummed that left/right margins correspond to latitudes
- and top/bottom to longitudes. It is valid every combination of
- the characters 't' | 'b' | 'l' | 'r' (top|bottom|left|right).
- Ex: 'lrb' means than the longitude values will appear in bottom
+ It is assummed that left/right margins (i.e. Y axes) correspond
+ to latitudes and top/bottom (X axes) to longitudes. It is valid
+ every combination of the characters 't' | 'b' | 'l' | 'r'
+ (top|bottom|left|right).
+ Ex: 'lrb' means that the longitude values will appear in bottom
margin and latitudes in left and right.
**kwargs -- Other arguments to drawparallels, drawmeridians and plt.text.
labelpad has units of points.
-
'''
# Proccesses arguments and rcParams for defult values
if 'color' not in kwargs:
@@ -75,7 +74,7 @@
assert nlats / int(nlats) == 1, nlats
lons = np.linspace(lon0, lon1, int(nlons))
lats = np.linspace(lat0, lat1, int(nlats))
- # If not specified, computes de label offset by 'labelpad'
+ # If not specified then computes de label offset by 'labelpad'
xos = yos = None
if 'xoffset' in kwargs:
xos = kwargs['xoffset']
@@ -90,7 +89,7 @@
# Width and height of axes in points
w = (x2 - x1) * fig_w * 72
h = (y2 - y1) * fig_h * 72
- # If the aspect relation is fixed, compute the real values
+ # If the aspect relation is fixed then compute the real values
if bmap.fix_aspect:
aspect = bmap.aspect * w / h
if aspect > 1:
@@ -108,7 +107,7 @@
lst = {'l': latlabels, 'r': latlabels, 't': lonlabels, 'b': lonlabels}
for i in labels.lower():
lst[i][pst[i]] = True
- # Draw the grid
+ # Draws the grid
bmap.drawparallels(lats, labels=latlabels, fontsize=yfontsize,
xoffset=xos, yoffset=yos, **kwargs)
bmap.drawmeridians(lons, labels=lonlabels, fontsize=xfontsize,