Skip to content

Commit 3cbc8ca

Browse files
author
Jeff Whitaker
committed
Merge pull request #124 from barronh/master
Convenience Modifications to Basemap for LCC
2 parents 1d7664c + 8a89076 commit 3cbc8ca

File tree

3 files changed

+134
-7
lines changed

3 files changed

+134
-7
lines changed

examples/camx.sample.nc

34.7 KB
Binary file not shown.

examples/plotozone.py

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
# make plot of ozone concentration data on
2+
# lambert conformal conic map projection, drawing coastlines, state and
3+
# country boundaries, and parallels/meridians.
4+
5+
# the data is interpolated to the native projection grid.
6+
from mpl_toolkits.basemap import Basemap, shiftgrid
7+
import numpy as np
8+
import matplotlib.pyplot as plt
9+
import netCDF4
10+
plt.rcParams['text.usetex'] = False
11+
12+
# read in netCDF4 file. Results from CAMx v6
13+
# test case, converted to netcdf by PseudoNetCDF
14+
# pseudonetcdf.googlecode.com
15+
camx = netCDF4.Dataset('camx.sample.nc')
16+
17+
#alternatively read directly from CAMx uamiv file
18+
#if available
19+
#
20+
# from PseudoNetCDF.camxfiles.Memmaps import uamiv
21+
# camx = uamiv('camx.bin')
22+
23+
# Get Ozone Variable
24+
o3 = camx.variables['O3']
25+
26+
# Get projection space
27+
llcrnrx = camx.XORIG
28+
llcrnry = camx.YORIG
29+
urcrnrx = llcrnrx + (o3[:].shape[-1] + 1) * camx.XCELL
30+
urcrnry = llcrnry + (o3[:].shape[-2] + 1) * camx.XCELL
31+
32+
# Get edge values for pcolor
33+
xedge = np.linspace(0, urcrnrx - llcrnrx, camx.NCOLS + 1)
34+
yedge = np.linspace(0, urcrnry - llcrnry, camx.NCOLS + 1)
35+
X, Y = np.meshgrid(xedge, yedge)
36+
37+
38+
# setup of basemap ('lcc' = lambert conformal conic).
39+
# projection parameters from CAMx file
40+
m = Basemap(projection = 'lcc',
41+
lon_0=camx.P_GAM, lat_0 = 40.,
42+
lat_1 = camx.P_ALP, lat_2 = camx.P_BET,
43+
llcrnrx = llcrnrx, llcrnry = llcrnry,
44+
urcrnry = urcrnry, urcrnrx = urcrnrx)
45+
46+
# create the figure.
47+
fig=plt.figure(figsize=(8,8))
48+
49+
# add an axes.
50+
ax = fig.add_axes([0.1,0.1,0.8,0.8])
51+
ax.set_axis_bgcolor('lightgrey')
52+
# associate this axes with the Basemap instance.
53+
m.ax = ax
54+
55+
# plot tile plot with pcolor
56+
# Use first time and first layer (i.e., o3[0, 0] (time, layer, row, col))
57+
# Edge cells have precisely 0 value, and are masked
58+
# to avoid an unnecessary color range.
59+
# Each color bin contains precisely 10% of values
60+
# which makes for a pretty plot.
61+
from matplotlib.colors import ListedColormap
62+
WhGrYlBu = ListedColormap(['#ffffff', '#b7f6ff', '#70edff', '#29e4ff', '#00e1fb', '#0fffc6', '#3bffa4', '#68ff82', '#94ff60', '#c0ff3e', '#edff1c', '#fff400', '#ffc700', '#ff9b00', '#ff6e00', '#ff4200', '#ff1500', '#e80000', '#bb0000', '#8f0000'])
63+
#.from_list('WhGrYlBu', ['white', 'white', 'cyan', 'lightblue', 'lightgreen', 'green', 'yellow', 'orange', 'red', 'red'])
64+
65+
toplot = np.ma.masked_values(o3[0, 0], 0.) * 1000.
66+
bounds = np.percentile(toplot.compressed().ravel(), np.linspace(5, 95, 9).tolist())
67+
ptch = m.pcolor(X, Y, toplot, cmap = WhGrYlBu, norm = plt.matplotlib.colors.BoundaryNorm(bounds, 20), vmin = bounds[0], vmax = bounds[-1])
68+
69+
# Add a colorbar using proportional spacing, but
70+
# colors based on 10 distinct bins
71+
cb = m.colorbar(ptch, location='right',pad='10%', boundaries = bounds, spacing = 'proportional', format = '%.3f', extend = 'both') # draw colorbar
72+
73+
# Add units to the colorbar
74+
cb.ax.set_xlabel('%s*1000.' % o3.units.strip())
75+
76+
77+
# plot blue dot on Houston, Baton Rouge, and Atlanta
78+
def add_dot(lon, lat, label):
79+
xpt,ypt = m(lon,lat)
80+
m.plot([xpt],[ypt],'bo')
81+
ax.annotate(label, xy=(xpt, ypt), xytext=(xpt+1e5, ypt+1e5),
82+
bbox=dict(boxstyle="round4", fc="w"),
83+
arrowprops=dict(facecolor='black'),
84+
)
85+
add_dot(-95.361328,29.754505, 'Houston')
86+
add_dot(-91.140320, 30.458283, 'Baton Rouge')
87+
add_dot(-84.387982, 33.748995, 'Atlanta')
88+
# draw coastlines and political boundaries.
89+
m.drawcoastlines()
90+
m.drawcountries()
91+
m.drawstates()
92+
# draw parallels and meridians.
93+
# label on left, right and bottom of map.
94+
parallels = np.arange(20.,60,10.)
95+
m.drawparallels(parallels,labels=[1,1,0,1])
96+
meridians = np.arange(-120., 70.,10.)
97+
m.drawmeridians(meridians,labels=[1,1,0,1])
98+
99+
# set title.
100+
ax.set_title('O$_3$ as predicted by the CAMx v6 Test-Case\neach color division has 10% of cells 5-95% and 5% in triagles')
101+
import textwrap
102+
histstr = 'Processing: %s' % '\n'.join(textwrap.wrap(camx.history.strip(), 140))
103+
104+
fig.text(0.01, 0.01, histstr, horizontalalignment = 'left', verticalalignment = 'bottom', size = 8)
105+
plt.draw()
106+
plt.show()

lib/mpl_toolkits/basemap/__init__.py

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -703,13 +703,19 @@ def __init__(self, llcrnrlon=None, llcrnrlat=None,
703703
if lat_2 is None:
704704
projparams['lat_2'] = lat_1
705705
if not using_corners:
706-
if width is None or height is None:
707-
raise ValueError('must either specify lat/lon values of corners (llcrnrlon,llcrnrlat,ucrnrlon,urcrnrlat) in degrees or width and height in meters')
708-
if lon_0 is None or lat_0 is None:
709-
raise ValueError('must specify lon_0 and lat_0 when using width, height to specify projection region')
710-
llcrnrlon,llcrnrlat,urcrnrlon,urcrnrlat = _choosecorners(width,height,**projparams)
711-
self.llcrnrlon = llcrnrlon; self.llcrnrlat = llcrnrlat
712-
self.urcrnrlon = urcrnrlon; self.urcrnrlat = urcrnrlat
706+
using_cornersxy = (None not in [llcrnrx,llcrnry,urcrnrx,urcrnry])
707+
if using_cornersxy:
708+
llcrnrlon,llcrnrlat,urcrnrlon,urcrnrlat = _choosecornersllur(llcrnrx,llcrnry,urcrnrx,urcrnry,**projparams)
709+
self.llcrnrlon = llcrnrlon; self.llcrnrlat = llcrnrlat
710+
self.urcrnrlon = urcrnrlon; self.urcrnrlat = urcrnrlat
711+
else:
712+
if width is None or height is None:
713+
raise ValueError('must either specify lat/lon values of corners (llcrnrlon,llcrnrlat,ucrnrlon,urcrnrlat) in degrees or width and height in meters')
714+
if lon_0 is None or lat_0 is None:
715+
raise ValueError('must specify lon_0 and lat_0 when using width, height to specify projection region')
716+
llcrnrlon,llcrnrlat,urcrnrlon,urcrnrlat = _choosecorners(width,height,**projparams)
717+
self.llcrnrlon = llcrnrlon; self.llcrnrlat = llcrnrlat
718+
self.urcrnrlon = urcrnrlon; self.urcrnrlat = urcrnrlat
713719
elif projection == 'stere':
714720
if k_0 is not None:
715721
projparams['k_0']=k_0
@@ -5078,6 +5084,21 @@ def _choosecorners(width,height,**kwargs):
50785084
else:
50795085
return corners
50805086

5087+
def _choosecornersllur(llcrnrx, llcrnry, urcrnrx, urcrnry,**kwargs):
5088+
"""
5089+
private function to determine lat/lon values of projection region corners,
5090+
given width and height of projection region in meters.
5091+
"""
5092+
p = pyproj.Proj(kwargs)
5093+
urcrnrlon, urcrnrlat = p(urcrnrx, urcrnry, inverse=True)
5094+
llcrnrlon, llcrnrlat = p(llcrnrx, llcrnry, inverse=True)
5095+
corners = llcrnrlon,llcrnrlat,urcrnrlon,urcrnrlat
5096+
# test for invalid projection points on output
5097+
if llcrnrlon > 1.e20 or urcrnrlon > 1.e20:
5098+
raise ValueError('width and/or height too large for this projection, try smaller values')
5099+
else:
5100+
return corners
5101+
50815102
def maskoceans(lonsin,latsin,datain,inlands=True,resolution='l',grid=5):
50825103
"""
50835104
mask data (``datain``), defined on a grid with latitudes ``latsin``

0 commit comments

Comments
 (0)