1
- import matplotlib . numerix as NX
1
+ import numpy as npy
2
2
import pyproj
3
3
import math
4
4
5
5
__version__ = '1.2.2'
6
6
_dg2rad = math .radians (1. )
7
7
_rad2dg = math .degrees (1. )
8
8
9
+ _upper_right_out_of_bounds = (
10
+ 'the upper right corner of the plot is not in the map projection region' )
11
+
12
+ _lower_left_out_of_bounds = (
13
+ 'the lower left corner of the plot is not in the map projection region' )
14
+
15
+
9
16
class Proj (object ):
10
17
"""
11
- peforms cartographic transformations (converts from longitude,latitude
12
- to native map projection x,y coordinates and vice versa) using proj
13
- (http://proj.maptools.org/)
14
- Uses a pyrex generated C-interface to libproj.
15
-
16
- __init__ method sets up projection information.
17
- __call__ method compute transformations.
18
- See docstrings for __init__ and __call__ for details.
18
+ peforms cartographic transformations (converts from longitude,latitude
19
+ to native map projection x,y coordinates and vice versa) using proj
20
+ (http://proj.maptools.org/)
21
+ Uses a pyrex generated C-interface to libproj.
19
22
20
- Contact: Jeff Whitaker <jeffrey.s.whitaker@noaa.gov>
23
+ __init__ method sets up projection information.
24
+ __call__ method compute transformations.
25
+ See docstrings for __init__ and __call__ for details.
26
+
27
+ Contact: Jeff Whitaker <jeffrey.s.whitaker@noaa.gov>
21
28
"""
22
29
23
- def __init__ (self ,projparams ,llcrnrlon ,llcrnrlat ,urcrnrlon ,urcrnrlat ,urcrnrislatlon = True ):
30
+ def __init__ (self ,projparams ,llcrnrlon ,llcrnrlat ,
31
+ urcrnrlon ,urcrnrlat ,urcrnrislatlon = True ):
24
32
"""
25
- initialize a Proj class instance.
33
+ initialize a Proj class instance.
26
34
27
- Input 'projparams' is a dictionary containing proj map
28
- projection control parameter key/value pairs.
29
- See the proj documentation (http://www.remotesensing.org/proj/)
30
- for details.
35
+ Input 'projparams' is a dictionary containing proj map
36
+ projection control parameter key/value pairs.
37
+ See the proj documentation (http://www.remotesensing.org/proj/)
38
+ for details.
31
39
32
- llcrnrlon,llcrnrlat are lon and lat (in degrees) of lower left hand corner
33
- of projection region.
40
+ llcrnrlon,llcrnrlat are lon and lat (in degrees) of lower
41
+ left hand corner of projection region.
34
42
35
- urcrnrlon,urcrnrlat are lon and lat (in degrees) of upper right hand corner
36
- of projection region if urcrnrislatlon=True (default). Otherwise,
37
- urcrnrlon,urcrnrlat are x,y in projection coordinates (units meters),
38
- assuming the lower left corner is x=0,y=0.
43
+ urcrnrlon,urcrnrlat are lon and lat (in degrees) of upper
44
+ right hand corner of projection region if urcrnrislatlon=True
45
+ (default). Otherwise, urcrnrlon,urcrnrlat are x,y in projection
46
+ coordinates (units meters), assuming the lower left corner is x=0,y=0.
39
47
"""
40
48
self .projparams = projparams
41
49
self .projection = projparams ['proj' ]
@@ -63,8 +71,9 @@ def __init__(self,projparams,llcrnrlon,llcrnrlat,urcrnrlon,urcrnrlat,urcrnrislat
63
71
llcrnrx = llcrnrlon
64
72
llcrnry = llcrnrlat
65
73
elif self .projection == 'ortho' :
66
- if llcrnrlon == - 180 and llcrnrlat == - 90 and urcrnrlon == 180 and urcrnrlat == 90 :
67
- self ._fulldisk = True
74
+ if (llcrnrlon == - 180 and llcrnrlat == - 90 and
75
+ urcrnrlon == 180 and urcrnrlat == 90 ):
76
+ self ._fulldisk = True
68
77
self ._proj4 = pyproj .Proj (projparams )
69
78
llcrnrx = - self .rmajor
70
79
llcrnry = - self .rmajor
@@ -75,27 +84,28 @@ def __init__(self,projparams,llcrnrlon,llcrnrlat,urcrnrlon,urcrnrlat,urcrnrislat
75
84
self ._proj4 = pyproj .Proj (projparams )
76
85
llcrnrx , llcrnry = self (llcrnrlon ,llcrnrlat )
77
86
if llcrnrx > 1.e20 or llcrnry > 1.e20 :
78
- raise ValueError ('the lower left corner of the plot is not in the map projection region' )
87
+ raise ValueError (_lower_left_out_of_bounds )
79
88
elif self .projection == 'geos' :
80
89
self ._proj4 = pyproj .Proj (projparams )
81
90
# find major and minor axes of ellipse defining map proj region.
82
91
delta = 0.01
83
- lats = NX .arange (0 ,90 ,delta )
92
+ lats = npy .arange (0 ,90 ,delta )
84
93
lon_0 = projparams ['lon_0' ]
85
- lons = lon_0 * NX .ones (len (lats ),'d' )
94
+ lons = lon_0 * npy .ones (len (lats ),'d' )
86
95
x , y = self ._proj4 (lons , lats )
87
96
yi = (y > 1.e20 ).tolist ()
88
97
ny = yi .index (1 )- 1
89
98
height = y [ny ]
90
- lons = NX .arange (lon_0 ,lon_0 + 90 ,delta )
91
- lats = NX .zeros (len (lons ),'d' )
99
+ lons = npy .arange (lon_0 ,lon_0 + 90 ,delta )
100
+ lats = npy .zeros (len (lons ),'d' )
92
101
x , y = self (lons , lats )
93
102
xi = (x > 1.e20 ).tolist ()
94
103
nx = xi .index (1 )- 1
95
104
width = x [nx ]
96
105
self ._height = height
97
106
self ._width = width
98
- if llcrnrlon == - 180 and llcrnrlat == - 90 and urcrnrlon == 180 and urcrnrlat == 90 :
107
+ if (llcrnrlon == - 180 and llcrnrlat == - 90 and
108
+ urcrnrlon == 180 and urcrnrlat == 90 ):
99
109
self ._fulldisk = True
100
110
llcrnrx = - width
101
111
llcrnry = - height
@@ -105,7 +115,7 @@ def __init__(self,projparams,llcrnrlon,llcrnrlat,urcrnrlon,urcrnrlat,urcrnrislat
105
115
self ._fulldisk = False
106
116
llcrnrx , llcrnry = self (llcrnrlon ,llcrnrlat )
107
117
if llcrnrx > 1.e20 or llcrnry > 1.e20 :
108
- raise ValueError ('the lower left corner of the plot is not in the map projection region' )
118
+ raise ValueError (_lower_left_out_of_bounds )
109
119
elif self .projection in ['moll' ,'robin' ,'sinu' ]:
110
120
self ._proj4 = pyproj .Proj (projparams )
111
121
xtmp ,urcrnry = self (projparams ['lon_0' ],90. )
@@ -116,7 +126,7 @@ def __init__(self,projparams,llcrnrlon,llcrnrlat,urcrnrlon,urcrnrlat,urcrnrislat
116
126
# note that for 'cyl' x,y == lon,lat
117
127
self .projparams ['x_0' ]= - llcrnrx
118
128
self .projparams ['y_0' ]= - llcrnry
119
- # reset with x_0, y_0.
129
+ # reset with x_0, y_0.
120
130
if self .projection != 'cyl' :
121
131
self ._proj4 = pyproj .Proj (projparams )
122
132
llcrnry = 0.
@@ -136,15 +146,15 @@ def __init__(self,projparams,llcrnrlon,llcrnrlat,urcrnrlon,urcrnrlat,urcrnrislat
136
146
else :
137
147
urcrnrx ,urcrnry = self (urcrnrlon ,urcrnrlat )
138
148
if urcrnrx > 1.e20 or urcrnry > 1.e20 :
139
- raise ValueError ('the upper right corner of the plot is not in the map projection region' )
149
+ raise ValueError (_upper_right_out_of_bounds )
140
150
elif self .projection == 'geos' :
141
151
if self ._fulldisk :
142
152
urcrnrx = 2. * self ._width
143
153
urcrnry = 2. * self ._height
144
154
else :
145
155
urcrnrx ,urcrnry = self (urcrnrlon ,urcrnrlat )
146
156
if urcrnrx > 1.e20 or urcrnry > 1.e20 :
147
- raise ValueError ('the upper right corner of the plot is not in the map projection region' )
157
+ raise ValueError (_upper_right_out_of_bounds )
148
158
elif self .projection in ['moll' ,'robin' ,'sinu' ]:
149
159
xtmp ,urcrnry = self (projparams ['lon_0' ],90. )
150
160
urcrnrx ,xtmp = self (projparams ['lon_0' ]+ 180. ,0 )
@@ -172,24 +182,38 @@ def __init__(self,projparams,llcrnrlon,llcrnrlat,urcrnrlon,urcrnrlat,urcrnrislat
172
182
self .ymax = llcrnry
173
183
self .ymin = urcrnry
174
184
175
- def __call__ (self ,x ,y ,inverse = False ):
185
+ def __call__ (self , * args , ** kw ):
186
+ # x,y,inverse=False):
176
187
"""
177
- Calling a Proj class instance with the arguments lon, lat will
178
- convert lon/lat (in degrees) to x/y native map projection
179
- coordinates (in meters). If optional keyword 'inverse' is
180
- True (default is False), the inverse transformation from x/y
181
- to lon/lat is performed.
188
+ Calling a Proj class instance with the arguments lon, lat will
189
+ convert lon/lat (in degrees) to x/y native map projection
190
+ coordinates (in meters). If optional keyword 'inverse' is
191
+ True (default is False), the inverse transformation from x/y
192
+ to lon/lat is performed.
182
193
183
- For cylindrical equidistant projection ('cyl'), this
184
- does nothing (i.e. x,y == lon,lat).
194
+ For cylindrical equidistant projection ('cyl'), this
195
+ does nothing (i.e. x,y == lon,lat).
185
196
186
- lon,lat can be either scalar floats or N arrays.
197
+ lon,lat can be either scalar floats or N arrays.
187
198
"""
199
+ if len (args ) == 1 :
200
+ xy = args [0 ]
201
+ onearray = True
202
+ else :
203
+ x ,y = args
204
+ onearray = False
188
205
if self .projection == 'cyl' : # for cyl x,y == lon,lat
189
- return x ,y
206
+ if onearray :
207
+ return xy
208
+ else :
209
+ return x ,y
210
+ inverse = kw .get ('inverse' , False )
211
+ if onearray :
212
+ outxy = self ._proj4 (xy , inverse = inverse )
213
+ else :
214
+ outx ,outy = self ._proj4 (x , y , inverse = inverse )
190
215
if inverse :
191
- outx ,outy = self ._proj4 (x ,y ,inverse = True )
192
- if self .projection in ['merc' ,'mill' ]:
216
+ if self .projection in ['merc' ,'mill' ]:
193
217
if self .projection == 'merc' :
194
218
coslat = math .cos (math .radians (self .projparams ['lat_ts' ]))
195
219
sinlat = math .sin (math .radians (self .projparams ['lat_ts' ]))
@@ -199,12 +223,14 @@ def __call__(self,x,y,inverse=False):
199
223
# radius of curvature of the ellipse perpendicular to
200
224
# the plane of the meridian.
201
225
rcurv = self .rmajor * coslat / math .sqrt (1. - self .esq * sinlat ** 2 )
202
- try : # x a scalar or an array
203
- outx = _rad2dg * (x / rcurv ) + self .llcrnrlon
204
- except : # x a sequence
205
- outx = [_rad2dg * (xi / rcurv ) + self .llcrnrlon for xi in x ]
226
+ if onearray :
227
+ outxy [:,0 ] = _rad2dg * (xy [:,0 ]/ rcurv ) + self .llcrnrlon
228
+ else :
229
+ try : # x a scalar or an array
230
+ outx = _rad2dg * (x / rcurv ) + self .llcrnrlon
231
+ except : # x a sequence
232
+ outx = [_rad2dg * (xi / rcurv ) + self .llcrnrlon for xi in x ]
206
233
else :
207
- outx ,outy = self ._proj4 (x ,y )
208
234
if self .projection in ['merc' ,'mill' ]:
209
235
if self .projection == 'merc' :
210
236
coslat = math .cos (math .radians (self .projparams ['lat_ts' ]))
@@ -215,28 +241,52 @@ def __call__(self,x,y,inverse=False):
215
241
# radius of curvature of the ellipse perpendicular to
216
242
# the plane of the meridian.
217
243
rcurv = self .rmajor * coslat / math .sqrt (1. - self .esq * sinlat ** 2 )
218
- try : # x is a scalar or an array
219
- outx = rcurv * _dg2rad * (x - self .llcrnrlon )
220
- except : # x is a sequence.
221
- outx = [rcurv * _dg2rad * (xi - self .llcrnrlon ) for xi in x ]
222
- return outx ,outy
244
+ if onearray :
245
+ outxy [:,0 ] = rcurv * _dg2rad * (xy [:,0 ]- self .llcrnrlon )
246
+ else :
247
+ try : # x is a scalar or an array
248
+ outx = rcurv * _dg2rad * (x - self .llcrnrlon )
249
+ except : # x is a sequence.
250
+ outx = [rcurv * _dg2rad * (xi - self .llcrnrlon ) for xi in x ]
251
+ if onearray :
252
+ return outxy
253
+ else :
254
+ return outx , outy
223
255
224
256
def makegrid (self ,nx ,ny ,returnxy = False ):
225
257
"""
226
- return arrays of shape (ny,nx) containing lon,lat coordinates of
227
- an equally spaced native projection grid.
228
- if returnxy=True, the x,y values of the grid are returned also.
258
+ return arrays of shape (ny,nx) containing lon,lat coordinates of
259
+ an equally spaced native projection grid.
260
+ if returnxy=True, the x,y values of the grid are returned also.
229
261
"""
230
262
dx = (self .urcrnrx - self .llcrnrx )/ (nx - 1 )
231
263
dy = (self .urcrnry - self .llcrnry )/ (ny - 1 )
232
- x = self .llcrnrx + dx * NX .indices ((ny ,nx ),NX . Float32 )[1 ,:,:]
233
- y = self .llcrnry + dy * NX .indices ((ny ,nx ),NX . Float32 )[0 ,:,:]
264
+ x = self .llcrnrx + dx * npy .indices ((ny ,nx ),npy . float32 )[1 ,:,:]
265
+ y = self .llcrnry + dy * npy .indices ((ny ,nx ),npy . float32 )[0 ,:,:]
234
266
lons , lats = self (x , y , inverse = True )
235
267
if returnxy :
236
268
return lons , lats , x , y
237
269
else :
238
270
return lons , lats
239
271
272
+ def makegrid3d (self ,nx ,ny ,returnxy = False ):
273
+ """
274
+ return array of shape (ny,nx, 2) containing lon,lat coordinates of
275
+ an equally spaced native projection grid.
276
+ if returnxy=True, the x,y values of the grid are returned also.
277
+ """
278
+ dx = (self .urcrnrx - self .llcrnrx )/ (nx - 1 )
279
+ dy = (self .urcrnry - self .llcrnry )/ (ny - 1 )
280
+ xy = npy .empty ((ny ,nx ,2 ), npy .float64 )
281
+ xy [...,0 ] = self .llcrnrx + dx * npy .indices ((ny ,nx ),npy .float32 )[1 ,:,:]
282
+ xy [...,1 ] = self .llcrnry + dy * npy .indices ((ny ,nx ),npy .float32 )[0 ,:,:]
283
+ lonlat = self (xy , inverse = True )
284
+ if returnxy :
285
+ return lonlat , xy
286
+ else :
287
+ return lonlat
288
+
289
+
240
290
if __name__ == "__main__" :
241
291
242
292
params = {}
@@ -247,10 +297,10 @@ def makegrid(self,nx,ny,returnxy=False):
247
297
params ['lon_0' ] = - 107
248
298
nx = 349 ; ny = 277 ; dx = 32463.41 ; dy = dx
249
299
awips221 = Proj (params ,- 145.5 ,1.0 ,(nx - 1 )* dx ,(ny - 1 )* dy ,urcrnrislatlon = False )
250
- # AWIPS grid 221 parameters
251
- # (from http://www.nco.ncep.noaa.gov/pmb/docs/on388/tableb.html)
300
+ # AWIPS grid 221 parameters
301
+ # (from http://www.nco.ncep.noaa.gov/pmb/docs/on388/tableb.html)
252
302
llcornerx , llcornery = awips221 (- 145.5 ,1. )
253
- # find 4 lon/lat corners of AWIPS grid 221.
303
+ # find 4 lon/lat corners of AWIPS grid 221.
254
304
llcornerx = 0. ; llcornery = 0.
255
305
lrcornerx = dx * (nx - 1 ); lrcornery = 0.
256
306
ulcornerx = 0. ; ulcornery = dy * (ny - 1 )
@@ -270,13 +320,22 @@ def makegrid(self,nx,ny,returnxy=False):
270
320
print ' -68.318 0.897'
271
321
print ' -2.566 46.352'
272
322
print ' 148.639 46.635'
273
- # compute lons and lats for the whole AWIPS grid 221 (377x249).
323
+ # compute lons and lats for the whole AWIPS grid 221 (377x249).
274
324
import time ; t1 = time .clock ()
275
325
lons , lats = awips221 .makegrid (nx ,ny )
276
326
t2 = time .clock ()
277
327
print 'compute lats/lons for all points on AWIPS 221 grid (%sx%s)' % (nx ,ny )
278
328
print 'max/min lons'
279
- print min (NX .ravel (lons )),max (NX .ravel (lons ))
329
+ print min (npy .ravel (lons )),max (npy .ravel (lons ))
280
330
print 'max/min lats'
281
- print min (NX .ravel (lats )),max (NX .ravel (lats ))
331
+ print min (npy .ravel (lats )),max (npy .ravel (lats ))
332
+ print 'took' ,t2 - t1 ,'secs'
333
+ print 'Same thing but with a single 3-D array'
334
+ t1 = time .clock ()
335
+ lonlat , xy = awips221 .makegrid3d (nx ,ny , returnxy = True )
336
+ t2 = time .clock ()
282
337
print 'took' ,t2 - t1 ,'secs'
338
+
339
+ assert (lons == lonlat [...,0 ]).all (), "The longitudes are different"
340
+ assert (lats == lonlat [...,1 ]).all (), "The latitudes are different"
341
+
0 commit comments