3 ! Module that defines constants, data structures, and
4 ! subroutines used to convert grid indices to lat/lon
7 ! SUPPORTED PROJECTIONS
8 ! ---------------------
9 ! Cylindrical Lat/Lon (code = PROJ_LATLON)
10 ! Mercator (code = PROJ_MERC)
11 ! Lambert Conformal (code = PROJ_LC)
12 ! Gaussian (code = PROJ_GAUSS)
13 ! Polar Stereographic (code = PROJ_PS)
14 ! Rotated Lat/Lon (code = PROJ_ROTLL)
18 ! The routines contained within were adapted from routines
19 ! obtained from NCEP's w3 library. The original NCEP routines were less
20 ! flexible (e.g., polar-stereo routines only supported truelat of 60N/60S)
21 ! than what we needed, so modifications based on equations in Hoke, Hayes, and
22 ! Renninger (AFGWC/TN/79-003) were added to improve the flexibility.
23 ! Additionally, coding was improved to F90 standards and the routines were
24 ! combined into this module.
29 ! For mercator, lambert conformal, and polar-stereographic projections,
30 ! the routines within assume the following:
32 ! 1. Grid is dimensioned (i,j) where i is the East-West direction,
33 ! positive toward the east, and j is the north-south direction,
34 ! positive toward the north.
35 ! 2. Origin is at (1,1) and is located at the southwest corner,
36 ! regardless of hemispere.
37 ! 3. Grid spacing (dx) is always positive.
38 ! 4. Values of true latitudes must be positive for NH domains
39 ! and negative for SH domains.
41 ! For the latlon and Gaussian projection, the grid origin may be at any
42 ! of the corners, and the deltalat and deltalon values can be signed to
43 ! account for this using the following convention:
44 ! Origin Location Deltalat Sign Deltalon Sign
45 ! --------------- ------------- -------------
52 ! 1. Any arguments that are a latitude value are expressed in
53 ! degrees north with a valid range of -90 -> 90
54 ! 2. Any arguments that are a longitude value are expressed in
55 ! degrees east with a valid range of -180 -> 180.
56 ! 3. Distances are in meters and are always positive.
57 ! 4. The standard longitude (stdlon) is defined as the longitude
58 ! line which is parallel to the grid's y-axis (j-direction), along
59 ! which latitude increases (NOT the absolute value of latitude, but
60 ! the actual latitude, such that latitude increases continuously
61 ! from the south pole to the north pole) as j increases.
62 ! 5. One true latitude value is required for polar-stereographic and
63 ! mercator projections, and defines at which latitude the
64 ! grid spacing is true. For lambert conformal, two true latitude
65 ! values must be specified, but may be set equal to each other to
66 ! specify a tangent projection instead of a secant projection.
70 ! To use the routines in this module, the calling routines must have the
71 ! following statement at the beginning of its declaration block:
74 ! The use of the module not only provides access to the necessary routines,
75 ! but also defines a structure of TYPE (proj_info) that can be used
76 ! to declare a variable of the same type to hold your map projection
77 ! information. It also defines some integer parameters that contain
78 ! the projection codes so one only has to use those variable names rather
79 ! than remembering the acutal code when using them. The basic steps are
82 ! 1. Ensure the "USE map_utils" is in your declarations.
83 ! 2. Declare the projection information structure as type(proj_info):
84 ! TYPE(proj_info) :: proj
85 ! 3. Populate your structure by calling the map_set routine:
86 ! CALL map_set(code,lat1,lon1,knowni,knownj,dx,stdlon,truelat1,truelat2,proj)
88 ! code (input) = one of PROJ_LATLON, PROJ_MERC, PROJ_LC, PROJ_PS,
89 ! PROJ_GAUSS, or PROJ_ROTLL
90 ! lat1 (input) = Latitude of grid origin point (i,j)=(1,1)
92 ! lon1 (input) = Longitude of grid origin
93 ! knowni (input) = origin point, x-location
94 ! knownj (input) = origin point, y-location
95 ! dx (input) = grid spacing in meters (ignored for LATLON projections)
96 ! stdlon (input) = Standard longitude for PROJ_PS and PROJ_LC,
97 ! deltalon (see assumptions) for PROJ_LATLON,
98 ! ignored for PROJ_MERC
99 ! truelat1 (input) = 1st true latitude for PROJ_PS, PROJ_LC, and
100 ! PROJ_MERC, deltalat (see assumptions) for PROJ_LATLON
101 ! truelat2 (input) = 2nd true latitude for PROJ_LC,
102 ! ignored for all others.
103 ! proj (output) = The structure of type (proj_info) that will be fully
104 ! populated after this call
106 ! 4. Now that the proj structure is populated, you may call either
107 ! of the following routines:
109 ! latlon_to_ij(proj, lat, lon, i, j)
110 ! ij_to_latlon(proj, i, j, lat, lon)
112 ! It is incumbent upon the calling routine to determine whether or
113 ! not the values returned are within your domain's bounds. All values
114 ! of i, j, lat, and lon are REAL values.
119 ! Hoke, Hayes, and Renninger, "Map Preojections and Grid Systems for
120 ! Meteorological Applications." AFGWC/TN-79/003(Rev), Air Weather
123 ! NCAR MM5v3 Modeling System, REGRIDDER program, module_first_guess_map.F
124 ! NCEP routines w3fb06, w3fb07, w3fb08, w3fb09, w3fb11, w3fb12
128 ! 27 Mar 2001 - Original Version
129 ! Brent L. Shaw, NOAA/FSL (CSU/CIRA)
131 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
134 use misc_definitions_module
137 ! Define some private constants
138 INTEGER, PRIVATE
, PARAMETER :: HIGH
= 8
142 INTEGER :: code
! Integer code for projection TYPE
143 INTEGER :: nlat
! For Gaussian -- number of latitude points
144 ! north of the equator
147 INTEGER :: ixdim
! For Rotated Lat/Lon -- number of mass points
149 INTEGER :: jydim
! For Rotated Lat/Lon -- number of rows
150 INTEGER :: stagger
! For Rotated Lat/Lon -- mass or velocity grid
151 REAL :: phi
! For Rotated Lat/Lon -- domain half-extent in
153 REAL :: lambda
! For Rotated Lat/Lon -- domain half-extend in
155 REAL :: lat1
! SW latitude (1,1) in degrees (-90->90N)
156 REAL :: lon1
! SW longitude (1,1) in degrees (-180->180E)
157 REAL :: lat0
! For Cassini, latitude of projection pole
158 REAL :: lon0
! For Cassini, longitude of projection pole
159 REAL :: dx
! Grid spacing in meters at truelats, used
160 ! only for ps, lc, and merc projections
161 REAL :: dy
! Grid spacing in meters at truelats, used
162 ! only for ps, lc, and merc projections
163 REAL :: latinc
! Latitude increment for cylindrical lat/lon
164 REAL :: loninc
! Longitude increment for cylindrical lat/lon
165 ! also the lon increment for Gaussian grid
166 REAL :: dlat
! Lat increment for lat/lon grids
167 REAL :: dlon
! Lon increment for lat/lon grids
168 REAL :: stdlon
! Longitude parallel to y-axis (-180->180E)
169 REAL :: truelat1
! First true latitude (all projections)
170 REAL :: truelat2
! Second true lat (LC only)
171 REAL :: hemi
! 1 for NH, -1 for SH
172 REAL :: cone
! Cone factor for LC projections
173 REAL :: polei
! Computed i-location of pole point
174 REAL :: polej
! Computed j-location of pole point
175 REAL :: rsw
! Computed radius to SW corner
176 REAL :: rebydx
! Earth radius divided by dx
177 REAL :: knowni
! X-location of known lat/lon
178 REAL :: knownj
! Y-location of known lat/lon
179 REAL :: re_m
! Radius of spherical earth, meters
180 REAL :: rho0
! For Albers equal area
181 REAL :: nc
! For Albers equal area
182 REAL :: bigc
! For Albers equal area
183 LOGICAL :: init
! Flag to indicate if this struct is
185 LOGICAL :: wrap
! For Gaussian -- flag to indicate wrapping
187 REAL, POINTER, DIMENSION(:) :: gauss_lat
! Latitude array for Gaussian grid
191 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
193 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
195 SUBROUTINE map_init(proj
)
196 ! Initializes the map projection structure to missing values
199 TYPE(proj_info
), INTENT(INOUT
) :: proj
210 proj
%truelat1
= -999.9
211 proj
%truelat2
= -999.9
226 proj
%re_m
= EARTH_RADIUS_M
232 nullify(proj
%gauss_lat
)
234 END SUBROUTINE map_init
237 SUBROUTINE map_set(proj_code
, proj
, lat1
, lon1
, lat0
, lon0
, knowni
, knownj
, dx
, latinc
, &
238 loninc
, stdlon
, truelat1
, truelat2
, nlat
, nlon
, ixdim
, jydim
, &
239 stagger
, phi
, lambda
, r_earth
)
240 ! Given a partially filled proj_info structure, this routine computes
241 ! polei, polej, rsw, and cone (if LC projection) to complete the
242 ! structure. This allows us to eliminate redundant calculations when
243 ! calling the coordinate conversion routines multiple times for the
245 ! This will generally be the first routine called when a user wants
246 ! to be able to use the coordinate conversion routines, and it
247 ! will call the appropriate subroutines based on the
248 ! proj%code which indicates which projection type this is.
253 INTEGER, INTENT(IN
) :: proj_code
254 INTEGER, INTENT(IN
), OPTIONAL
:: nlat
255 INTEGER, INTENT(IN
), OPTIONAL
:: nlon
256 INTEGER, INTENT(IN
), OPTIONAL
:: ixdim
257 INTEGER, INTENT(IN
), OPTIONAL
:: jydim
258 INTEGER, INTENT(IN
), OPTIONAL
:: stagger
259 REAL, INTENT(IN
), OPTIONAL
:: latinc
260 REAL, INTENT(IN
), OPTIONAL
:: loninc
261 REAL, INTENT(IN
), OPTIONAL
:: lat1
262 REAL, INTENT(IN
), OPTIONAL
:: lon1
263 REAL, INTENT(IN
), OPTIONAL
:: lat0
264 REAL, INTENT(IN
), OPTIONAL
:: lon0
265 REAL, INTENT(IN
), OPTIONAL
:: dx
266 REAL, INTENT(IN
), OPTIONAL
:: stdlon
267 REAL, INTENT(IN
), OPTIONAL
:: truelat1
268 REAL, INTENT(IN
), OPTIONAL
:: truelat2
269 REAL, INTENT(IN
), OPTIONAL
:: knowni
270 REAL, INTENT(IN
), OPTIONAL
:: knownj
271 REAL, INTENT(IN
), OPTIONAL
:: phi
272 REAL, INTENT(IN
), OPTIONAL
:: lambda
273 REAL, INTENT(IN
), OPTIONAL
:: r_earth
274 TYPE(proj_info
), INTENT(OUT
) :: proj
281 ! First, verify that mandatory parameters are present for the specified proj_code
282 IF ( proj_code
== PROJ_LC
) THEN
283 IF ( .NOT
.PRESENT(truelat1
) .OR
. &
284 .NOT
.PRESENT(truelat2
) .OR
. &
285 .NOT
.PRESENT(lat1
) .OR
. &
286 .NOT
.PRESENT(lon1
) .OR
. &
287 .NOT
.PRESENT(knowni
) .OR
. &
288 .NOT
.PRESENT(knownj
) .OR
. &
289 .NOT
.PRESENT(stdlon
) .OR
. &
290 .NOT
.PRESENT(dx
) ) THEN
291 PRINT '(A,I2)', 'The following are mandatory parameters for projection code : ', proj_code
292 PRINT '(A)', ' truelat1, truelat2, lat1, lon1, knowni, knownj, stdlon, dx'
293 call mprintf(.true
.,ERROR
,'MAP_INIT')
295 ELSE IF ( proj_code
== PROJ_PS
) THEN
296 IF ( .NOT
.PRESENT(truelat1
) .OR
. &
297 .NOT
.PRESENT(lat1
) .OR
. &
298 .NOT
.PRESENT(lon1
) .OR
. &
299 .NOT
.PRESENT(knowni
) .OR
. &
300 .NOT
.PRESENT(knownj
) .OR
. &
301 .NOT
.PRESENT(stdlon
) .OR
. &
302 .NOT
.PRESENT(dx
) ) THEN
303 PRINT '(A,I2)', 'The following are mandatory parameters for projection code : ', proj_code
304 PRINT '(A)', ' truelat1, lat1, lon1, knonwi, knownj, stdlon, dx'
305 call mprintf(.true
.,ERROR
,'MAP_INIT')
307 ELSE IF ( proj_code
== PROJ_PS_WGS84
) THEN
308 IF ( .NOT
.PRESENT(truelat1
) .OR
. &
309 .NOT
.PRESENT(lat1
) .OR
. &
310 .NOT
.PRESENT(lon1
) .OR
. &
311 .NOT
.PRESENT(knowni
) .OR
. &
312 .NOT
.PRESENT(knownj
) .OR
. &
313 .NOT
.PRESENT(stdlon
) .OR
. &
314 .NOT
.PRESENT(dx
) ) THEN
315 PRINT '(A,I2)', 'The following are mandatory parameters for projection code : ', proj_code
316 PRINT '(A)', ' truelat1, lat1, lon1, knonwi, knownj, stdlon, dx'
317 call mprintf(.true
.,ERROR
,'MAP_INIT')
319 ELSE IF ( proj_code
== PROJ_ALBERS_NAD83
) THEN
320 IF ( .NOT
.PRESENT(truelat1
) .OR
. &
321 .NOT
.PRESENT(truelat2
) .OR
. &
322 .NOT
.PRESENT(lat1
) .OR
. &
323 .NOT
.PRESENT(lon1
) .OR
. &
324 .NOT
.PRESENT(knowni
) .OR
. &
325 .NOT
.PRESENT(knownj
) .OR
. &
326 .NOT
.PRESENT(stdlon
) .OR
. &
327 .NOT
.PRESENT(dx
) ) THEN
328 PRINT '(A,I2)', 'The following are mandatory parameters for projection code : ', proj_code
329 PRINT '(A)', ' truelat1, truelat2, lat1, lon1, knonwi, knownj, stdlon, dx'
330 call mprintf(.true
.,ERROR
,'MAP_INIT')
332 ELSE IF ( proj_code
== PROJ_MERC
) THEN
333 IF ( .NOT
.PRESENT(truelat1
) .OR
. &
334 .NOT
.PRESENT(lat1
) .OR
. &
335 .NOT
.PRESENT(lon1
) .OR
. &
336 .NOT
.PRESENT(knowni
) .OR
. &
337 .NOT
.PRESENT(knownj
) .OR
. &
338 .NOT
.PRESENT(dx
) ) THEN
339 PRINT '(A,I2)', 'The following are mandatory parameters for projection code : ', proj_code
340 PRINT '(A)', ' truelat1, lat1, lon1, knowni, knownj, dx'
341 call mprintf(.true
.,ERROR
,'MAP_INIT')
343 ELSE IF ( proj_code
== PROJ_LATLON
) THEN
344 IF ( .NOT
.PRESENT(latinc
) .OR
. &
345 .NOT
.PRESENT(loninc
) .OR
. &
346 .NOT
.PRESENT(knowni
) .OR
. &
347 .NOT
.PRESENT(knownj
) .OR
. &
348 .NOT
.PRESENT(lat1
) .OR
. &
349 .NOT
.PRESENT(lon1
) ) THEN
350 PRINT '(A,I2)', 'The following are mandatory parameters for projection code : ', proj_code
351 PRINT '(A)', ' latinc, loninc, knowni, knownj, lat1, lon1'
352 call mprintf(.true
.,ERROR
,'MAP_INIT')
354 ELSE IF ( proj_code
== PROJ_CYL
) THEN
355 IF ( .NOT
.PRESENT(latinc
) .OR
. &
356 .NOT
.PRESENT(loninc
) .OR
. &
357 .NOT
.PRESENT(stdlon
) ) THEN
358 PRINT '(A,I2)', 'The following are mandatory parameters for projection code : ', proj_code
359 PRINT '(A)', ' latinc, loninc, stdlon'
360 call mprintf(.true
.,ERROR
,'MAP_INIT')
362 ELSE IF ( proj_code
== PROJ_CASSINI
) THEN
363 IF ( .NOT
.PRESENT(latinc
) .OR
. &
364 .NOT
.PRESENT(loninc
) .OR
. &
365 .NOT
.PRESENT(lat1
) .OR
. &
366 .NOT
.PRESENT(lon1
) .OR
. &
367 .NOT
.PRESENT(lat0
) .OR
. &
368 .NOT
.PRESENT(lon0
) .OR
. &
369 .NOT
.PRESENT(knowni
) .OR
. &
370 .NOT
.PRESENT(knownj
) .OR
. &
371 .NOT
.PRESENT(stdlon
) ) THEN
372 PRINT '(A,I2)', 'The following are mandatory parameters for projection code : ', proj_code
373 PRINT '(A)', ' latinc, loninc, lat1, lon1, knowni, knownj, lat0, lon0, stdlon'
374 call mprintf(.true
.,ERROR
,'MAP_INIT')
376 ELSE IF ( proj_code
== PROJ_GAUSS
) THEN
377 IF ( .NOT
.PRESENT(nlat
) .OR
. &
378 .NOT
.PRESENT(lat1
) .OR
. &
379 .NOT
.PRESENT(lon1
) .OR
. &
380 .NOT
.PRESENT(loninc
) ) THEN
381 PRINT '(A,I2)', 'The following are mandatory parameters for projection code : ', proj_code
382 PRINT '(A)', ' nlat, lat1, lon1, loninc'
383 call mprintf(.true
.,ERROR
,'MAP_INIT')
385 ELSE IF ( proj_code
== PROJ_ROTLL
) THEN
386 IF ( .NOT
.PRESENT(ixdim
) .OR
. &
387 .NOT
.PRESENT(jydim
) .OR
. &
388 .NOT
.PRESENT(phi
) .OR
. &
389 .NOT
.PRESENT(lambda
) .OR
. &
390 .NOT
.PRESENT(lat1
) .OR
. &
391 .NOT
.PRESENT(lon1
) .OR
. &
392 .NOT
.PRESENT(stagger
) ) THEN
393 PRINT '(A,I2)', 'The following are mandatory parameters for projection code : ', proj_code
394 PRINT '(A)', ' ixdim, jydim, phi, lambda, lat1, lon1, stagger'
395 call mprintf(.true
.,ERROR
,'MAP_INIT')
398 PRINT '(A,I2)', 'Unknown projection code: ', proj_code
399 call mprintf(.true
.,ERROR
,'MAP_INIT')
402 ! Check for validity of mandatory variables in proj
403 IF ( PRESENT(lat1
) ) THEN
404 IF ( ABS(lat1
) .GT
. 90. ) THEN
405 PRINT '(A)', 'Latitude of origin corner required as follows:'
406 PRINT '(A)', ' -90N <= lat1 < = 90.N'
407 call mprintf(.true
.,ERROR
,'MAP_INIT')
411 IF ( PRESENT(lon1
) ) THEN
413 IF ( ABS(dummy_lon1
) .GT
. 180.) THEN
415 DO WHILE (ABS(dummy_lon1
) > 180. .AND
. iter
< 10)
416 IF (dummy_lon1
< -180.) dummy_lon1
= dummy_lon1
+ 360.
417 IF (dummy_lon1
> 180.) dummy_lon1
= dummy_lon1
- 360.
420 IF (abs(dummy_lon1
) > 180.) THEN
421 PRINT '(A)', 'Longitude of origin required as follows:'
422 PRINT '(A)', ' -180E <= lon1 <= 180W'
423 call mprintf(.true
.,ERROR
,'MAP_INIT')
428 IF ( PRESENT(lon0
) ) THEN
430 IF ( ABS(dummy_lon0
) .GT
. 180.) THEN
432 DO WHILE (ABS(dummy_lon0
) > 180. .AND
. iter
< 10)
433 IF (dummy_lon0
< -180.) dummy_lon0
= dummy_lon0
+ 360.
434 IF (dummy_lon0
> 180.) dummy_lon0
= dummy_lon0
- 360.
437 IF (abs(dummy_lon0
) > 180.) THEN
438 PRINT '(A)', 'Longitude of pole required as follows:'
439 PRINT '(A)', ' -180E <= lon0 <= 180W'
440 call mprintf(.true
.,ERROR
,'MAP_INIT')
445 IF ( PRESENT(dx
) ) THEN
446 IF ((dx
.LE
. 0.).AND
.(proj_code
.NE
. PROJ_LATLON
)) THEN
447 PRINT '(A)', 'Require grid spacing (dx) in meters be positive!'
448 call mprintf(.true
.,ERROR
,'MAP_INIT')
452 IF ( PRESENT(stdlon
) ) THEN
453 dummy_stdlon
= stdlon
454 IF ((ABS(dummy_stdlon
) > 180.).AND
.(proj_code
/= PROJ_MERC
)) THEN
456 DO WHILE (ABS(dummy_stdlon
) > 180. .AND
. iter
< 10)
457 IF (dummy_stdlon
< -180.) dummy_stdlon
= dummy_stdlon
+ 360.
458 IF (dummy_stdlon
> 180.) dummy_stdlon
= dummy_stdlon
- 360.
461 IF (abs(dummy_stdlon
) > 180.) THEN
462 PRINT '(A)', 'Need orientation longitude (stdlon) as: '
463 PRINT '(A)', ' -180E <= stdlon <= 180W'
464 call mprintf(.true
.,ERROR
,'MAP_INIT')
469 IF ( PRESENT(truelat1
) ) THEN
470 IF (ABS(truelat1
).GT
.90.) THEN
471 PRINT '(A)', 'Set true latitude 1 for all projections!'
472 call mprintf(.true
.,ERROR
,'MAP_INIT')
477 proj
%code
= proj_code
478 IF ( PRESENT(lat1
) ) proj
%lat1
= lat1
479 IF ( PRESENT(lon1
) ) proj
%lon1
= dummy_lon1
480 IF ( PRESENT(lat0
) ) proj
%lat0
= lat0
481 IF ( PRESENT(lon0
) ) proj
%lon0
= dummy_lon0
482 IF ( PRESENT(latinc
) ) proj
%latinc
= latinc
483 IF ( PRESENT(loninc
) ) proj
%loninc
= loninc
484 IF ( PRESENT(knowni
) ) proj
%knowni
= knowni
485 IF ( PRESENT(knownj
) ) proj
%knownj
= knownj
486 IF ( PRESENT(dx
) ) proj
%dx
= dx
487 IF ( PRESENT(stdlon
) ) proj
%stdlon
= dummy_stdlon
488 IF ( PRESENT(truelat1
) ) proj
%truelat1
= truelat1
489 IF ( PRESENT(truelat2
) ) proj
%truelat2
= truelat2
490 IF ( PRESENT(nlat
) ) proj
%nlat
= nlat
491 IF ( PRESENT(nlon
) ) proj
%nlon
= nlon
492 IF ( PRESENT(ixdim
) ) proj
%ixdim
= ixdim
493 IF ( PRESENT(jydim
) ) proj
%jydim
= jydim
494 IF ( PRESENT(stagger
) ) proj
%stagger
= stagger
495 IF ( PRESENT(phi
) ) proj
%phi
= phi
496 IF ( PRESENT(lambda
) ) proj
%lambda
= lambda
497 IF ( PRESENT(r_earth
) ) proj
%re_m
= r_earth
499 IF ( PRESENT(dx
) ) THEN
500 IF ( (proj_code
== PROJ_LC
) .OR
. (proj_code
== PROJ_PS
) .OR
. &
501 (proj_code
== PROJ_PS_WGS84
) .OR
. (proj_code
== PROJ_ALBERS_NAD83
) .OR
. &
502 (proj_code
== PROJ_MERC
) ) THEN
504 IF (truelat1
.LT
. 0.) THEN
509 proj
%rebydx
= proj
%re_m
/ dx
513 pick_proj
: SELECT
CASE(proj
%code
)
519 CALL set_ps_wgs84(proj
)
521 CASE(PROJ_ALBERS_NAD83
)
522 CALL set_albers_nad83(proj
)
525 IF (ABS(proj
%truelat2
) .GT
. 90.) THEN
526 proj
%truelat2
=proj
%truelat1
542 CALL set_cassini(proj
)
551 END SUBROUTINE map_set
554 SUBROUTINE latlon_to_ij(proj
, lat
, lon
, i
, j
)
555 ! Converts input lat/lon values to the cartesian (i,j) value
556 ! for the given projection.
559 TYPE(proj_info
), INTENT(IN
) :: proj
560 REAL, INTENT(IN
) :: lat
561 REAL, INTENT(IN
) :: lon
562 REAL, INTENT(OUT
) :: i
563 REAL, INTENT(OUT
) :: j
565 IF (.NOT
.proj
%init
) THEN
566 PRINT '(A)', 'You have not called map_set for this projection!'
567 call mprintf(.true
.,ERROR
,'LATLON_TO_IJ')
570 SELECT
CASE(proj
%code
)
573 CALL llij_latlon(lat
,lon
,proj
,i
,j
)
576 CALL llij_merc(lat
,lon
,proj
,i
,j
)
579 CALL llij_ps(lat
,lon
,proj
,i
,j
)
582 CALL llij_ps_wgs84(lat
,lon
,proj
,i
,j
)
584 CASE(PROJ_ALBERS_NAD83
)
585 CALL llij_albers_nad83(lat
,lon
,proj
,i
,j
)
588 CALL llij_lc(lat
,lon
,proj
,i
,j
)
591 CALL llij_gauss(lat
,lon
,proj
,i
,j
)
594 CALL llij_cyl(lat
,lon
,proj
,i
,j
)
597 CALL llij_cassini(lat
,lon
,proj
,i
,j
)
600 CALL llij_rotlatlon(lat
,lon
,proj
,i
,j
)
603 PRINT '(A,I2)', 'Unrecognized map projection code: ', proj
%code
604 call mprintf(.true
.,ERROR
,'LATLON_TO_IJ')
610 END SUBROUTINE latlon_to_ij
613 SUBROUTINE ij_to_latlon(proj
, i
, j
, lat
, lon
)
614 ! Computes geographical latitude and longitude for a given (i,j) point
615 ! in a grid with a projection of proj
618 TYPE(proj_info
),INTENT(IN
) :: proj
619 REAL, INTENT(IN
) :: i
620 REAL, INTENT(IN
) :: j
621 REAL, INTENT(OUT
) :: lat
622 REAL, INTENT(OUT
) :: lon
624 IF (.NOT
.proj
%init
) THEN
625 PRINT '(A)', 'You have not called map_set for this projection!'
626 call mprintf(.true
.,ERROR
,'IJ_TO_LATLON')
628 SELECT
CASE (proj
%code
)
631 CALL ijll_latlon(i
, j
, proj
, lat
, lon
)
634 CALL ijll_merc(i
, j
, proj
, lat
, lon
)
637 CALL ijll_ps(i
, j
, proj
, lat
, lon
)
640 CALL ijll_ps_wgs84(i
, j
, proj
, lat
, lon
)
642 CASE (PROJ_ALBERS_NAD83
)
643 CALL ijll_albers_nad83(i
, j
, proj
, lat
, lon
)
646 CALL ijll_lc(i
, j
, proj
, lat
, lon
)
649 CALL ijll_cyl(i
, j
, proj
, lat
, lon
)
652 CALL ijll_cassini(i
, j
, proj
, lat
, lon
)
655 CALL ijll_rotlatlon(i
, j
, proj
, lat
, lon
)
658 PRINT '(A,I2)', 'Unrecognized map projection code: ', proj
%code
659 call mprintf(.true
.,ERROR
,'IJ_TO_LATLON')
663 END SUBROUTINE ij_to_latlon
666 SUBROUTINE set_ps(proj
)
667 ! Initializes a polar-stereographic map projection from the partially
668 ! filled proj structure. This routine computes the radius to the
669 ! southwest corner and computes the i/j location of the pole for use
670 ! in llij_ps and ijll_ps.
674 TYPE(proj_info
), INTENT(INOUT
) :: proj
683 reflon
= proj
%stdlon
+ 90.
685 ! Compute numerator term of map scale factor
686 scale_top
= 1. + proj
%hemi
* SIN(proj
%truelat1
* rad_per_deg
)
688 ! Compute radius to lower-left (SW) corner
689 ala1
= proj
%lat1
* rad_per_deg
690 proj
%rsw
= proj
%rebydx
*COS(ala1
)*scale_top
/(1.+proj
%hemi
*SIN(ala1
))
692 ! Find the pole point
693 alo1
= (proj
%lon1
- reflon
) * rad_per_deg
694 proj
%polei
= proj
%knowni
- proj
%rsw
* COS(alo1
)
695 proj
%polej
= proj
%knownj
- proj
%hemi
* proj
%rsw
* SIN(alo1
)
699 END SUBROUTINE set_ps
702 SUBROUTINE llij_ps(lat
,lon
,proj
,i
,j
)
703 ! Given latitude (-90 to 90), longitude (-180 to 180), and the
704 ! standard polar-stereographic projection information via the
705 ! public proj structure, this routine returns the i/j indices which
706 ! if within the domain range from 1->nx and 1->ny, respectively.
710 ! Delcare input arguments
711 REAL, INTENT(IN
) :: lat
712 REAL, INTENT(IN
) :: lon
713 TYPE(proj_info
),INTENT(IN
) :: proj
715 ! Declare output arguments
716 REAL, INTENT(OUT
) :: i
!(x-index)
717 REAL, INTENT(OUT
) :: j
!(y-index)
719 ! Declare local variables
729 reflon
= proj
%stdlon
+ 90.
731 ! Compute numerator term of map scale factor
733 scale_top
= 1. + proj
%hemi
* SIN(proj
%truelat1
* rad_per_deg
)
735 ! Find radius to desired point
736 ala
= lat
* rad_per_deg
737 rm
= proj
%rebydx
* COS(ala
) * scale_top
/(1. + proj
%hemi
*SIN(ala
))
738 alo
= (lon
- reflon
) * rad_per_deg
739 i
= proj
%polei
+ rm
* COS(alo
)
740 j
= proj
%polej
+ proj
%hemi
* rm
* SIN(alo
)
744 END SUBROUTINE llij_ps
747 SUBROUTINE ijll_ps(i
, j
, proj
, lat
, lon
)
749 ! This is the inverse subroutine of llij_ps. It returns the
750 ! latitude and longitude of an i/j point given the projection info
755 ! Declare input arguments
756 REAL, INTENT(IN
) :: i
! Column
757 REAL, INTENT(IN
) :: j
! Row
758 TYPE (proj_info
), INTENT(IN
) :: proj
760 ! Declare output arguments
761 REAL, INTENT(OUT
) :: lat
! -90 -> 90 north
762 REAL, INTENT(OUT
) :: lon
! -180 -> 180 East
773 ! Compute the reference longitude by rotating 90 degrees to the east
774 ! to find the longitude line parallel to the positive x-axis.
775 reflon
= proj
%stdlon
+ 90.
777 ! Compute numerator term of map scale factor
778 scale_top
= 1. + proj
%hemi
* SIN(proj
%truelat1
* rad_per_deg
)
780 ! Compute radius to point of interest
782 yy
= (j
- proj
%polej
) * proj
%hemi
787 lat
= proj
%hemi
* 90.
790 gi2
= (proj
%rebydx
* scale_top
)**2.
791 lat
= deg_per_rad
* proj
%hemi
* ASIN((gi2
-r2
)/(gi2
+r2
))
792 arccos
= ACOS(xx
/SQRT(r2
))
794 lon
= reflon
+ deg_per_rad
* arccos
796 lon
= reflon
- deg_per_rad
* arccos
800 ! Convert to a -180 -> 180 East convention
801 IF (lon
.GT
. 180.) lon
= lon
- 360.
802 IF (lon
.LT
. -180.) lon
= lon
+ 360.
806 END SUBROUTINE ijll_ps
809 SUBROUTINE set_ps_wgs84(proj
)
810 ! Initializes a polar-stereographic map projection (WGS84 ellipsoid)
811 ! from the partially filled proj structure. This routine computes the
812 ! radius to the southwest corner and computes the i/j location of the
813 ! pole for use in llij_ps and ijll_ps.
818 TYPE(proj_info
), INTENT(INOUT
) :: proj
821 real :: h
, mc
, tc
, t
, rho
825 mc
= cos(h
*proj
%truelat1
*rad_per_deg
)/sqrt(1.0-(E_WGS84
*sin(h
*proj
%truelat1
*rad_per_deg
))**2.0)
826 tc
= sqrt(((1.0-sin(h
*proj
%truelat1
*rad_per_deg
))/(1.0+sin(h
*proj
%truelat1
*rad_per_deg
)))* &
827 (((1.0+E_WGS84
*sin(h
*proj
%truelat1
*rad_per_deg
))/(1.0-E_WGS84
*sin(h
*proj
%truelat1
*rad_per_deg
)))**E_WGS84
))
829 ! Find the i/j location of reference lat/lon with respect to the pole of the projection
830 t
= sqrt(((1.0-sin(h
*proj
%lat1
*rad_per_deg
))/(1.0+sin(h
*proj
%lat1
*rad_per_deg
)))* &
831 (((1.0+E_WGS84
*sin(h
*proj
%lat1
*rad_per_deg
))/(1.0-E_WGS84
*sin(h
*proj
%lat1
*rad_per_deg
)) )**E_WGS84
) )
832 rho
= h
* (A_WGS84
/ proj
%dx
) * mc
* t
/ tc
833 proj
%polei
= rho
* sin((h
*proj
%lon1
- h
*proj
%stdlon
)*rad_per_deg
)
834 proj
%polej
= -rho
* cos((h
*proj
%lon1
- h
*proj
%stdlon
)*rad_per_deg
)
838 END SUBROUTINE set_ps_wgs84
841 SUBROUTINE llij_ps_wgs84(lat
,lon
,proj
,i
,j
)
842 ! Given latitude (-90 to 90), longitude (-180 to 180), and the
843 ! standard polar-stereographic projection information via the
844 ! public proj structure, this routine returns the i/j indices which
845 ! if within the domain range from 1->nx and 1->ny, respectively.
850 REAL, INTENT(IN
) :: lat
851 REAL, INTENT(IN
) :: lon
852 REAL, INTENT(OUT
) :: i
!(x-index)
853 REAL, INTENT(OUT
) :: j
!(y-index)
854 TYPE(proj_info
),INTENT(IN
) :: proj
857 real :: h
, mc
, tc
, t
, rho
861 mc
= cos(h
*proj
%truelat1
*rad_per_deg
)/sqrt(1.0-(E_WGS84
*sin(h
*proj
%truelat1
*rad_per_deg
))**2.0)
862 tc
= sqrt(((1.0-sin(h
*proj
%truelat1
*rad_per_deg
))/(1.0+sin(h
*proj
%truelat1
*rad_per_deg
)))* &
863 (((1.0+E_WGS84
*sin(h
*proj
%truelat1
*rad_per_deg
))/(1.0-E_WGS84
*sin(h
*proj
%truelat1
*rad_per_deg
)))**E_WGS84
))
865 t
= sqrt(((1.0-sin(h
*lat
*rad_per_deg
))/(1.0+sin(h
*lat
*rad_per_deg
))) * &
866 (((1.0+E_WGS84
*sin(h
*lat
*rad_per_deg
))/(1.0-E_WGS84
*sin(h
*lat
*rad_per_deg
)))**E_WGS84
))
868 ! Find the x/y location of the requested lat/lon with respect to the pole of the projection
869 rho
= (A_WGS84
/ proj
%dx
) * mc
* t
/ tc
870 i
= h
* rho
* sin((h
*lon
- h
*proj
%stdlon
)*rad_per_deg
)
871 j
= h
*(-rho
)* cos((h
*lon
- h
*proj
%stdlon
)*rad_per_deg
)
873 ! Get i/j relative to reference i/j
874 i
= proj
%knowni
+ (i
- proj
%polei
)
875 j
= proj
%knownj
+ (j
- proj
%polej
)
879 END SUBROUTINE llij_ps_wgs84
882 SUBROUTINE ijll_ps_wgs84(i
, j
, proj
, lat
, lon
)
884 ! This is the inverse subroutine of llij_ps. It returns the
885 ! latitude and longitude of an i/j point given the projection info
891 REAL, INTENT(IN
) :: i
! Column
892 REAL, INTENT(IN
) :: j
! Row
893 REAL, INTENT(OUT
) :: lat
! -90 -> 90 north
894 REAL, INTENT(OUT
) :: lon
! -180 -> 180 East
895 TYPE (proj_info
), INTENT(IN
) :: proj
898 real :: h
, mc
, tc
, t
, rho
, x
, y
899 real :: chi
, a
, b
, c
, d
902 x
= (i
- proj
%knowni
+ proj
%polei
)
903 y
= (j
- proj
%knownj
+ proj
%polej
)
905 mc
= cos(h
*proj
%truelat1
*rad_per_deg
)/sqrt(1.0-(E_WGS84
*sin(h
*proj
%truelat1
*rad_per_deg
))**2.0)
906 tc
= sqrt(((1.0-sin(h
*proj
%truelat1
*rad_per_deg
))/(1.0+sin(h
*proj
%truelat1
*rad_per_deg
))) * &
907 (((1.0+E_WGS84
*sin(h
*proj
%truelat1
*rad_per_deg
))/(1.0-E_WGS84
*sin(h
*proj
%truelat1
*rad_per_deg
)))**E_WGS84
))
909 rho
= sqrt((x
*proj
%dx
)**2.0 + (y
*proj
%dx
)**2.0)
910 t
= rho
* tc
/ (A_WGS84
* mc
)
912 lon
= h
*proj
%stdlon
+ h
*atan2(h
*x
,h
*(-y
))
914 chi
= PI
/2.0-2.0*atan(t
)
915 a
= 1./2.*E_WGS84
**2. + 5./24.*E_WGS84
**4. + 1./40.*E_WGS84
**6. + 73./2016.*E_WGS84
**8.
916 b
= 7./24.*E_WGS84
**4. + 29./120.*E_WGS84
**6. + 54113./40320.*E_WGS84
**8.
917 c
= 7./30.*E_WGS84
**6. + 81./280.*E_WGS84
**8.
918 d
= 4279./20160.*E_WGS84
**8.
920 lat
= chi
+ sin(2.*chi
)*(a
+ cos(2.*chi
)*(b
+ cos(2.*chi
)*(c
+ d
*cos(2.*chi
))))
923 lat
= lat
*deg_per_rad
924 lon
= lon
*deg_per_rad
928 END SUBROUTINE ijll_ps_wgs84
931 SUBROUTINE set_albers_nad83(proj
)
932 ! Initializes an Albers equal area map projection (NAD83 ellipsoid)
933 ! from the partially filled proj structure. This routine computes the
934 ! radius to the southwest corner and computes the i/j location of the
935 ! pole for use in llij_albers_nad83 and ijll_albers_nad83.
940 TYPE(proj_info
), INTENT(INOUT
) :: proj
943 real :: h
, m1
, m2
, q1
, q2
, theta
, q
, sinphi
947 m1
= cos(h
*proj
%truelat1
*rad_per_deg
)/sqrt(1.0-(E_NAD83
*sin(h
*proj
%truelat1
*rad_per_deg
))**2.0)
948 m2
= cos(h
*proj
%truelat2
*rad_per_deg
)/sqrt(1.0-(E_NAD83
*sin(h
*proj
%truelat2
*rad_per_deg
))**2.0)
950 sinphi
= sin(proj
%truelat1
*rad_per_deg
)
951 q1
= (1.0-E_NAD83
**2.0) * &
952 ((sinphi
/(1.0-(E_NAD83
*sinphi
)**2.0)) - 1.0/(2.0*E_NAD83
) * log((1.0-E_NAD83
*sinphi
)/(1.0+E_NAD83
*sinphi
)))
954 sinphi
= sin(proj
%truelat2
*rad_per_deg
)
955 q2
= (1.0-E_NAD83
**2.0) * &
956 ((sinphi
/(1.0-(E_NAD83
*sinphi
)**2.0)) - 1.0/(2.0*E_NAD83
) * log((1.0-E_NAD83
*sinphi
)/(1.0+E_NAD83
*sinphi
)))
958 if (proj
%truelat1
== proj
%truelat2
) then
959 proj
%nc
= sin(proj
%truelat1
*rad_per_deg
)
961 proj
%nc
= (m1
**2.0 - m2
**2.0) / (q2
- q1
)
964 proj
%bigc
= m1
**2.0 + proj
%nc
*q1
966 ! Find the i/j location of reference lat/lon with respect to the pole of the projection
967 sinphi
= sin(proj
%lat1
*rad_per_deg
)
968 q
= (1.0-E_NAD83
**2.0) * &
969 ((sinphi
/(1.0-(E_NAD83
*sinphi
)**2.0)) - 1.0/(2.0*E_NAD83
) * log((1.0-E_NAD83
*sinphi
)/(1.0+E_NAD83
*sinphi
)))
971 proj
%rho0
= h
* (A_NAD83
/ proj
%dx
) * sqrt(proj
%bigc
- proj
%nc
* q
) / proj
%nc
972 theta
= proj
%nc
*(proj
%lon1
- proj
%stdlon
)*rad_per_deg
974 proj
%polei
= proj
%rho0
* sin(h
*theta
)
975 proj
%polej
= proj
%rho0
- proj
%rho0
* cos(h
*theta
)
979 END SUBROUTINE set_albers_nad83
982 SUBROUTINE llij_albers_nad83(lat
,lon
,proj
,i
,j
)
983 ! Given latitude (-90 to 90), longitude (-180 to 180), and the
984 ! standard projection information via the
985 ! public proj structure, this routine returns the i/j indices which
986 ! if within the domain range from 1->nx and 1->ny, respectively.
991 REAL, INTENT(IN
) :: lat
992 REAL, INTENT(IN
) :: lon
993 REAL, INTENT(OUT
) :: i
!(x-index)
994 REAL, INTENT(OUT
) :: j
!(y-index)
995 TYPE(proj_info
),INTENT(IN
) :: proj
998 real :: h
, q
, rho
, theta
, sinphi
1002 sinphi
= sin(h
*lat
*rad_per_deg
)
1004 ! Find the x/y location of the requested lat/lon with respect to the pole of the projection
1005 q
= (1.0-E_NAD83
**2.0) * &
1006 ((sinphi
/(1.0-(E_NAD83
*sinphi
)**2.0)) - 1.0/(2.0*E_NAD83
) * log((1.0-E_NAD83
*sinphi
)/(1.0+E_NAD83
*sinphi
)))
1008 rho
= h
* (A_NAD83
/ proj
%dx
) * sqrt(proj
%bigc
- proj
%nc
* q
) / proj
%nc
1009 theta
= proj
%nc
* (h
*lon
- h
*proj
%stdlon
)*rad_per_deg
1011 i
= h
*rho
*sin(theta
)
1012 j
= h
*proj
%rho0
- h
*rho
*cos(theta
)
1014 ! Get i/j relative to reference i/j
1015 i
= proj
%knowni
+ (i
- proj
%polei
)
1016 j
= proj
%knownj
+ (j
- proj
%polej
)
1020 END SUBROUTINE llij_albers_nad83
1023 SUBROUTINE ijll_albers_nad83(i
, j
, proj
, lat
, lon
)
1025 ! This is the inverse subroutine of llij_albers_nad83. It returns the
1026 ! latitude and longitude of an i/j point given the projection info
1032 REAL, INTENT(IN
) :: i
! Column
1033 REAL, INTENT(IN
) :: j
! Row
1034 REAL, INTENT(OUT
) :: lat
! -90 -> 90 north
1035 REAL, INTENT(OUT
) :: lon
! -180 -> 180 East
1036 TYPE (proj_info
), INTENT(IN
) :: proj
1039 real :: h
, q
, rho
, theta
, beta
, x
, y
1044 x
= (i
- proj
%knowni
+ proj
%polei
)
1045 y
= (j
- proj
%knownj
+ proj
%polej
)
1047 rho
= sqrt(x
**2.0 + (proj
%rho0
- y
)**2.0)
1048 theta
= atan2(x
, proj
%rho0
-y
)
1050 q
= (proj
%bigc
- (rho
*proj
%nc
*proj
%dx
/A_NAD83
)**2.0) / proj
%nc
1052 beta
= asin(q
/(1.0 - log((1.0-E_NAD83
)/(1.0+E_NAD83
))*(1.0-E_NAD83
**2.0)/(2.0*E_NAD83
)))
1053 a
= 1./3.*E_NAD83
**2. + 31./180.*E_NAD83
**4. + 517./5040.*E_NAD83
**6.
1054 b
= 23./360.*E_NAD83
**4. + 251./3780.*E_NAD83
**6.
1055 c
= 761./45360.*E_NAD83
**6.
1057 lat
= beta
+ a
*sin(2.*beta
) + b
*sin(4.*beta
) + c
*sin(6.*beta
)
1059 lat
= h
*lat
*deg_per_rad
1060 lon
= proj
%stdlon
+ theta
*deg_per_rad
/proj
%nc
1064 END SUBROUTINE ijll_albers_nad83
1067 SUBROUTINE set_lc(proj
)
1068 ! Initialize the remaining items in the proj structure for a
1069 ! lambert conformal grid.
1073 TYPE(proj_info
), INTENT(INOUT
) :: proj
1080 ! Compute cone factor
1081 CALL lc_cone(proj
%truelat1
, proj
%truelat2
, proj
%cone
)
1083 ! Compute longitude differences and ensure we stay out of the
1084 ! forbidden "cut zone"
1085 deltalon1
= proj
%lon1
- proj
%stdlon
1086 IF (deltalon1
.GT
. +180.) deltalon1
= deltalon1
- 360.
1087 IF (deltalon1
.LT
. -180.) deltalon1
= deltalon1
+ 360.
1089 ! Convert truelat1 to radian and compute COS for later use
1090 tl1r
= proj
%truelat1
* rad_per_deg
1093 ! Compute the radius to our known lower-left (SW) corner
1094 proj
%rsw
= proj
%rebydx
* ctl1r
/proj
%cone
* &
1095 (TAN((90.*proj
%hemi
-proj
%lat1
)*rad_per_deg
/2.) / &
1096 TAN((90.*proj
%hemi
-proj
%truelat1
)*rad_per_deg
/2.))**proj
%cone
1099 arg
= proj
%cone
*(deltalon1
*rad_per_deg
)
1100 proj
%polei
= proj
%hemi
*proj
%knowni
- proj
%hemi
* proj
%rsw
* SIN(arg
)
1101 proj
%polej
= proj
%hemi
*proj
%knownj
+ proj
%rsw
* COS(arg
)
1105 END SUBROUTINE set_lc
1108 SUBROUTINE lc_cone(truelat1
, truelat2
, cone
)
1110 ! Subroutine to compute the cone factor of a Lambert Conformal projection
1115 REAL, INTENT(IN
) :: truelat1
! (-90 -> 90 degrees N)
1116 REAL, INTENT(IN
) :: truelat2
! " " " " "
1119 REAL, INTENT(OUT
) :: cone
1125 ! First, see if this is a secant or tangent projection. For tangent
1126 ! projections, truelat1 = truelat2 and the cone is tangent to the
1127 ! Earth's surface at this latitude. For secant projections, the cone
1128 ! intersects the Earth's surface at each of the distinctly different
1130 IF (ABS(truelat1
-truelat2
) .GT
. 0.1) THEN
1131 cone
= ALOG10(COS(truelat1
*rad_per_deg
)) - &
1132 ALOG10(COS(truelat2
*rad_per_deg
))
1133 cone
= cone
/(ALOG10(TAN((45.0 - ABS(truelat1
)/2.0) * rad_per_deg
)) - &
1134 ALOG10(TAN((45.0 - ABS(truelat2
)/2.0) * rad_per_deg
)))
1136 cone
= SIN(ABS(truelat1
)*rad_per_deg
)
1141 END SUBROUTINE lc_cone
1144 SUBROUTINE ijll_lc( i
, j
, proj
, lat
, lon
)
1146 ! Subroutine to convert from the (i,j) cartesian coordinate to the
1147 ! geographical latitude and longitude for a Lambert Conformal projection.
1150 ! 25 Jul 01: Corrected by B. Shaw, NOAA/FSL
1155 REAL, INTENT(IN
) :: i
! Cartesian X coordinate
1156 REAL, INTENT(IN
) :: j
! Cartesian Y coordinate
1157 TYPE(proj_info
),INTENT(IN
) :: proj
! Projection info structure
1160 REAL, INTENT(OUT
) :: lat
! Latitude (-90->90 deg N)
1161 REAL, INTENT(OUT
) :: lon
! Longitude (-180->180 E)
1167 REAL :: chi
,chi1
,chi2
1174 chi1
= (90. - proj
%hemi
*proj
%truelat1
)*rad_per_deg
1175 chi2
= (90. - proj
%hemi
*proj
%truelat2
)*rad_per_deg
1177 ! See if we are in the southern hemispere and flip the indices
1179 inew
= proj
%hemi
* i
1180 jnew
= proj
%hemi
* j
1182 ! Compute radius**2 to i/j location
1183 xx
= inew
- proj
%polei
1184 yy
= proj
%polej
- jnew
1185 r2
= (xx
*xx
+ yy
*yy
)
1186 r
= SQRT(r2
)/proj
%rebydx
1188 ! Convert to lat/lon
1189 IF (r2
.EQ
. 0.) THEN
1190 lat
= proj
%hemi
* 90.
1195 lon
= proj
%stdlon
+ deg_per_rad
* ATAN2(proj
%hemi
*xx
,yy
)/proj
%cone
1196 lon
= AMOD(lon
+360., 360.)
1198 ! Latitude. Latitude determined by solving an equation adapted
1200 ! Maling, D.H., 1973: Coordinate Systems and Map Projections
1201 ! Equations #20 in Appendix I.
1203 IF (chi1
.EQ
. chi2
) THEN
1204 chi
= 2.0*ATAN( ( r
/TAN(chi1
) )**(1./proj
%cone
) * TAN(chi1
*0.5) )
1206 chi
= 2.0*ATAN( (r
*proj
%cone
/SIN(chi1
))**(1./proj
%cone
) * TAN(chi1
*0.5))
1208 lat
= (90.0-chi
*deg_per_rad
)*proj
%hemi
1212 IF (lon
.GT
. +180.) lon
= lon
- 360.
1213 IF (lon
.LT
. -180.) lon
= lon
+ 360.
1217 END SUBROUTINE ijll_lc
1220 SUBROUTINE llij_lc( lat
, lon
, proj
, i
, j
)
1222 ! Subroutine to compute the geographical latitude and longitude values
1223 ! to the cartesian x/y on a Lambert Conformal projection.
1228 REAL, INTENT(IN
) :: lat
! Latitude (-90->90 deg N)
1229 REAL, INTENT(IN
) :: lon
! Longitude (-180->180 E)
1230 TYPE(proj_info
),INTENT(IN
) :: proj
! Projection info structure
1233 REAL, INTENT(OUT
) :: i
! Cartesian X coordinate
1234 REAL, INTENT(OUT
) :: j
! Cartesian Y coordinate
1246 ! Compute deltalon between known longitude and standard lon and ensure
1247 ! it is not in the cut zone
1248 deltalon
= lon
- proj
%stdlon
1249 IF (deltalon
.GT
. +180.) deltalon
= deltalon
- 360.
1250 IF (deltalon
.LT
. -180.) deltalon
= deltalon
+ 360.
1252 ! Convert truelat1 to radian and compute COS for later use
1253 tl1r
= proj
%truelat1
* rad_per_deg
1256 ! Radius to desired point
1257 rm
= proj
%rebydx
* ctl1r
/proj
%cone
* &
1258 (TAN((90.*proj
%hemi
-lat
)*rad_per_deg
/2.) / &
1259 TAN((90.*proj
%hemi
-proj
%truelat1
)*rad_per_deg
/2.))**proj
%cone
1261 arg
= proj
%cone
*(deltalon
*rad_per_deg
)
1262 i
= proj
%polei
+ proj
%hemi
* rm
* SIN(arg
)
1263 j
= proj
%polej
- rm
* COS(arg
)
1265 ! Finally, if we are in the southern hemisphere, flip the i/j
1266 ! values to a coordinate system where (1,1) is the SW corner
1267 ! (what we assume) which is different than the original NCEP
1268 ! algorithms which used the NE corner as the origin in the
1269 ! southern hemisphere (left-hand vs. right-hand coordinate?)
1274 END SUBROUTINE llij_lc
1277 SUBROUTINE set_merc(proj
)
1279 ! Sets up the remaining basic elements for the mercator projection
1282 TYPE(proj_info
), INTENT(INOUT
) :: proj
1286 ! Preliminary variables
1288 clain
= COS(rad_per_deg
*proj
%truelat1
)
1289 proj
%dlon
= proj
%dx
/ (proj
%re_m
* clain
)
1291 ! Compute distance from equator to origin, and store in the
1295 IF (proj
%lat1
.NE
. 0.) THEN
1296 proj
%rsw
= (ALOG(TAN(0.5*((proj
%lat1
+90.)*rad_per_deg
))))/proj
%dlon
1301 END SUBROUTINE set_merc
1304 SUBROUTINE llij_merc(lat
, lon
, proj
, i
, j
)
1306 ! Compute i/j coordinate from lat lon for mercator projection
1309 REAL, INTENT(IN
) :: lat
1310 REAL, INTENT(IN
) :: lon
1311 TYPE(proj_info
),INTENT(IN
) :: proj
1312 REAL,INTENT(OUT
) :: i
1313 REAL,INTENT(OUT
) :: j
1316 deltalon
= lon
- proj
%lon1
1317 IF (deltalon
.LT
. -180.) deltalon
= deltalon
+ 360.
1318 IF (deltalon
.GT
. 180.) deltalon
= deltalon
- 360.
1319 i
= proj
%knowni
+ (deltalon
/(proj
%dlon
*deg_per_rad
))
1320 j
= proj
%knownj
+ (ALOG(TAN(0.5*((lat
+ 90.) * rad_per_deg
)))) / &
1321 proj
%dlon
- proj
%rsw
1325 END SUBROUTINE llij_merc
1328 SUBROUTINE ijll_merc(i
, j
, proj
, lat
, lon
)
1330 ! Compute the lat/lon from i/j for mercator projection
1333 REAL,INTENT(IN
) :: i
1334 REAL,INTENT(IN
) :: j
1335 TYPE(proj_info
),INTENT(IN
) :: proj
1336 REAL, INTENT(OUT
) :: lat
1337 REAL, INTENT(OUT
) :: lon
1340 lat
= 2.0*ATAN(EXP(proj
%dlon
*(proj
%rsw
+ j
-proj
%knownj
)))*deg_per_rad
- 90.
1341 lon
= (i
-proj
%knowni
)*proj
%dlon
*deg_per_rad
+ proj
%lon1
1342 IF (lon
.GT
.180.) lon
= lon
- 360.
1343 IF (lon
.LT
.-180.) lon
= lon
+ 360.
1346 END SUBROUTINE ijll_merc
1349 SUBROUTINE llij_latlon(lat
, lon
, proj
, i
, j
)
1351 ! Compute the i/j location of a lat/lon on a LATLON grid.
1353 REAL, INTENT(IN
) :: lat
1354 REAL, INTENT(IN
) :: lon
1355 TYPE(proj_info
), INTENT(IN
) :: proj
1356 REAL, INTENT(OUT
) :: i
1357 REAL, INTENT(OUT
) :: j
1362 ! Compute deltalat and deltalon as the difference between the input
1363 ! lat/lon and the origin lat/lon
1364 deltalat
= lat
- proj
%lat1
1365 deltalon
= lon
- proj
%lon1
1368 i
= deltalon
/proj
%loninc
1369 j
= deltalat
/proj
%latinc
1376 END SUBROUTINE llij_latlon
1379 SUBROUTINE ijll_latlon(i
, j
, proj
, lat
, lon
)
1381 ! Compute the lat/lon location of an i/j on a LATLON grid.
1383 REAL, INTENT(IN
) :: i
1384 REAL, INTENT(IN
) :: j
1385 TYPE(proj_info
), INTENT(IN
) :: proj
1386 REAL, INTENT(OUT
) :: lat
1387 REAL, INTENT(OUT
) :: lon
1389 REAL :: i_work
, j_work
1393 i_work
= i
- proj
%knowni
1394 j_work
= j
- proj
%knownj
1396 ! Compute deltalat and deltalon
1397 deltalat
= j_work
*proj
%latinc
1398 deltalon
= i_work
*proj
%loninc
1400 lat
= proj
%lat1
+ deltalat
1401 lon
= proj
%lon1
+ deltalon
1405 END SUBROUTINE ijll_latlon
1408 SUBROUTINE set_cyl(proj
)
1413 type(proj_info
), intent(inout
) :: proj
1417 END SUBROUTINE set_cyl
1420 SUBROUTINE llij_cyl(lat
, lon
, proj
, i
, j
)
1425 real, intent(in
) :: lat
, lon
1426 real, intent(out
) :: i
, j
1427 type(proj_info
), intent(in
) :: proj
1433 ! Compute deltalat and deltalon as the difference between the input
1434 ! lat/lon and the origin lat/lon
1435 deltalat
= lat
- proj
%lat1
1436 ! deltalon = lon - proj%stdlon
1437 deltalon
= lon
- proj
%lon1
1439 if (deltalon
< 0.) deltalon
= deltalon
+ 360.
1440 if (deltalon
> 360.) deltalon
= deltalon
- 360.
1443 i
= deltalon
/proj
%loninc
1444 j
= deltalat
/proj
%latinc
1446 if (i
<= 0.) i
= i
+ 360./proj
%loninc
1447 if (i
> 360./proj
%loninc
) i
= i
- 360./proj
%loninc
1452 END SUBROUTINE llij_cyl
1455 SUBROUTINE ijll_cyl(i
, j
, proj
, lat
, lon
)
1460 real, intent(in
) :: i
, j
1461 real, intent(out
) :: lat
, lon
1462 type(proj_info
), intent(in
) :: proj
1467 real :: i_work
, j_work
1469 i_work
= i
- proj
%knowni
1470 j_work
= j
- proj
%knownj
1472 if (i_work
< 0.) i_work
= i_work
+ 360./proj
%loninc
1473 if (i_work
>= 360./proj
%loninc
) i_work
= i_work
- 360./proj
%loninc
1475 ! Compute deltalat and deltalon
1476 deltalat
= j_work
*proj
%latinc
1477 deltalon
= i_work
*proj
%loninc
1479 lat
= deltalat
+ proj
%lat1
1480 ! lon = deltalon + proj%stdlon
1481 lon
= deltalon
+ proj
%lon1
1483 if (lon
< -180.) lon
= lon
+ 360.
1484 if (lon
> 180.) lon
= lon
- 360.
1486 END SUBROUTINE ijll_cyl
1489 SUBROUTINE set_cassini(proj
)
1494 type(proj_info
), intent(inout
) :: proj
1497 real :: comp_lat
, comp_lon
1498 logical :: global_domain
1502 ! Try to determine whether this domain has global coverage
1503 if (abs(proj
%lat1
- proj
%latinc
/2. + 90.) < 0.001 .and
. &
1504 abs(mod(proj
%lon1
- proj
%loninc
/2. - proj
%stdlon
,360.)) < 0.001) then
1505 global_domain
= .true
.
1507 global_domain
= .false
.
1510 if (abs(proj
%lat0
) /= 90. .and
. .not
.global_domain
) then
1511 call rotate_coords(proj
%lat1
,proj
%lon1
,comp_lat
,comp_lon
,proj
%lat0
,proj
%lon0
,proj
%stdlon
,-1)
1512 proj
%lat1
= comp_lat
1513 proj
%lon1
= comp_lon
1516 END SUBROUTINE set_cassini
1519 SUBROUTINE llij_cassini(lat
, lon
, proj
, i
, j
)
1524 real, intent(in
) :: lat
, lon
1525 real, intent(out
) :: i
, j
1526 type(proj_info
), intent(in
) :: proj
1529 real :: comp_lat
, comp_lon
1531 ! Convert geographic to computational lat/lon
1532 if (abs(proj
%lat0
) /= 90.) then
1533 call rotate_coords(lat
,lon
,comp_lat
,comp_lon
,proj
%lat0
,proj
%lon0
,proj
%stdlon
,-1)
1539 ! Convert computational lat/lon to i/j
1540 call llij_cyl(comp_lat
, comp_lon
, proj
, i
, j
)
1542 END SUBROUTINE llij_cassini
1545 SUBROUTINE ijll_cassini(i
, j
, proj
, lat
, lon
)
1550 real, intent(in
) :: i
, j
1551 real, intent(out
) :: lat
, lon
1552 type(proj_info
), intent(in
) :: proj
1555 real :: comp_lat
, comp_lon
1557 ! Convert i/j to computational lat/lon
1558 call ijll_cyl(i
, j
, proj
, comp_lat
, comp_lon
)
1560 ! Convert computational to geographic lat/lon
1561 if (abs(proj
%lat0
) /= 90.) then
1562 call rotate_coords(comp_lat
,comp_lon
,lat
,lon
,proj
%lat0
,proj
%lon0
,proj
%stdlon
,1)
1568 END SUBROUTINE ijll_cassini
1571 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1572 ! Purpose: Converts between computational and geographic lat/lon for Cassini
1574 ! Notes: This routine was provided by Bill Skamarock, 2007-03-27
1575 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1576 SUBROUTINE rotate_coords(ilat
,ilon
,olat
,olon
,lat_np
,lon_np
,lon_0
,direction
)
1580 REAL, INTENT(IN
) :: ilat
, ilon
1581 REAL, INTENT( OUT
) :: olat
, olon
1582 REAL, INTENT(IN
) :: lat_np
, lon_np
, lon_0
1583 INTEGER, INTENT(IN
), OPTIONAL
:: direction
1584 ! >=0, default : computational -> geographical
1585 ! < 0 : geographical -> computational
1588 REAL :: phi_np
, lam_np
, lam_0
, dlam
1589 REAL :: sinphi
, cosphi
, coslam
, sinlam
1591 ! Convert all angles to radians
1592 phi_np
= lat_np
* rad_per_deg
1593 lam_np
= lon_np
* rad_per_deg
1594 lam_0
= lon_0
* rad_per_deg
1595 rlat
= ilat
* rad_per_deg
1596 rlon
= ilon
* rad_per_deg
1598 IF (PRESENT(direction
) .AND
. (direction
< 0)) THEN
1599 ! The equations are exactly the same except for one small difference
1600 ! with respect to longitude ...
1605 sinphi
= COS(phi_np
)*COS(rlat
)*COS(rlon
-dlam
) + SIN(phi_np
)*SIN(rlat
)
1606 cosphi
= SQRT(1.-sinphi
*sinphi
)
1607 coslam
= SIN(phi_np
)*COS(rlat
)*COS(rlon
-dlam
) - COS(phi_np
)*SIN(rlat
)
1608 sinlam
= COS(rlat
)*SIN(rlon
-dlam
)
1609 IF ( cosphi
/= 0. ) THEN
1610 coslam
= coslam
/cosphi
1611 sinlam
= sinlam
/cosphi
1613 olat
= deg_per_rad
*ASIN(sinphi
)
1614 olon
= deg_per_rad
*(ATAN2(sinlam
,coslam
)-dlam
-lam_0
+lam_np
)
1615 ! Both of my F90 text books prefer the DO-EXIT form, and claim it is faster
1616 ! when optimization is turned on (as we will always do...)
1618 IF (olon
>= -180.) EXIT
1622 IF (olon
<= 180.) EXIT
1626 END SUBROUTINE rotate_coords
1629 SUBROUTINE llij_rotlatlon(lat
, lon
, proj
, i
, j
)
1634 REAL, INTENT(IN
) :: lat
, lon
1635 REAL, INTENT(OUT
) :: i
, j
1636 TYPE (proj_info
), INTENT(IN
) :: proj
1639 REAL(KIND
=HIGH
) :: dphd
,dlmd
!Grid increments, degrees
1640 INTEGER :: ii
,jj
,jmt
,ncol
,nrow
1641 REAL(KIND
=HIGH
) :: glatd
!Geographic latitude, positive north
1642 REAL(KIND
=HIGH
) :: glond
!Geographic longitude, positive west
1643 REAL(KIND
=HIGH
) :: col
,d1
,d2
,d2r
,dlm
,dlm1
,dlm2
,dph
,glat
,glon
, &
1644 pi
,r2d
,row
,tlat
,tlat1
,tlat2
, &
1645 tlon
,tlon1
,tlon2
,tph0
,tlm0
,x
,y
,z
1650 dphd
= proj
%phi
/REAL((proj
%jydim
-1)/2)
1651 dlmd
= proj
%lambda
/REAL(proj
%ixdim
-1)
1657 jmt
= proj
%jydim
/2+1
1663 tph0
= proj
%lat1
*d2r
1664 tlm0
= -proj
%lon1
*d2r
1666 x
= COS(tph0
)*COS(glat
)*COS(glon
-tlm0
)+SIN(tph0
)*SIN(glat
)
1667 y
= -COS(glat
)*SIN(glon
-tlm0
)
1668 z
= COS(tph0
)*SIN(glat
)-SIN(tph0
)*COS(glat
)*COS(glon
-tlm0
)
1669 tlat
= r2d
*ATAN(z
/SQRT(x
*x
+y
*y
))
1670 tlon
= r2d
*ATAN(y
/x
)
1673 col
= tlon
/dlmd
+proj
%ixdim
1681 IF (proj
%stagger
== HH
) THEN
1683 IF ((abs(MOD(nrow
,2)) == 1 .AND
. abs(MOD(ncol
,2)) == 1) .OR
. &
1684 (MOD(nrow
,2) == 0 .AND
. MOD(ncol
,2) == 0)) THEN
1686 tlat1
= (nrow
-jmt
)*dph
1688 tlon1
= (ncol
-proj
%ixdim
)*dlm
1693 d1
= ACOS(COS(tlat
)*COS(tlat1
)*COS(dlm1
)+SIN(tlat
)*SIN(tlat1
))
1694 d2
= ACOS(COS(tlat
)*COS(tlat2
)*COS(dlm2
)+SIN(tlat
)*SIN(tlat2
))
1702 tlat1
= (nrow
+1-jmt
)*dph
1704 tlon1
= (ncol
-proj
%ixdim
)*dlm
1708 d1
= ACOS(COS(tlat
)*COS(tlat1
)*COS(dlm1
)+SIN(tlat
)*SIN(tlat1
))
1709 d2
= ACOS(COS(tlat
)*COS(tlat2
)*COS(dlm2
)+SIN(tlat
)*SIN(tlat2
))
1718 ELSE IF (proj
%stagger
== VV
) THEN
1720 IF ((MOD(nrow
,2) == 0 .AND
. abs(MOD(ncol
,2)) == 1) .OR
. &
1721 (abs(MOD(nrow
,2)) == 1 .AND
. MOD(ncol
,2) == 0)) THEN
1722 tlat1
= (nrow
-jmt
)*dph
1724 tlon1
= (ncol
-proj
%ixdim
)*dlm
1728 d1
= ACOS(COS(tlat
)*COS(tlat1
)*COS(dlm1
)+SIN(tlat
)*SIN(tlat1
))
1729 d2
= ACOS(COS(tlat
)*COS(tlat2
)*COS(dlm2
)+SIN(tlat
)*SIN(tlat2
))
1737 tlat1
= (nrow
+1-jmt
)*dph
1739 tlon1
= (ncol
-proj
%ixdim
)*dlm
1743 d1
= ACOS(COS(tlat
)*COS(tlat1
)*COS(dlm1
)+SIN(tlat
)*SIN(tlat1
))
1744 d2
= ACOS(COS(tlat
)*COS(tlat2
)*COS(dlm2
)+SIN(tlat
)*SIN(tlat2
))
1755 !!! Added next line as a Kludge - not yet understood why needed
1756 if (ncol
.le
. 0) ncol
=ncol
-1
1761 IF (proj
%stagger
== HH
) THEN
1762 IF (abs(MOD(jj
,2)) == 1) ii
= ii
+1
1763 ELSE IF (proj
%stagger
== VV
) THEN
1764 IF (MOD(jj
,2) == 0) ii
=ii
+1
1770 END SUBROUTINE llij_rotlatlon
1773 SUBROUTINE ijll_rotlatlon(i
, j
, proj
, lat
,lon
)
1778 REAL, INTENT(IN
) :: i
, j
1779 REAL, INTENT(OUT
) :: lat
, lon
1780 TYPE (proj_info
), INTENT(IN
) :: proj
1784 INTEGER :: midcol
,midrow
,ncol
1785 REAL :: dphd
,dlmd
!Grid increments, degrees
1786 REAL(KIND
=HIGH
) :: arg1
,arg2
,d2r
,fctr
,glatr
,glatd
,glond
,pi
, &
1787 r2d
,tlatd
,tlond
,tlatr
,tlonr
,tlm0
,tph0
1792 dphd
= proj
%phi
/REAL((proj
%jydim
-1)/2)
1793 dlmd
= proj
%lambda
/REAL(proj
%ixdim
-1)
1798 tph0
= proj
%lat1
*d2r
1799 tlm0
= -proj
%lon1
*d2r
1801 midrow
= (proj
%jydim
+1)/2
1804 ! IF (proj%stagger == HH) THEN
1806 !!! ncol = 2*ih-1+MOD(jh+1,2)
1807 ncol
= 2*ih
-1+abs(MOD(jh
+1,2))
1808 tlatd
= (jh
-midrow
)*dphd
1809 tlond
= (ncol
-midcol
)*dlmd
1810 IF (proj
%stagger
== VV
) THEN
1811 if (mod(jh
,2) .eq
. 0) then
1812 tlond
= tlond
- DLMD
1814 tlond
= tlond
+ DLMD
1820 arg1
= SIN(tlatr
)*COS(tph0
)+COS(tlatr
)*SIN(tph0
)*COS(tlonr
)
1825 arg2
= COS(tlatr
)*COS(tlonr
)/(COS(glatr
)*COS(tph0
))-TAN(glatr
)*TAN(tph0
)
1826 IF (ABS(arg2
) > 1.) arg2
= ABS(arg2
)/arg2
1828 IF (tlond
> 0.) fctr
= -1.
1830 glond
= tlm0
*r2d
+fctr
*ACOS(arg2
)*r2d
1835 IF (lon
.GT
. +180.) lon
= lon
- 360.
1836 IF (lon
.LT
. -180.) lon
= lon
+ 360.
1838 END SUBROUTINE ijll_rotlatlon
1841 SUBROUTINE set_gauss(proj
)
1846 type (proj_info
), intent(inout
) :: proj
1848 ! Initialize the array that will hold the Gaussian latitudes.
1850 IF ( ASSOCIATED( proj
%gauss_lat
) ) THEN
1851 DEALLOCATE ( proj
%gauss_lat
)
1854 ! Get the needed space for our array.
1856 ALLOCATE ( proj
%gauss_lat(proj
%nlat
*2) )
1858 ! Compute the Gaussian latitudes.
1860 CALL gausll( proj
%nlat
*2 , proj
%gauss_lat
)
1862 ! Now, these could be upside down from what we want, so let's check.
1863 ! We take advantage of the equatorial symmetry to remove any sort of
1864 ! array re-ordering.
1866 IF ( ABS(proj
%gauss_lat(1) - proj
%lat1
) .GT
. 0.01 ) THEN
1867 proj
%gauss_lat
= -1. * proj
%gauss_lat
1870 ! Just a sanity check.
1872 IF ( ABS(proj
%gauss_lat(1) - proj
%lat1
) .GT
. 0.01 ) THEN
1873 PRINT '(A)','Oops, something is not right with the Gaussian latitude computation.'
1874 PRINT '(A,F8.3,A)','The input data gave the starting latitude as ',proj
%lat1
,'.'
1875 PRINT '(A,F8.3,A)','This routine computed the starting latitude as +-',ABS(proj
%gauss_lat(1)),'.'
1876 PRINT '(A,F8.3,A)','The difference is larger than 0.01 degrees, which is not expected.'
1877 call mprintf(.true
.,ERROR
,'Gaussian_latitude_computation')
1880 END SUBROUTINE set_gauss
1883 SUBROUTINE gausll ( nlat
, lat_sp
)
1888 REAL (KIND
=HIGH
) , PARAMETER :: pi
= 3.141592653589793
1889 REAL (KIND
=HIGH
) , DIMENSION(nlat
) :: cosc
, gwt
, sinc
, colat
, wos2
, lat
1890 REAL , DIMENSION(nlat
) :: lat_sp
1892 CALL lggaus(nlat
, cosc
, gwt
, sinc
, colat
, wos2
)
1895 lat(i
) = ACOS(sinc(i
)) * 180._HIGH
/ pi
1896 IF (i
.gt
.nlat
/2) lat(i
) = -lat(i
)
1901 END SUBROUTINE gausll
1904 SUBROUTINE lggaus( nlat
, cosc
, gwt
, sinc
, colat
, wos2
)
1908 ! LGGAUS finds the Gaussian latitudes by finding the roots of the
1909 ! ordinary Legendre polynomial of degree NLAT using Newton's
1913 integer NLAT
! the number of latitudes (degree of the polynomial)
1915 ! On exit: for each Gaussian latitude
1916 ! COSC - cos(colatitude) or sin(latitude)
1917 ! GWT - the Gaussian weights
1918 ! SINC - sin(colatitude) or cos(latitude)
1919 ! COLAT - the colatitudes in radians
1920 ! WOS2 - Gaussian weight over sin**2(colatitude)
1922 REAL (KIND
=HIGH
) , DIMENSION(nlat
) :: cosc
, gwt
, sinc
, colat
, wos2
1923 REAL (KIND
=HIGH
) , PARAMETER :: pi
= 3.141592653589793
1925 ! Convergence criterion for iteration of cos latitude
1927 REAL , PARAMETER :: xlim
= 1.0E-14
1929 INTEGER :: nzero
, i
, j
1930 REAL (KIND
=HIGH
) :: fi
, fi1
, a
, b
, g
, gm
, gp
, gt
, delta
, c
, d
1932 ! The number of zeros between pole and equator
1936 ! Set first guess for cos(colat)
1939 cosc(i
) = SIN( (i
-0.5)*pi
/nlat
+ pi
*0.5 )
1942 ! Constants for determining the derivative of the polynomial
1945 a
= fi
*fi1
/ SQRT(4.0*fi1
*fi1
-1.0)
1946 b
= fi1
*fi
/ SQRT(4.0*fi
*fi
-1.0)
1948 ! Loop over latitudes, iterating the search for each root
1953 ! Determine the value of the ordinary Legendre polynomial for
1954 ! the current guess root
1957 CALL lgord( g
, cosc(i
), nlat
)
1959 ! Determine the derivative of the polynomial at this point
1961 CALL lgord( gm
, cosc(i
), nlat
-1 )
1962 CALL lgord( gp
, cosc(i
), nlat
+1 )
1963 gt
= (cosc(i
)*cosc(i
)-1.0) / (a
*gp
-b
*gm
)
1965 ! Update the estimate of the root
1968 cosc(i
) = cosc(i
) - delta
1970 ! If convergence criterion has not been met, keep trying
1973 IF( ABS(delta
).GT
.xlim
) CYCLE
1975 ! Determine the Gaussian weights
1977 c
= 2.0 *( 1.0-cosc(i
)*cosc(i
) )
1978 CALL lgord( d
, cosc(i
), nlat
-1 )
1980 gwt(i
) = c
*( fi
-0.5 ) / d
1987 ! Determine the colatitudes and sin(colat) and weights over sin**2
1990 colat(i
)= ACOS(cosc(i
))
1991 sinc(i
) = SIN(colat(i
))
1992 wos2(i
) = gwt(i
) /( sinc(i
)*sinc(i
) )
1995 ! If NLAT is odd, set values at the equator
1997 IF( MOD(nlat
,2) .NE
. 0 ) THEN
2001 CALL lgord( d
, cosc(i
), nlat
-1 )
2003 gwt(i
) = c
*( fi
-0.5 ) / d
2009 ! Determine the southern hemisphere values by symmetry
2011 DO i
=nlat
-nzero
+1,nlat
2012 cosc(i
) =-cosc(nlat
+1-i
)
2013 gwt(i
) = gwt(nlat
+1-i
)
2014 colat(i
)= pi
-colat(nlat
+1-i
)
2015 sinc(i
) = sinc(nlat
+1-i
)
2016 wos2(i
) = wos2(nlat
+1-i
)
2019 END SUBROUTINE lggaus
2022 SUBROUTINE lgord( f
, cosc
, n
)
2026 ! LGORD calculates the value of an ordinary Legendre polynomial at a
2027 ! specific latitude.
2030 ! cosc - COS(colatitude)
2031 ! n - the degree of the polynomial
2034 ! f - the value of the Legendre polynomial of degree N at
2035 ! latitude ASIN(cosc)
2037 REAL (KIND
=HIGH
) :: s1
, c4
, a
, b
, fk
, f
, cosc
, colat
, c1
, fn
, ang
2040 ! Determine the colatitude
2046 c1
= c1
* SQRT( 1.0 - 1.0/(4*k
*k
) )
2056 IF (k
.eq
.n
) c4
= 0.5 * c4
2057 s1
= s1
+ c4
* COS(ang
)
2061 ang
= colat
* (fn
-fk
-2.0)
2062 c4
= ( a
* (fn
-b
+1.0) / ( b
* (fn
+fn
-a
) ) ) * c4
2067 END SUBROUTINE lgord
2070 SUBROUTINE llij_gauss (lat
, lon
, proj
, i
, j
)
2074 REAL , INTENT(IN
) :: lat
, lon
2075 REAL , INTENT(OUT
) :: i
, j
2076 TYPE (proj_info
), INTENT(IN
) :: proj
2078 INTEGER :: n
, n_low
2079 LOGICAL :: found
= .FALSE
.
2080 REAL :: diff_1
, diff_nlat
2082 ! The easy one first, get the x location. The calling routine has already made
2083 ! sure that the necessary assumptions concerning the sign of the deltalon and the
2084 ! relative east/west'ness of the longitude and the starting longitude are consistent
2085 ! to allow this easy computation.
2087 i
= ( lon
- proj
%lon1
) / proj
%loninc
+ 1.
2089 ! Since this is a global data set, we need to be concerned about wrapping the
2090 ! fields around the globe.
2092 ! IF ( ( proj%loninc .GT. 0 ) .AND. &
2093 ! ( FLOOR((lon-proj%lon1)/proj%loninc) + 1 .GE. proj%ixdim ) .AND. &
2094 ! ( lon + proj%loninc .GE. proj%lon1 + 360 ) ) THEN
2095 !! BUG: We may need to set proj%wrap, but proj is intent(in)
2096 !! WHAT IS THIS USED FOR?
2097 !! proj%wrap = .TRUE.
2099 ! ELSE IF ( ( proj%loninc .LT. 0 ) .AND. &
2100 ! ( FLOOR((lon-proj%lon1)/proj%loninc) + 1 .GE. proj%ixdim ) .AND. &
2101 ! ( lon + proj%loninc .LE. proj%lon1 - 360 ) ) THEN
2102 ! ! BUG: We may need to set proj%wrap, but proj is intent(in)
2103 ! ! WHAT IS THIS USED FOR?
2104 ! ! proj%wrap = .TRUE.
2108 ! Yet another quicky test, can we find bounding values? If not, then we may be
2109 ! dealing with putting data to a polar projection, so just give them them maximal
2110 ! value for the location. This is an OK assumption for the interpolation across the
2111 ! top of the pole, given how close the longitude lines are.
2113 IF ( ABS(lat
) .GT
. ABS(proj
%gauss_lat(1)) ) THEN
2115 diff_1
= lat
- proj
%gauss_lat(1)
2116 diff_nlat
= lat
- proj
%gauss_lat(proj
%nlat
*2)
2118 IF ( ABS(diff_1
) .LT
. ABS(diff_nlat
) ) THEN
2124 ! If the latitude is between the two bounding values, we have to search and interpolate.
2128 DO n
= 1 , proj
%nlat
*2 -1
2129 IF ( ( proj
%gauss_lat(n
) - lat
) * ( proj
%gauss_lat(n
+1) - lat
) .LE
. 0 ) THEN
2136 ! Everything still OK?
2138 IF ( .NOT
. found
) THEN
2139 PRINT '(A)','Troubles in river city. No bounding values of latitude found in the Gaussian routines.'
2140 call mprintf(.true
.,ERROR
,'Gee_no_bounding_lats_Gaussian')
2143 j
= ( ( proj
%gauss_lat(n_low
) - lat
) * ( n_low
+ 1 ) + &
2144 ( lat
- proj
%gauss_lat(n_low
+1) ) * ( n_low
) ) / &
2145 ( proj
%gauss_lat(n_low
) - proj
%gauss_lat(n_low
+1) )
2149 END SUBROUTINE llij_gauss
2151 END MODULE map_utils