PageRenderTime 186ms CodeModel.GetById 22ms RepoModel.GetById 1ms app.codeStats 0ms

/wrfv2_fire/share/module_llxy.F

http://github.com/jbeezley/wrf-fire
FORTRAN Legacy | 2213 lines | 1165 code | 513 blank | 535 comment | 31 complexity | 81f99fa933e7c7e38056409f02655699 MD5 | raw file
Possible License(s): AGPL-1.0
  1. MODULE module_llxy
  2. ! Module that defines constants, data structures, and
  3. ! subroutines used to convert grid indices to lat/lon
  4. ! and vice versa.
  5. !
  6. ! SUPPORTED PROJECTIONS
  7. ! ---------------------
  8. ! Cylindrical Lat/Lon (code = PROJ_LATLON)
  9. ! Mercator (code = PROJ_MERC)
  10. ! Lambert Conformal (code = PROJ_LC)
  11. ! Gaussian (code = PROJ_GAUSS)
  12. ! Polar Stereographic (code = PROJ_PS)
  13. ! Rotated Lat/Lon (code = PROJ_ROTLL)
  14. !
  15. ! REMARKS
  16. ! -------
  17. ! The routines contained within were adapted from routines
  18. ! obtained from NCEP's w3 library. The original NCEP routines were less
  19. ! flexible (e.g., polar-stereo routines only supported truelat of 60N/60S)
  20. ! than what we needed, so modifications based on equations in Hoke, Hayes, and
  21. ! Renninger (AFGWC/TN/79-003) were added to improve the flexibility.
  22. ! Additionally, coding was improved to F90 standards and the routines were
  23. ! combined into this module.
  24. !
  25. ! ASSUMPTIONS
  26. ! -----------
  27. ! Grid Definition:
  28. ! For mercator, lambert conformal, and polar-stereographic projections,
  29. ! the routines within assume the following:
  30. !
  31. ! 1. Grid is dimensioned (i,j) where i is the East-West direction,
  32. ! positive toward the east, and j is the north-south direction,
  33. ! positive toward the north.
  34. ! 2. Origin is at (1,1) and is located at the southwest corner,
  35. ! regardless of hemispere.
  36. ! 3. Grid spacing (dx) is always positive.
  37. ! 4. Values of true latitudes must be positive for NH domains
  38. ! and negative for SH domains.
  39. !
  40. ! For the latlon and Gaussian projection, the grid origin may be at any
  41. ! of the corners, and the deltalat and deltalon values can be signed to
  42. ! account for this using the following convention:
  43. ! Origin Location Deltalat Sign Deltalon Sign
  44. ! --------------- ------------- -------------
  45. ! SW Corner + +
  46. ! NE Corner - -
  47. ! NW Corner - +
  48. ! SE Corner + -
  49. !
  50. ! Data Definitions:
  51. ! 1. Any arguments that are a latitude value are expressed in
  52. ! degrees north with a valid range of -90 -> 90
  53. ! 2. Any arguments that are a longitude value are expressed in
  54. ! degrees east with a valid range of -180 -> 180.
  55. ! 3. Distances are in meters and are always positive.
  56. ! 4. The standard longitude (stdlon) is defined as the longitude
  57. ! line which is parallel to the grid's y-axis (j-direction), along
  58. ! which latitude increases (NOT the absolute value of latitude, but
  59. ! the actual latitude, such that latitude increases continuously
  60. ! from the south pole to the north pole) as j increases.
  61. ! 5. One true latitude value is required for polar-stereographic and
  62. ! mercator projections, and defines at which latitude the
  63. ! grid spacing is true. For lambert conformal, two true latitude
  64. ! values must be specified, but may be set equal to each other to
  65. ! specify a tangent projection instead of a secant projection.
  66. !
  67. ! USAGE
  68. ! -----
  69. ! To use the routines in this module, the calling routines must have the
  70. ! following statement at the beginning of its declaration block:
  71. ! USE map_utils
  72. !
  73. ! The use of the module not only provides access to the necessary routines,
  74. ! but also defines a structure of TYPE (proj_info) that can be used
  75. ! to declare a variable of the same type to hold your map projection
  76. ! information. It also defines some integer parameters that contain
  77. ! the projection codes so one only has to use those variable names rather
  78. ! than remembering the acutal code when using them. The basic steps are
  79. ! as follows:
  80. !
  81. ! 1. Ensure the "USE map_utils" is in your declarations.
  82. ! 2. Declare the projection information structure as type(proj_info):
  83. ! TYPE(proj_info) :: proj
  84. ! 3. Populate your structure by calling the map_set routine:
  85. ! CALL map_set(code,lat1,lon1,knowni,knownj,dx,stdlon,truelat1,truelat2,proj)
  86. ! where:
  87. ! code (input) = one of PROJ_LATLON, PROJ_MERC, PROJ_LC, PROJ_PS,
  88. ! PROJ_GAUSS, or PROJ_ROTLL
  89. ! lat1 (input) = Latitude of grid origin point (i,j)=(1,1)
  90. ! (see assumptions!)
  91. ! lon1 (input) = Longitude of grid origin
  92. ! knowni (input) = origin point, x-location
  93. ! knownj (input) = origin point, y-location
  94. ! dx (input) = grid spacing in meters (ignored for LATLON projections)
  95. ! stdlon (input) = Standard longitude for PROJ_PS and PROJ_LC,
  96. ! deltalon (see assumptions) for PROJ_LATLON,
  97. ! ignored for PROJ_MERC
  98. ! truelat1 (input) = 1st true latitude for PROJ_PS, PROJ_LC, and
  99. ! PROJ_MERC, deltalat (see assumptions) for PROJ_LATLON
  100. ! truelat2 (input) = 2nd true latitude for PROJ_LC,
  101. ! ignored for all others.
  102. ! proj (output) = The structure of type (proj_info) that will be fully
  103. ! populated after this call
  104. !
  105. ! 4. Now that the proj structure is populated, you may call either
  106. ! of the following routines:
  107. !
  108. ! latlon_to_ij(proj, lat, lon, i, j)
  109. ! ij_to_latlon(proj, i, j, lat, lon)
  110. !
  111. ! It is incumbent upon the calling routine to determine whether or
  112. ! not the values returned are within your domain's bounds. All values
  113. ! of i, j, lat, and lon are REAL values.
  114. !
  115. !
  116. ! REFERENCES
  117. ! ----------
  118. ! Hoke, Hayes, and Renninger, "Map Preojections and Grid Systems for
  119. ! Meteorological Applications." AFGWC/TN-79/003(Rev), Air Weather
  120. ! Service, 1985.
  121. !
  122. ! NCAR MM5v3 Modeling System, REGRIDDER program, module_first_guess_map.F
  123. ! NCEP routines w3fb06, w3fb07, w3fb08, w3fb09, w3fb11, w3fb12
  124. !
  125. ! HISTORY
  126. ! -------
  127. ! 27 Mar 2001 - Original Version
  128. ! Brent L. Shaw, NOAA/FSL (CSU/CIRA)
  129. !
  130. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  131. USE module_wrf_error
  132. INTEGER, PARAMETER :: HH=4, VV=5
  133. REAL, PARAMETER :: PI = 3.141592653589793
  134. REAL, PARAMETER :: OMEGA_E = 7.292e-5
  135. REAL, PARAMETER :: DEG_PER_RAD = 180./PI
  136. REAL, PARAMETER :: RAD_PER_DEG = PI/180.
  137. REAL, PARAMETER :: A_WGS84 = 6378137.
  138. REAL, PARAMETER :: B_WGS84 = 6356752.314
  139. REAL, PARAMETER :: RE_WGS84 = A_WGS84
  140. REAL, PARAMETER :: E_WGS84 = 0.081819192
  141. REAL, PARAMETER :: A_NAD83 = 6378137.
  142. REAL, PARAMETER :: RE_NAD83 = A_NAD83
  143. REAL, PARAMETER :: E_NAD83 = 0.0818187034
  144. REAL, PARAMETER :: EARTH_RADIUS_M = 6370000.
  145. REAL, PARAMETER :: EARTH_CIRC_M = 2.*PI*EARTH_RADIUS_M
  146. INTEGER, PUBLIC, PARAMETER :: PROJ_LATLON = 0
  147. INTEGER, PUBLIC, PARAMETER :: PROJ_LC = 1
  148. INTEGER, PUBLIC, PARAMETER :: PROJ_PS = 2
  149. INTEGER, PUBLIC, PARAMETER :: PROJ_PS_WGS84 = 102
  150. INTEGER, PUBLIC, PARAMETER :: PROJ_MERC = 3
  151. INTEGER, PUBLIC, PARAMETER :: PROJ_GAUSS = 4
  152. INTEGER, PUBLIC, PARAMETER :: PROJ_CYL = 5
  153. INTEGER, PUBLIC, PARAMETER :: PROJ_CASSINI = 6
  154. INTEGER, PUBLIC, PARAMETER :: PROJ_ALBERS_NAD83 = 105
  155. INTEGER, PUBLIC, PARAMETER :: PROJ_ROTLL = 203
  156. ! Define some private constants
  157. INTEGER, PRIVATE, PARAMETER :: HIGH = 8
  158. TYPE proj_info
  159. INTEGER :: code ! Integer code for projection TYPE
  160. INTEGER :: nlat ! For Gaussian -- number of latitude points
  161. ! north of the equator
  162. INTEGER :: nlon !
  163. !
  164. INTEGER :: ixdim ! For Rotated Lat/Lon -- number of mass points
  165. ! in an odd row
  166. INTEGER :: jydim ! For Rotated Lat/Lon -- number of rows
  167. INTEGER :: stagger ! For Rotated Lat/Lon -- mass or velocity grid
  168. REAL :: phi ! For Rotated Lat/Lon -- domain half-extent in
  169. ! degrees latitude
  170. REAL :: lambda ! For Rotated Lat/Lon -- domain half-extend in
  171. ! degrees longitude
  172. REAL :: lat1 ! SW latitude (1,1) in degrees (-90->90N)
  173. REAL :: lon1 ! SW longitude (1,1) in degrees (-180->180E)
  174. REAL :: lat0 ! For Cassini, latitude of projection pole
  175. REAL :: lon0 ! For Cassini, longitude of projection pole
  176. REAL :: dx ! Grid spacing in meters at truelats, used
  177. ! only for ps, lc, and merc projections
  178. REAL :: dy ! Grid spacing in meters at truelats, used
  179. ! only for ps, lc, and merc projections
  180. REAL :: latinc ! Latitude increment for cylindrical lat/lon
  181. REAL :: loninc ! Longitude increment for cylindrical lat/lon
  182. ! also the lon increment for Gaussian grid
  183. REAL :: dlat ! Lat increment for lat/lon grids
  184. REAL :: dlon ! Lon increment for lat/lon grids
  185. REAL :: stdlon ! Longitude parallel to y-axis (-180->180E)
  186. REAL :: truelat1 ! First true latitude (all projections)
  187. REAL :: truelat2 ! Second true lat (LC only)
  188. REAL :: hemi ! 1 for NH, -1 for SH
  189. REAL :: cone ! Cone factor for LC projections
  190. REAL :: polei ! Computed i-location of pole point
  191. REAL :: polej ! Computed j-location of pole point
  192. REAL :: rsw ! Computed radius to SW corner
  193. REAL :: rebydx ! Earth radius divided by dx
  194. REAL :: knowni ! X-location of known lat/lon
  195. REAL :: knownj ! Y-location of known lat/lon
  196. REAL :: re_m ! Radius of spherical earth, meters
  197. REAL :: rho0 ! For Albers equal area
  198. REAL :: nc ! For Albers equal area
  199. REAL :: bigc ! For Albers equal area
  200. LOGICAL :: init ! Flag to indicate if this struct is
  201. ! ready for use
  202. LOGICAL :: wrap ! For Gaussian -- flag to indicate wrapping
  203. ! around globe?
  204. REAL, POINTER, DIMENSION(:) :: gauss_lat ! Latitude array for Gaussian grid
  205. END TYPE proj_info
  206. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  207. CONTAINS
  208. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  209. SUBROUTINE map_init(proj)
  210. ! Initializes the map projection structure to missing values
  211. IMPLICIT NONE
  212. TYPE(proj_info), INTENT(INOUT) :: proj
  213. proj%lat1 = -999.9
  214. proj%lon1 = -999.9
  215. proj%lat0 = -999.9
  216. proj%lon0 = -999.9
  217. proj%dx = -999.9
  218. proj%dy = -999.9
  219. proj%latinc = -999.9
  220. proj%loninc = -999.9
  221. proj%stdlon = -999.9
  222. proj%truelat1 = -999.9
  223. proj%truelat2 = -999.9
  224. proj%phi = -999.9
  225. proj%lambda = -999.9
  226. proj%ixdim = -999
  227. proj%jydim = -999
  228. proj%stagger = HH
  229. proj%nlat = 0
  230. proj%nlon = 0
  231. proj%hemi = 0.0
  232. proj%cone = -999.9
  233. proj%polei = -999.9
  234. proj%polej = -999.9
  235. proj%rsw = -999.9
  236. proj%knowni = -999.9
  237. proj%knownj = -999.9
  238. proj%re_m = EARTH_RADIUS_M
  239. proj%init = .FALSE.
  240. proj%wrap = .FALSE.
  241. proj%rho0 = 0.
  242. proj%nc = 0.
  243. proj%bigc = 0.
  244. nullify(proj%gauss_lat)
  245. END SUBROUTINE map_init
  246. SUBROUTINE map_set(proj_code, proj, lat1, lon1, lat0, lon0, knowni, knownj, dx, latinc, &
  247. loninc, stdlon, truelat1, truelat2, nlat, nlon, ixdim, jydim, &
  248. stagger, phi, lambda, r_earth)
  249. ! Given a partially filled proj_info structure, this routine computes
  250. ! polei, polej, rsw, and cone (if LC projection) to complete the
  251. ! structure. This allows us to eliminate redundant calculations when
  252. ! calling the coordinate conversion routines multiple times for the
  253. ! same map.
  254. ! This will generally be the first routine called when a user wants
  255. ! to be able to use the coordinate conversion routines, and it
  256. ! will call the appropriate subroutines based on the
  257. ! proj%code which indicates which projection type this is.
  258. IMPLICIT NONE
  259. ! Declare arguments
  260. INTEGER, INTENT(IN) :: proj_code
  261. INTEGER, INTENT(IN), OPTIONAL :: nlat
  262. INTEGER, INTENT(IN), OPTIONAL :: nlon
  263. INTEGER, INTENT(IN), OPTIONAL :: ixdim
  264. INTEGER, INTENT(IN), OPTIONAL :: jydim
  265. INTEGER, INTENT(IN), OPTIONAL :: stagger
  266. REAL, INTENT(IN), OPTIONAL :: latinc
  267. REAL, INTENT(IN), OPTIONAL :: loninc
  268. REAL, INTENT(IN), OPTIONAL :: lat1
  269. REAL, INTENT(IN), OPTIONAL :: lon1
  270. REAL, INTENT(IN), OPTIONAL :: lat0
  271. REAL, INTENT(IN), OPTIONAL :: lon0
  272. REAL, INTENT(IN), OPTIONAL :: dx
  273. REAL, INTENT(IN), OPTIONAL :: stdlon
  274. REAL, INTENT(IN), OPTIONAL :: truelat1
  275. REAL, INTENT(IN), OPTIONAL :: truelat2
  276. REAL, INTENT(IN), OPTIONAL :: knowni
  277. REAL, INTENT(IN), OPTIONAL :: knownj
  278. REAL, INTENT(IN), OPTIONAL :: phi
  279. REAL, INTENT(IN), OPTIONAL :: lambda
  280. REAL, INTENT(IN), OPTIONAL :: r_earth
  281. TYPE(proj_info), INTENT(OUT) :: proj
  282. INTEGER :: iter
  283. REAL :: dummy_lon1
  284. REAL :: dummy_lon0
  285. REAL :: dummy_stdlon
  286. ! First, verify that mandatory parameters are present for the specified proj_code
  287. IF ( proj_code == PROJ_LC ) THEN
  288. IF ( .NOT.PRESENT(truelat1) .OR. &
  289. .NOT.PRESENT(truelat2) .OR. &
  290. .NOT.PRESENT(lat1) .OR. &
  291. .NOT.PRESENT(lon1) .OR. &
  292. .NOT.PRESENT(knowni) .OR. &
  293. .NOT.PRESENT(knownj) .OR. &
  294. .NOT.PRESENT(stdlon) .OR. &
  295. .NOT.PRESENT(dx) ) THEN
  296. PRINT '(A,I2)', 'The following are mandatory parameters for projection code : ', proj_code
  297. PRINT '(A)', ' truelat1, truelat2, lat1, lon1, knowni, knownj, stdlon, dx'
  298. CALL wrf_error_fatal ( 'MAP_INIT' )
  299. END IF
  300. ELSE IF ( proj_code == PROJ_PS ) THEN
  301. IF ( .NOT.PRESENT(truelat1) .OR. &
  302. .NOT.PRESENT(lat1) .OR. &
  303. .NOT.PRESENT(lon1) .OR. &
  304. .NOT.PRESENT(knowni) .OR. &
  305. .NOT.PRESENT(knownj) .OR. &
  306. .NOT.PRESENT(stdlon) .OR. &
  307. .NOT.PRESENT(dx) ) THEN
  308. PRINT '(A,I2)', 'The following are mandatory parameters for projection code : ', proj_code
  309. PRINT '(A)', ' truelat1, lat1, lon1, knonwi, knownj, stdlon, dx'
  310. CALL wrf_error_fatal ( 'MAP_INIT' )
  311. END IF
  312. ELSE IF ( proj_code == PROJ_PS_WGS84 ) THEN
  313. IF ( .NOT.PRESENT(truelat1) .OR. &
  314. .NOT.PRESENT(lat1) .OR. &
  315. .NOT.PRESENT(lon1) .OR. &
  316. .NOT.PRESENT(knowni) .OR. &
  317. .NOT.PRESENT(knownj) .OR. &
  318. .NOT.PRESENT(stdlon) .OR. &
  319. .NOT.PRESENT(dx) ) THEN
  320. PRINT '(A,I2)', 'The following are mandatory parameters for projection code : ', proj_code
  321. PRINT '(A)', ' truelat1, lat1, lon1, knonwi, knownj, stdlon, dx'
  322. CALL wrf_error_fatal ( 'MAP_INIT' )
  323. END IF
  324. ELSE IF ( proj_code == PROJ_ALBERS_NAD83 ) THEN
  325. IF ( .NOT.PRESENT(truelat1) .OR. &
  326. .NOT.PRESENT(truelat2) .OR. &
  327. .NOT.PRESENT(lat1) .OR. &
  328. .NOT.PRESENT(lon1) .OR. &
  329. .NOT.PRESENT(knowni) .OR. &
  330. .NOT.PRESENT(knownj) .OR. &
  331. .NOT.PRESENT(stdlon) .OR. &
  332. .NOT.PRESENT(dx) ) THEN
  333. PRINT '(A,I2)', 'The following are mandatory parameters for projection code : ', proj_code
  334. PRINT '(A)', ' truelat1, truelat2, lat1, lon1, knonwi, knownj, stdlon, dx'
  335. CALL wrf_error_fatal ( 'MAP_INIT' )
  336. END IF
  337. ELSE IF ( proj_code == PROJ_MERC ) THEN
  338. IF ( .NOT.PRESENT(truelat1) .OR. &
  339. .NOT.PRESENT(lat1) .OR. &
  340. .NOT.PRESENT(lon1) .OR. &
  341. .NOT.PRESENT(knowni) .OR. &
  342. .NOT.PRESENT(knownj) .OR. &
  343. .NOT.PRESENT(dx) ) THEN
  344. PRINT '(A,I2)', 'The following are mandatory parameters for projection code : ', proj_code
  345. PRINT '(A)', ' truelat1, lat1, lon1, knowni, knownj, dx'
  346. CALL wrf_error_fatal ( 'MAP_INIT' )
  347. END IF
  348. ELSE IF ( proj_code == PROJ_LATLON ) THEN
  349. IF ( .NOT.PRESENT(latinc) .OR. &
  350. .NOT.PRESENT(loninc) .OR. &
  351. .NOT.PRESENT(knowni) .OR. &
  352. .NOT.PRESENT(knownj) .OR. &
  353. .NOT.PRESENT(lat1) .OR. &
  354. .NOT.PRESENT(lon1) ) THEN
  355. PRINT '(A,I2)', 'The following are mandatory parameters for projection code : ', proj_code
  356. PRINT '(A)', ' latinc, loninc, knowni, knownj, lat1, lon1'
  357. CALL wrf_error_fatal ( 'MAP_INIT' )
  358. END IF
  359. ELSE IF ( proj_code == PROJ_CYL ) THEN
  360. IF ( .NOT.PRESENT(latinc) .OR. &
  361. .NOT.PRESENT(loninc) .OR. &
  362. .NOT.PRESENT(stdlon) ) THEN
  363. PRINT '(A,I2)', 'The following are mandatory parameters for projection code : ', proj_code
  364. PRINT '(A)', ' latinc, loninc, stdlon'
  365. CALL wrf_error_fatal ( 'MAP_INIT' )
  366. END IF
  367. ELSE IF ( proj_code == PROJ_CASSINI ) THEN
  368. IF ( .NOT.PRESENT(latinc) .OR. &
  369. .NOT.PRESENT(loninc) .OR. &
  370. .NOT.PRESENT(lat1) .OR. &
  371. .NOT.PRESENT(lon1) .OR. &
  372. .NOT.PRESENT(lat0) .OR. &
  373. .NOT.PRESENT(lon0) .OR. &
  374. .NOT.PRESENT(knowni) .OR. &
  375. .NOT.PRESENT(knownj) .OR. &
  376. .NOT.PRESENT(stdlon) ) THEN
  377. PRINT '(A,I2)', 'The following are mandatory parameters for projection code : ', proj_code
  378. PRINT '(A)', ' latinc, loninc, lat1, lon1, knowni, knownj, lat0, lon0, stdlon'
  379. CALL wrf_error_fatal ( 'MAP_INIT' )
  380. END IF
  381. ELSE IF ( proj_code == PROJ_GAUSS ) THEN
  382. IF ( .NOT.PRESENT(nlat) .OR. &
  383. .NOT.PRESENT(lat1) .OR. &
  384. .NOT.PRESENT(lon1) .OR. &
  385. .NOT.PRESENT(loninc) ) THEN
  386. PRINT '(A,I2)', 'The following are mandatory parameters for projection code : ', proj_code
  387. PRINT '(A)', ' nlat, lat1, lon1, loninc'
  388. CALL wrf_error_fatal ( 'MAP_INIT' )
  389. END IF
  390. ELSE IF ( proj_code == PROJ_ROTLL ) THEN
  391. IF ( .NOT.PRESENT(ixdim) .OR. &
  392. .NOT.PRESENT(jydim) .OR. &
  393. .NOT.PRESENT(phi) .OR. &
  394. .NOT.PRESENT(lambda) .OR. &
  395. .NOT.PRESENT(lat1) .OR. &
  396. .NOT.PRESENT(lon1) .OR. &
  397. .NOT.PRESENT(stagger) ) THEN
  398. PRINT '(A,I2)', 'The following are mandatory parameters for projection code : ', proj_code
  399. PRINT '(A)', ' ixdim, jydim, phi, lambda, lat1, lon1, stagger'
  400. CALL wrf_error_fatal ( 'MAP_INIT' )
  401. END IF
  402. ELSE
  403. PRINT '(A,I2)', 'Unknown projection code: ', proj_code
  404. CALL wrf_error_fatal ( 'MAP_INIT' )
  405. END IF
  406. ! Check for validity of mandatory variables in proj
  407. IF ( PRESENT(lat1) ) THEN
  408. IF ( ABS(lat1) .GT. 90. ) THEN
  409. PRINT '(A)', 'Latitude of origin corner required as follows:'
  410. PRINT '(A)', ' -90N <= lat1 < = 90.N'
  411. CALL wrf_error_fatal ( 'MAP_INIT' )
  412. ENDIF
  413. ENDIF
  414. IF ( PRESENT(lon1) ) THEN
  415. dummy_lon1 = lon1
  416. IF ( ABS(dummy_lon1) .GT. 180.) THEN
  417. iter = 0
  418. DO WHILE (ABS(dummy_lon1) > 180. .AND. iter < 10)
  419. IF (dummy_lon1 < -180.) dummy_lon1 = dummy_lon1 + 360.
  420. IF (dummy_lon1 > 180.) dummy_lon1 = dummy_lon1 - 360.
  421. iter = iter + 1
  422. END DO
  423. IF (abs(dummy_lon1) > 180.) THEN
  424. PRINT '(A)', 'Longitude of origin required as follows:'
  425. PRINT '(A)', ' -180E <= lon1 <= 180W'
  426. CALL wrf_error_fatal ( 'MAP_INIT' )
  427. ENDIF
  428. ENDIF
  429. ENDIF
  430. IF ( PRESENT(lon0) ) THEN
  431. dummy_lon0 = lon0
  432. IF ( ABS(dummy_lon0) .GT. 180.) THEN
  433. iter = 0
  434. DO WHILE (ABS(dummy_lon0) > 180. .AND. iter < 10)
  435. IF (dummy_lon0 < -180.) dummy_lon0 = dummy_lon0 + 360.
  436. IF (dummy_lon0 > 180.) dummy_lon0 = dummy_lon0 - 360.
  437. iter = iter + 1
  438. END DO
  439. IF (abs(dummy_lon0) > 180.) THEN
  440. PRINT '(A)', 'Longitude of pole required as follows:'
  441. PRINT '(A)', ' -180E <= lon0 <= 180W'
  442. CALL wrf_error_fatal ( 'MAP_INIT' )
  443. ENDIF
  444. ENDIF
  445. ENDIF
  446. IF ( PRESENT(dx) ) THEN
  447. IF ((dx .LE. 0.).AND.(proj_code .NE. PROJ_LATLON)) THEN
  448. PRINT '(A)', 'Require grid spacing (dx) in meters be positive'
  449. CALL wrf_error_fatal ( 'MAP_INIT' )
  450. ENDIF
  451. ENDIF
  452. IF ( PRESENT(stdlon) ) THEN
  453. dummy_stdlon = stdlon
  454. IF ((ABS(dummy_stdlon) > 180.).AND.(proj_code /= PROJ_MERC)) THEN
  455. iter = 0
  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.
  459. iter = iter + 1
  460. END DO
  461. IF (abs(dummy_stdlon) > 180.) THEN
  462. PRINT '(A)', 'Need orientation longitude (stdlon) as: '
  463. PRINT '(A)', ' -180E <= stdlon <= 180W'
  464. CALL wrf_error_fatal ( 'MAP_INIT' )
  465. ENDIF
  466. ENDIF
  467. ENDIF
  468. IF ( PRESENT(truelat1) ) THEN
  469. IF (ABS(truelat1).GT.90.) THEN
  470. PRINT '(A)', 'Set true latitude 1 for all projections'
  471. CALL wrf_error_fatal ( 'MAP_INIT' )
  472. ENDIF
  473. ENDIF
  474. CALL map_init(proj)
  475. proj%code = proj_code
  476. IF ( PRESENT(lat1) ) proj%lat1 = lat1
  477. IF ( PRESENT(lon1) ) proj%lon1 = dummy_lon1
  478. IF ( PRESENT(lat0) ) proj%lat0 = lat0
  479. IF ( PRESENT(lon0) ) proj%lon0 = dummy_lon0
  480. IF ( PRESENT(latinc) ) proj%latinc = latinc
  481. IF ( PRESENT(loninc) ) proj%loninc = loninc
  482. IF ( PRESENT(knowni) ) proj%knowni = knowni
  483. IF ( PRESENT(knownj) ) proj%knownj = knownj
  484. IF ( PRESENT(dx) ) proj%dx = dx
  485. IF ( PRESENT(stdlon) ) proj%stdlon = dummy_stdlon
  486. IF ( PRESENT(truelat1) ) proj%truelat1 = truelat1
  487. IF ( PRESENT(truelat2) ) proj%truelat2 = truelat2
  488. IF ( PRESENT(nlat) ) proj%nlat = nlat
  489. IF ( PRESENT(nlon) ) proj%nlon = nlon
  490. IF ( PRESENT(ixdim) ) proj%ixdim = ixdim
  491. IF ( PRESENT(jydim) ) proj%jydim = jydim
  492. IF ( PRESENT(stagger) ) proj%stagger = stagger
  493. IF ( PRESENT(phi) ) proj%phi = phi
  494. IF ( PRESENT(lambda) ) proj%lambda = lambda
  495. IF ( PRESENT(r_earth) ) proj%re_m = r_earth
  496. IF ( PRESENT(dx) ) THEN
  497. IF ( (proj_code == PROJ_LC) .OR. (proj_code == PROJ_PS) .OR. &
  498. (proj_code == PROJ_PS_WGS84) .OR. (proj_code == PROJ_ALBERS_NAD83) .OR. &
  499. (proj_code == PROJ_MERC) ) THEN
  500. proj%dx = dx
  501. IF (truelat1 .LT. 0.) THEN
  502. proj%hemi = -1.0
  503. ELSE
  504. proj%hemi = 1.0
  505. ENDIF
  506. proj%rebydx = proj%re_m / dx
  507. ENDIF
  508. ENDIF
  509. pick_proj: SELECT CASE(proj%code)
  510. CASE(PROJ_PS)
  511. CALL set_ps(proj)
  512. CASE(PROJ_PS_WGS84)
  513. CALL set_ps_wgs84(proj)
  514. CASE(PROJ_ALBERS_NAD83)
  515. CALL set_albers_nad83(proj)
  516. CASE(PROJ_LC)
  517. IF (ABS(proj%truelat2) .GT. 90.) THEN
  518. proj%truelat2=proj%truelat1
  519. ENDIF
  520. CALL set_lc(proj)
  521. CASE (PROJ_MERC)
  522. CALL set_merc(proj)
  523. CASE (PROJ_LATLON)
  524. CASE (PROJ_GAUSS)
  525. CALL set_gauss(proj)
  526. CASE (PROJ_CYL)
  527. CALL set_cyl(proj)
  528. CASE (PROJ_CASSINI)
  529. CALL set_cassini(proj)
  530. CASE (PROJ_ROTLL)
  531. END SELECT pick_proj
  532. proj%init = .TRUE.
  533. RETURN
  534. END SUBROUTINE map_set
  535. SUBROUTINE latlon_to_ij(proj, lat, lon, i, j)
  536. ! Converts input lat/lon values to the cartesian (i,j) value
  537. ! for the given projection.
  538. IMPLICIT NONE
  539. TYPE(proj_info), INTENT(IN) :: proj
  540. REAL, INTENT(IN) :: lat
  541. REAL, INTENT(IN) :: lon
  542. REAL, INTENT(OUT) :: i
  543. REAL, INTENT(OUT) :: j
  544. IF (.NOT.proj%init) THEN
  545. PRINT '(A)', 'You have not called map_set for this projection'
  546. CALL wrf_error_fatal ( 'LATLON_TO_IJ' )
  547. ENDIF
  548. SELECT CASE(proj%code)
  549. CASE(PROJ_LATLON)
  550. CALL llij_latlon(lat,lon,proj,i,j)
  551. CASE(PROJ_MERC)
  552. CALL llij_merc(lat,lon,proj,i,j)
  553. CASE(PROJ_PS)
  554. CALL llij_ps(lat,lon,proj,i,j)
  555. CASE(PROJ_PS_WGS84)
  556. CALL llij_ps_wgs84(lat,lon,proj,i,j)
  557. CASE(PROJ_ALBERS_NAD83)
  558. CALL llij_albers_nad83(lat,lon,proj,i,j)
  559. CASE(PROJ_LC)
  560. CALL llij_lc(lat,lon,proj,i,j)
  561. CASE(PROJ_GAUSS)
  562. CALL llij_gauss(lat,lon,proj,i,j)
  563. CASE(PROJ_CYL)
  564. CALL llij_cyl(lat,lon,proj,i,j)
  565. CASE(PROJ_CASSINI)
  566. CALL llij_cassini(lat,lon,proj,i,j)
  567. CASE(PROJ_ROTLL)
  568. CALL llij_rotlatlon(lat,lon,proj,i,j)
  569. CASE DEFAULT
  570. PRINT '(A,I2)', 'Unrecognized map projection code: ', proj%code
  571. CALL wrf_error_fatal ( 'LATLON_TO_IJ' )
  572. END SELECT
  573. RETURN
  574. END SUBROUTINE latlon_to_ij
  575. SUBROUTINE ij_to_latlon(proj, i, j, lat, lon)
  576. ! Computes geographical latitude and longitude for a given (i,j) point
  577. ! in a grid with a projection of proj
  578. IMPLICIT NONE
  579. TYPE(proj_info),INTENT(IN) :: proj
  580. REAL, INTENT(IN) :: i
  581. REAL, INTENT(IN) :: j
  582. REAL, INTENT(OUT) :: lat
  583. REAL, INTENT(OUT) :: lon
  584. IF (.NOT.proj%init) THEN
  585. PRINT '(A)', 'You have not called map_set for this projection'
  586. CALL wrf_error_fatal ( 'IJ_TO_LATLON' )
  587. ENDIF
  588. SELECT CASE (proj%code)
  589. CASE (PROJ_LATLON)
  590. CALL ijll_latlon(i, j, proj, lat, lon)
  591. CASE (PROJ_MERC)
  592. CALL ijll_merc(i, j, proj, lat, lon)
  593. CASE (PROJ_PS)
  594. CALL ijll_ps(i, j, proj, lat, lon)
  595. CASE (PROJ_PS_WGS84)
  596. CALL ijll_ps_wgs84(i, j, proj, lat, lon)
  597. CASE (PROJ_ALBERS_NAD83)
  598. CALL ijll_albers_nad83(i, j, proj, lat, lon)
  599. CASE (PROJ_LC)
  600. CALL ijll_lc(i, j, proj, lat, lon)
  601. CASE (PROJ_CYL)
  602. CALL ijll_cyl(i, j, proj, lat, lon)
  603. CASE (PROJ_CASSINI)
  604. CALL ijll_cassini(i, j, proj, lat, lon)
  605. CASE (PROJ_ROTLL)
  606. CALL ijll_rotlatlon(i, j, proj, lat, lon)
  607. CASE DEFAULT
  608. PRINT '(A,I2)', 'Unrecognized map projection code: ', proj%code
  609. CALL wrf_error_fatal ( 'IJ_TO_LATLON' )
  610. END SELECT
  611. RETURN
  612. END SUBROUTINE ij_to_latlon
  613. SUBROUTINE set_ps(proj)
  614. ! Initializes a polar-stereographic map projection from the partially
  615. ! filled proj structure. This routine computes the radius to the
  616. ! southwest corner and computes the i/j location of the pole for use
  617. ! in llij_ps and ijll_ps.
  618. IMPLICIT NONE
  619. ! Declare args
  620. TYPE(proj_info), INTENT(INOUT) :: proj
  621. ! Local vars
  622. REAL :: ala1
  623. REAL :: alo1
  624. REAL :: reflon
  625. REAL :: scale_top
  626. ! Executable code
  627. reflon = proj%stdlon + 90.
  628. ! Compute numerator term of map scale factor
  629. scale_top = 1. + proj%hemi * SIN(proj%truelat1 * rad_per_deg)
  630. ! Compute radius to lower-left (SW) corner
  631. ala1 = proj%lat1 * rad_per_deg
  632. proj%rsw = proj%rebydx*COS(ala1)*scale_top/(1.+proj%hemi*SIN(ala1))
  633. ! Find the pole point
  634. alo1 = (proj%lon1 - reflon) * rad_per_deg
  635. proj%polei = proj%knowni - proj%rsw * COS(alo1)
  636. proj%polej = proj%knownj - proj%hemi * proj%rsw * SIN(alo1)
  637. RETURN
  638. END SUBROUTINE set_ps
  639. SUBROUTINE llij_ps(lat,lon,proj,i,j)
  640. ! Given latitude (-90 to 90), longitude (-180 to 180), and the
  641. ! standard polar-stereographic projection information via the
  642. ! public proj structure, this routine returns the i/j indices which
  643. ! if within the domain range from 1->nx and 1->ny, respectively.
  644. IMPLICIT NONE
  645. ! Delcare input arguments
  646. REAL, INTENT(IN) :: lat
  647. REAL, INTENT(IN) :: lon
  648. TYPE(proj_info),INTENT(IN) :: proj
  649. ! Declare output arguments
  650. REAL, INTENT(OUT) :: i !(x-index)
  651. REAL, INTENT(OUT) :: j !(y-index)
  652. ! Declare local variables
  653. REAL :: reflon
  654. REAL :: scale_top
  655. REAL :: ala
  656. REAL :: alo
  657. REAL :: rm
  658. ! BEGIN CODE
  659. reflon = proj%stdlon + 90.
  660. ! Compute numerator term of map scale factor
  661. scale_top = 1. + proj%hemi * SIN(proj%truelat1 * rad_per_deg)
  662. ! Find radius to desired point
  663. ala = lat * rad_per_deg
  664. rm = proj%rebydx * COS(ala) * scale_top/(1. + proj%hemi *SIN(ala))
  665. alo = (lon - reflon) * rad_per_deg
  666. i = proj%polei + rm * COS(alo)
  667. j = proj%polej + proj%hemi * rm * SIN(alo)
  668. RETURN
  669. END SUBROUTINE llij_ps
  670. SUBROUTINE ijll_ps(i, j, proj, lat, lon)
  671. ! This is the inverse subroutine of llij_ps. It returns the
  672. ! latitude and longitude of an i/j point given the projection info
  673. ! structure.
  674. IMPLICIT NONE
  675. ! Declare input arguments
  676. REAL, INTENT(IN) :: i ! Column
  677. REAL, INTENT(IN) :: j ! Row
  678. TYPE (proj_info), INTENT(IN) :: proj
  679. ! Declare output arguments
  680. REAL, INTENT(OUT) :: lat ! -90 -> 90 north
  681. REAL, INTENT(OUT) :: lon ! -180 -> 180 East
  682. ! Local variables
  683. REAL :: reflon
  684. REAL :: scale_top
  685. REAL :: xx,yy
  686. REAL :: gi2, r2
  687. REAL :: arccos
  688. ! Begin Code
  689. ! Compute the reference longitude by rotating 90 degrees to the east
  690. ! to find the longitude line parallel to the positive x-axis.
  691. reflon = proj%stdlon + 90.
  692. ! Compute numerator term of map scale factor
  693. scale_top = 1. + proj%hemi * SIN(proj%truelat1 * rad_per_deg)
  694. ! Compute radius to point of interest
  695. xx = i - proj%polei
  696. yy = (j - proj%polej) * proj%hemi
  697. r2 = xx**2 + yy**2
  698. ! Now the magic code
  699. IF (r2 .EQ. 0.) THEN
  700. lat = proj%hemi * 90.
  701. lon = reflon
  702. ELSE
  703. gi2 = (proj%rebydx * scale_top)**2.
  704. lat = deg_per_rad * proj%hemi * ASIN((gi2-r2)/(gi2+r2))
  705. arccos = ACOS(xx/SQRT(r2))
  706. IF (yy .GT. 0) THEN
  707. lon = reflon + deg_per_rad * arccos
  708. ELSE
  709. lon = reflon - deg_per_rad * arccos
  710. ENDIF
  711. ENDIF
  712. ! Convert to a -180 -> 180 East convention
  713. IF (lon .GT. 180.) lon = lon - 360.
  714. IF (lon .LT. -180.) lon = lon + 360.
  715. RETURN
  716. END SUBROUTINE ijll_ps
  717. SUBROUTINE set_ps_wgs84(proj)
  718. ! Initializes a polar-stereographic map projection (WGS84 ellipsoid)
  719. ! from the partially filled proj structure. This routine computes the
  720. ! radius to the southwest corner and computes the i/j location of the
  721. ! pole for use in llij_ps and ijll_ps.
  722. IMPLICIT NONE
  723. ! Arguments
  724. TYPE(proj_info), INTENT(INOUT) :: proj
  725. ! Local variables
  726. real :: h, mc, tc, t, rho
  727. h = proj%hemi
  728. mc = cos(h*proj%truelat1*rad_per_deg)/sqrt(1.0-(E_WGS84*sin(h*proj%truelat1*rad_per_deg))**2.0)
  729. tc = sqrt(((1.0-sin(h*proj%truelat1*rad_per_deg))/(1.0+sin(h*proj%truelat1*rad_per_deg)))* &
  730. (((1.0+E_WGS84*sin(h*proj%truelat1*rad_per_deg))/(1.0-E_WGS84*sin(h*proj%truelat1*rad_per_deg)))**E_WGS84 ))
  731. ! Find the i/j location of reference lat/lon with respect to the pole of the projection
  732. t = sqrt(((1.0-sin(h*proj%lat1*rad_per_deg))/(1.0+sin(h*proj%lat1*rad_per_deg)))* &
  733. (((1.0+E_WGS84*sin(h*proj%lat1*rad_per_deg))/(1.0-E_WGS84*sin(h*proj%lat1*rad_per_deg)) )**E_WGS84 ) )
  734. rho = h * (A_WGS84 / proj%dx) * mc * t / tc
  735. proj%polei = rho * sin((h*proj%lon1 - h*proj%stdlon)*rad_per_deg)
  736. proj%polej = -rho * cos((h*proj%lon1 - h*proj%stdlon)*rad_per_deg)
  737. RETURN
  738. END SUBROUTINE set_ps_wgs84
  739. SUBROUTINE llij_ps_wgs84(lat,lon,proj,i,j)
  740. ! Given latitude (-90 to 90), longitude (-180 to 180), and the
  741. ! standard polar-stereographic projection information via the
  742. ! public proj structure, this routine returns the i/j indices which
  743. ! if within the domain range from 1->nx and 1->ny, respectively.
  744. IMPLICIT NONE
  745. ! Arguments
  746. REAL, INTENT(IN) :: lat
  747. REAL, INTENT(IN) :: lon
  748. REAL, INTENT(OUT) :: i !(x-index)
  749. REAL, INTENT(OUT) :: j !(y-index)
  750. TYPE(proj_info),INTENT(IN) :: proj
  751. ! Local variables
  752. real :: h, mc, tc, t, rho
  753. h = proj%hemi
  754. mc = cos(h*proj%truelat1*rad_per_deg)/sqrt(1.0-(E_WGS84*sin(h*proj%truelat1*rad_per_deg))**2.0)
  755. tc = sqrt(((1.0-sin(h*proj%truelat1*rad_per_deg))/(1.0+sin(h*proj%truelat1*rad_per_deg)))* &
  756. (((1.0+E_WGS84*sin(h*proj%truelat1*rad_per_deg))/(1.0-E_WGS84*sin(h*proj%truelat1*rad_per_deg)))**E_WGS84 ))
  757. t = sqrt(((1.0-sin(h*lat*rad_per_deg))/(1.0+sin(h*lat*rad_per_deg))) * &
  758. (((1.0+E_WGS84*sin(h*lat*rad_per_deg))/(1.0-E_WGS84*sin(h*lat*rad_per_deg)))**E_WGS84))
  759. ! Find the x/y location of the requested lat/lon with respect to the pole of the projection
  760. rho = (A_WGS84 / proj%dx) * mc * t / tc
  761. i = h * rho * sin((h*lon - h*proj%stdlon)*rad_per_deg)
  762. j = h *(-rho)* cos((h*lon - h*proj%stdlon)*rad_per_deg)
  763. ! Get i/j relative to reference i/j
  764. i = proj%knowni + (i - proj%polei)
  765. j = proj%knownj + (j - proj%polej)
  766. RETURN
  767. END SUBROUTINE llij_ps_wgs84
  768. SUBROUTINE ijll_ps_wgs84(i, j, proj, lat, lon)
  769. ! This is the inverse subroutine of llij_ps. It returns the
  770. ! latitude and longitude of an i/j point given the projection info
  771. ! structure.
  772. implicit none
  773. ! Arguments
  774. REAL, INTENT(IN) :: i ! Column
  775. REAL, INTENT(IN) :: j ! Row
  776. REAL, INTENT(OUT) :: lat ! -90 -> 90 north
  777. REAL, INTENT(OUT) :: lon ! -180 -> 180 East
  778. TYPE (proj_info), INTENT(IN) :: proj
  779. ! Local variables
  780. real :: h, mc, tc, t, rho, x, y
  781. real :: chi, a, b, c, d
  782. h = proj%hemi
  783. x = (i - proj%knowni + proj%polei)
  784. y = (j - proj%knownj + proj%polej)
  785. mc = cos(h*proj%truelat1*rad_per_deg)/sqrt(1.0-(E_WGS84*sin(h*proj%truelat1*rad_per_deg))**2.0)
  786. tc = sqrt(((1.0-sin(h*proj%truelat1*rad_per_deg))/(1.0+sin(h*proj%truelat1*rad_per_deg))) * &
  787. (((1.0+E_WGS84*sin(h*proj%truelat1*rad_per_deg))/(1.0-E_WGS84*sin(h*proj%truelat1*rad_per_deg)))**E_WGS84 ))
  788. rho = sqrt((x*proj%dx)**2.0 + (y*proj%dx)**2.0)
  789. t = rho * tc / (A_WGS84 * mc)
  790. lon = h*proj%stdlon + h*atan2(h*x,h*(-y))
  791. chi = PI/2.0-2.0*atan(t)
  792. a = 1./2.*E_WGS84**2. + 5./24.*E_WGS84**4. + 1./40.*E_WGS84**6. + 73./2016.*E_WGS84**8.
  793. b = 7./24.*E_WGS84**4. + 29./120.*E_WGS84**6. + 54113./40320.*E_WGS84**8.
  794. c = 7./30.*E_WGS84**6. + 81./280.*E_WGS84**8.
  795. d = 4279./20160.*E_WGS84**8.
  796. lat = chi + sin(2.*chi)*(a + cos(2.*chi)*(b + cos(2.*chi)*(c + d*cos(2.*chi))))
  797. lat = h * lat
  798. lat = lat*deg_per_rad
  799. lon = lon*deg_per_rad
  800. RETURN
  801. END SUBROUTINE ijll_ps_wgs84
  802. SUBROUTINE set_albers_nad83(proj)
  803. ! Initializes an Albers equal area map projection (NAD83 ellipsoid)
  804. ! from the partially filled proj structure. This routine computes the
  805. ! radius to the southwest corner and computes the i/j location of the
  806. ! pole for use in llij_albers_nad83 and ijll_albers_nad83.
  807. IMPLICIT NONE
  808. ! Arguments
  809. TYPE(proj_info), INTENT(INOUT) :: proj
  810. ! Local variables
  811. real :: h, m1, m2, q1, q2, theta, q, sinphi
  812. h = proj%hemi
  813. m1 = cos(h*proj%truelat1*rad_per_deg)/sqrt(1.0-(E_NAD83*sin(h*proj%truelat1*rad_per_deg))**2.0)
  814. m2 = cos(h*proj%truelat2*rad_per_deg)/sqrt(1.0-(E_NAD83*sin(h*proj%truelat2*rad_per_deg))**2.0)
  815. sinphi = sin(proj%truelat1*rad_per_deg)
  816. q1 = (1.0-E_NAD83**2.0) * &
  817. ((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)))
  818. sinphi = sin(proj%truelat2*rad_per_deg)
  819. q2 = (1.0-E_NAD83**2.0) * &
  820. ((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)))
  821. if (proj%truelat1 == proj%truelat2) then
  822. proj%nc = sin(proj%truelat1*rad_per_deg)
  823. else
  824. proj%nc = (m1**2.0 - m2**2.0) / (q2 - q1)
  825. end if
  826. proj%bigc = m1**2.0 + proj%nc*q1
  827. ! Find the i/j location of reference lat/lon with respect to the pole of the projection
  828. sinphi = sin(proj%lat1*rad_per_deg)
  829. q = (1.0-E_NAD83**2.0) * &
  830. ((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)))
  831. proj%rho0 = h * (A_NAD83 / proj%dx) * sqrt(proj%bigc - proj%nc * q) / proj%nc
  832. theta = proj%nc*(proj%lon1 - proj%stdlon)*rad_per_deg
  833. proj%polei = proj%rho0 * sin(h*theta)
  834. proj%polej = proj%rho0 - proj%rho0 * cos(h*theta)
  835. RETURN
  836. END SUBROUTINE set_albers_nad83
  837. SUBROUTINE llij_albers_nad83(lat,lon,proj,i,j)
  838. ! Given latitude (-90 to 90), longitude (-180 to 180), and the
  839. ! standard projection information via the
  840. ! public proj structure, this routine returns the i/j indices which
  841. ! if within the domain range from 1->nx and 1->ny, respectively.
  842. IMPLICIT NONE
  843. ! Arguments
  844. REAL, INTENT(IN) :: lat
  845. REAL, INTENT(IN) :: lon
  846. REAL, INTENT(OUT) :: i !(x-index)
  847. REAL, INTENT(OUT) :: j !(y-index)
  848. TYPE(proj_info),INTENT(IN) :: proj
  849. ! Local variables
  850. real :: h, q, rho, theta, sinphi
  851. h = proj%hemi
  852. sinphi = sin(h*lat*rad_per_deg)
  853. ! Find the x/y location of the requested lat/lon with respect to the pole of the projection
  854. q = (1.0-E_NAD83**2.0) * &
  855. ((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)))
  856. rho = h * (A_NAD83 / proj%dx) * sqrt(proj%bigc - proj%nc * q) / proj%nc
  857. theta = proj%nc * (h*lon - h*proj%stdlon)*rad_per_deg
  858. i = h*rho*sin(theta)
  859. j = h*proj%rho0 - h*rho*cos(theta)
  860. ! Get i/j relative to reference i/j
  861. i = proj%knowni + (i - proj%polei)
  862. j = proj%knownj + (j - proj%polej)
  863. RETURN
  864. END SUBROUTINE llij_albers_nad83
  865. SUBROUTINE ijll_albers_nad83(i, j, proj, lat, lon)
  866. ! This is the inverse subroutine of llij_albers_nad83. It returns the
  867. ! latitude and longitude of an i/j point given the projection info
  868. ! structure.
  869. implicit none
  870. ! Arguments
  871. REAL, INTENT(IN) :: i ! Column
  872. REAL, INTENT(IN) :: j ! Row
  873. REAL, INTENT(OUT) :: lat ! -90 -> 90 north
  874. REAL, INTENT(OUT) :: lon ! -180 -> 180 East
  875. TYPE (proj_info), INTENT(IN) :: proj
  876. ! Local variables
  877. real :: h, q, rho, theta, beta, x, y
  878. real :: a, b, c
  879. h = proj%hemi
  880. x = (i - proj%knowni + proj%polei)
  881. y = (j - proj%knownj + proj%polej)
  882. rho = sqrt(x**2.0 + (proj%rho0 - y)**2.0)
  883. theta = atan2(x, proj%rho0-y)
  884. q = (proj%bigc - (rho*proj%nc*proj%dx/A_NAD83)**2.0) / proj%nc
  885. beta = asin(q/(1.0 - log((1.0-E_NAD83)/(1.0+E_NAD83))*(1.0-E_NAD83**2.0)/(2.0*E_NAD83)))
  886. a = 1./3.*E_NAD83**2. + 31./180.*E_NAD83**4. + 517./5040.*E_NAD83**6.
  887. b = 23./360.*E_NAD83**4. + 251./3780.*E_NAD83**6.
  888. c = 761./45360.*E_NAD83**6.
  889. lat = beta + a*sin(2.*beta) + b*sin(4.*beta) + c*sin(6.*beta)
  890. lat = h*lat*deg_per_rad
  891. lon = proj%stdlon + theta*deg_per_rad/proj%nc
  892. RETURN
  893. END SUBROUTINE ijll_albers_nad83
  894. SUBROUTINE set_lc(proj)
  895. ! Initialize the remaining items in the proj structure for a
  896. ! lambert conformal grid.
  897. IMPLICIT NONE
  898. TYPE(proj_info), INTENT(INOUT) :: proj
  899. REAL :: arg
  900. REAL :: deltalon1
  901. REAL :: tl1r
  902. REAL :: ctl1r
  903. ! Compute cone factor
  904. CALL lc_cone(proj%truelat1, proj%truelat2, proj%cone)
  905. ! Compute longitude differences and ensure we stay out of the
  906. ! forbidden "cut zone"
  907. deltalon1 = proj%lon1 - proj%stdlon
  908. IF (deltalon1 .GT. +180.) deltalon1 = deltalon1 - 360.
  909. IF (deltalon1 .LT. -180.) deltalon1 = deltalon1 + 360.
  910. ! Convert truelat1 to radian and compute COS for later use
  911. tl1r = proj%truelat1 * rad_per_deg
  912. ctl1r = COS(tl1r)
  913. ! Compute the radius to our known lower-left (SW) corner
  914. proj%rsw = proj%rebydx * ctl1r/proj%cone * &
  915. (TAN((90.*proj%hemi-proj%lat1)*rad_per_deg/2.) / &
  916. TAN((90.*proj%hemi-proj%truelat1)*rad_per_deg/2.))**proj%cone
  917. ! Find pole point
  918. arg = proj%cone*(deltalon1*rad_per_deg)
  919. proj%polei = proj%hemi*proj%knowni - proj%hemi * proj%rsw * SIN(arg)
  920. proj%polej = proj%hemi*proj%knownj + proj%rsw * COS(arg)
  921. RETURN
  922. END SUBROUTINE set_lc
  923. SUBROUTINE lc_cone(truelat1, truelat2, cone)
  924. ! Subroutine to compute the cone factor of a Lambert Conformal projection
  925. IMPLICIT NONE
  926. ! Input Args
  927. REAL, INTENT(IN) :: truelat1 ! (-90 -> 90 degrees N)
  928. REAL, INTENT(IN) :: truelat2 ! " " " " "
  929. ! Output Args
  930. REAL, INTENT(OUT) :: cone
  931. ! Locals
  932. ! BEGIN CODE
  933. ! First, see if this is a secant or tangent projection. For tangent
  934. ! projections, truelat1 = truelat2 and the cone is tangent to the
  935. ! Earth's surface at this latitude. For secant projections, the cone
  936. ! intersects the Earth's surface at each of the distinctly different
  937. ! latitudes
  938. IF (ABS(truelat1-truelat2) .GT. 0.1) THEN
  939. cone = ALOG10(COS(truelat1*rad_per_deg)) - &
  940. ALOG10(COS(truelat2*rad_per_deg))
  941. cone = cone /(ALOG10(TAN((45.0 - ABS(truelat1)/2.0) * rad_per_deg)) - &
  942. ALOG10(TAN((45.0 - ABS(truelat2)/2.0) * rad_per_deg)))
  943. ELSE
  944. cone = SIN(ABS(truelat1)*rad_per_deg )
  945. ENDIF
  946. RETURN
  947. END SUBROUTINE lc_cone
  948. SUBROUTINE ijll_lc( i, j, proj, lat, lon)
  949. ! Subroutine to convert from the (i,j) cartesian coordinate to the
  950. ! geographical latitude and longitude for a Lambert Conformal projection.
  951. ! History:
  952. ! 25 Jul 01: Corrected by B. Shaw, NOAA/FSL
  953. !
  954. IMPLICIT NONE
  955. ! Input Args
  956. REAL, INTENT(IN) :: i ! Cartesian X coordinate
  957. REAL, INTENT(IN) :: j ! Cartesian Y coordinate
  958. TYPE(proj_info),INTENT(IN) :: proj ! Projection info structure
  959. ! Output Args
  960. REAL, INTENT(OUT) :: lat ! Latitude (-90->90 deg N)
  961. REAL, INTENT(OUT) :: lon ! Longitude (-180->180 E)
  962. ! Locals
  963. REAL :: inew
  964. REAL :: jnew
  965. REAL :: r
  966. REAL :: chi,chi1,chi2
  967. REAL :: r2
  968. REAL :: xx
  969. REAL :: yy
  970. ! BEGIN CODE
  971. chi1 = (90. - proj%hemi*proj%truelat1)*rad_per_deg
  972. chi2 = (90. - proj%hemi*proj%truelat2)*rad_per_deg
  973. ! See if we are in the southern hemispere and flip the indices
  974. ! if we are.
  975. inew = proj%hemi * i
  976. jnew = proj%hemi * j
  977. ! Compute radius**2 to i/j location
  978. xx = inew - proj%polei
  979. yy = proj%polej - jnew
  980. r2 = (xx*xx + yy*yy)
  981. r = SQRT(r2)/proj%rebydx
  982. ! Convert to lat/lon
  983. IF (r2 .EQ. 0.) THEN
  984. lat = proj%hemi * 90.
  985. lon = proj%stdlon
  986. ELSE
  987. ! Longitude
  988. lon = proj%stdlon + deg_per_rad * ATAN2(proj%hemi*xx,yy)/proj%cone
  989. lon = MOD(lon+360., 360.)
  990. ! Latitude. Latitude determined by solving an equation adapted
  991. ! from:
  992. ! Maling, D.H., 1973: Coordinate Systems and Map Projections
  993. ! Equations #20 in Appendix I.
  994. IF (chi1 .EQ. chi2) THEN
  995. chi = 2.0*ATAN( ( r/TAN(chi1) )**(1./proj%cone) * TAN(chi1*0.5) )
  996. ELSE
  997. chi = 2.0*ATAN( (r*proj%cone/SIN(chi1))**(1./proj%cone) * TAN(chi1*0.5))
  998. ENDIF
  999. lat = (90.0-chi*deg_per_rad)*proj%hemi
  1000. ENDIF
  1001. IF (lon .GT. +180.) lon = lon - 360.
  1002. IF (lon .LT. -180.) lon = lon + 360.
  1003. RETURN
  1004. END SUBROUTINE ijll_lc
  1005. SUBROUTINE llij_lc( lat, lon, proj, i, j)
  1006. ! Subroutine to compute the geographical latitude and longitude values
  1007. ! to the cartesian x/y on a Lambert Conformal projection.
  1008. IMPLICIT NONE
  1009. ! Input Args
  1010. REAL, INTENT(IN) :: lat ! Latitude (-90->90 deg N)
  1011. REAL, INTENT(IN) :: lon ! Longitude (-180->180 E)
  1012. TYPE(proj_info),INTENT(IN) :: proj ! Projection info structure
  1013. ! Output Args
  1014. REAL, INTENT(OUT) :: i ! Cartesian X coordinate
  1015. REAL, INTENT(OUT) :: j ! Cartesian Y coordinate
  1016. ! Locals
  1017. REAL :: arg
  1018. REAL :: deltalon
  1019. REAL :: tl1r
  1020. REAL :: rm
  1021. REAL :: ctl1r
  1022. ! BEGIN CODE
  1023. ! Compute deltalon between known longitude and standard lon and ensure
  1024. ! it is not in the cut zone
  1025. deltalon = lon - proj%stdlon
  1026. IF (deltalon .GT. +180.) deltalon = deltalon - 360.
  1027. IF (deltalon .LT. -180.) deltalon = deltalon + 360.
  1028. ! Convert truelat1 to radian and compute COS for later use
  1029. tl1r = proj%truelat1 * rad_per_deg
  1030. ctl1r = COS(tl1r)
  1031. ! Radius to desired point
  1032. rm = proj%rebydx * ctl1r/proj%cone * &
  1033. (TAN((90.*proj%hemi-lat)*rad_per_deg/2.) / &
  1034. TAN((90.*proj%hemi-proj%truelat1)*rad_per_deg/2.))**proj%cone
  1035. arg = proj%cone*(deltalon*rad_per_deg)
  1036. i = proj%polei + proj%hemi * rm * SIN(arg)
  1037. j = proj%polej - rm * COS(arg)
  1038. ! Finally, if we are in the southern hemisphere, flip the i/j
  1039. ! values to a coordinate system where (1,1) is the SW corner
  1040. ! (what we assume) which is different than the original NCEP
  1041. ! algorithms which used the NE corner as the origin in the
  1042. ! southern hemisphere (left-hand vs. right-hand coordinate?)
  1043. i = proj%hemi * i
  1044. j = proj%hemi * j
  1045. RETURN
  1046. END SUBROUTINE llij_lc
  1047. SUBROUTINE set_merc(proj)
  1048. ! Sets up the remaining basic elements for the mercator projection
  1049. IMPLICIT NONE
  1050. TYPE(proj_info), INTENT(INOUT) :: proj
  1051. REAL :: clain
  1052. ! Preliminary variables
  1053. clain = COS(rad_per_deg*proj%truelat1)
  1054. proj%dlon = proj%dx / (proj%re_m * clain)
  1055. ! Compute distance from equator to origin, and store in the
  1056. ! proj%rsw tag.
  1057. proj%rsw = 0.
  1058. IF (proj%lat1 .NE. 0.) THEN
  1059. proj%rsw = (ALOG(TAN(0.5*((proj%lat1+90.)*rad_per_deg))))/proj%dlon
  1060. ENDIF
  1061. RETURN
  1062. END SUBROUTINE set_merc
  1063. SUBROUTINE llij_merc(lat, lon, proj, i, j)
  1064. ! Compute i/j coordinate from lat lon for mercator projection
  1065. IMPLICIT NONE
  1066. REAL, INTENT(IN) :: lat
  1067. REAL, INTENT(IN) :: lon
  1068. TYPE(proj_info),INTENT(IN) :: proj
  1069. REAL,INTENT(OUT) :: i
  1070. REAL,INTENT(OUT) :: j
  1071. REAL :: deltalon
  1072. deltalon = lon - proj%lon1
  1073. IF (deltalon .LT. -180.) deltalon = deltalon + 360.
  1074. IF (deltalon .GT. 180.) deltalon = deltalon - 360.
  1075. i = proj%knowni + (deltalon/(proj%dlon*deg_per_rad))
  1076. j = proj%knownj + (ALOG(TAN(0.5*((lat + 90.) * rad_per_deg)))) / &
  1077. proj%dlon - proj%rsw
  1078. RETURN
  1079. END SUBROUTINE llij_merc
  1080. SUBROUTINE ijll_merc(i, j, proj, lat, lon)
  1081. ! Compute the lat/lon from i/j for mercator projection
  1082. IMPLICIT NONE
  1083. REAL,INTENT(IN) :: i
  1084. REAL,INTENT(IN) :: j
  1085. TYPE(proj_info),INTENT(IN) :: proj
  1086. REAL, INTENT(OUT) :: lat
  1087. REAL, INTENT(OUT) :: lon
  1088. lat = 2.0*ATAN(EXP(proj%dlon*(proj%rsw + j-proj%knownj)))*deg_per_rad - 90.
  1089. lon = (i-proj%knowni)*proj%dlon*deg_per_rad + proj%lon1
  1090. IF (lon.GT.180.) lon = lon - 360.
  1091. IF (lon.LT.-180.) lon = lon + 360.
  1092. RETURN
  1093. END SUBROUTINE ijll_merc
  1094. SUBROUTINE llij_latlon(lat, lon, proj, i, j)
  1095. ! Compute the i/j location of a lat/lon on a LATLON grid.
  1096. IMPLICIT NONE
  1097. REAL, INTENT(IN) :: lat
  1098. REAL, INTENT(IN) :: lon
  1099. TYPE(proj_info), INTENT(IN) :: proj
  1100. REAL, INTENT(OUT) :: i
  1101. REAL, INTENT(OUT) :: j
  1102. REAL :: deltalat
  1103. REAL :: deltalon
  1104. ! Compute deltalat and deltalon as the difference between the input
  1105. ! lat/lon and the origin lat/lon
  1106. deltalat = lat - proj%lat1
  1107. deltalon = lon - proj%lon1
  1108. ! Compute i/j
  1109. i = deltalon/proj%loninc
  1110. j = deltalat/proj%latinc
  1111. i = i + proj%knowni
  1112. j = j + proj%knownj
  1113. RETURN
  1114. END SUBROUTINE llij_latlon
  1115. SUBROUTINE ijll_latlon(i, j, proj, lat, lon)
  1116. ! Compute the lat/lon location of an i/j on a LATLON grid.
  1117. IMPLICIT NONE
  1118. REAL, INTENT(IN) :: i
  1119. REAL, INTENT(IN) :: j
  1120. TYPE(proj_info), INTENT(IN) :: proj
  1121. REAL, INTENT(OUT) :: lat
  1122. REAL, INTENT(OUT) :: lon
  1123. REAL :: i_work, j_work
  1124. REAL :: deltalat
  1125. REAL :: deltalon
  1126. i_work = i - proj%knowni
  1127. j_work = j - proj%knownj
  1128. ! Compute deltalat and deltalon
  1129. deltalat = j_work*proj%latinc
  1130. deltalon = i_work*proj%loninc
  1131. lat = proj%lat1 + deltalat
  1132. lon = proj%lon1 + deltalon
  1133. RETURN
  1134. END SUBROUTINE ijll_latlon
  1135. SUBROUTINE set_cyl(proj)
  1136. implicit none
  1137. ! Arguments
  1138. type(proj_info), intent(inout) :: proj
  1139. proj%hemi = 1.0
  1140. END SUBROUTINE set_cyl
  1141. SUBROUTINE llij_cyl(lat, lon, proj, i, j)
  1142. implicit none
  1143. ! Arguments
  1144. real, intent(in) :: lat, lon
  1145. real, intent(out) :: i, j
  1146. type(proj_info), intent(in) :: proj
  1147. ! Local variables
  1148. real :: deltalat
  1149. real :: deltalon
  1150. ! Compute deltalat and deltalon as the difference between the input
  1151. ! lat/lon and the origin lat/lon
  1152. deltalat = lat - proj%lat1
  1153. ! deltalon = lon - proj%stdlon
  1154. deltalon = lon - proj%lon1
  1155. if (deltalon < 0.) deltalon = deltalon + 360.
  1156. if (deltalon > 360.) deltalon = deltalon - 360.
  1157. ! Compute i/j
  1158. i = deltalon/proj%loninc
  1159. j = deltalat/proj%latinc
  1160. if (i <= 0.) i = i + 360./proj%loninc
  1161. if (i > 360./proj%loninc) i = i - 360./proj%loninc
  1162. i = i + proj%knowni
  1163. j = j + proj%knownj
  1164. END SUBROUTINE llij_cyl
  1165. SUBROUTINE ijll_cyl(i, j, proj, lat, lon)
  1166. implicit none
  1167. ! Arguments
  1168. real, intent(in) :: i, j
  1169. real, intent(out) :: lat, lon
  1170. type(proj_info), intent(in) :: proj
  1171. ! Local variables
  1172. real :: deltalat
  1173. real :: deltalon
  1174. real :: i_work, j_work
  1175. i_work = i - proj%knowni
  1176. j_work = j - proj%knownj
  1177. if (i_work < 0.) i_work = i_work + 360./proj%loninc
  1178. if (i_work >= 360./proj%loninc) i_work = i_work - 360./proj%loninc
  1179. ! Compute deltalat and deltalon
  1180. deltalat = j_work*proj%latinc
  1181. deltalon = i_work*proj%loninc
  1182. lat = deltalat + proj%lat1
  1183. ! lon = deltalon + proj%stdlon
  1184. lon = deltalon + proj%lon1
  1185. if (lon < -180.) lon = lon + 360.
  1186. if (lon > 180.) lon = lon - 360.
  1187. END SUBROUTINE ijll_cyl
  1188. SUBROUTINE set_cassini(proj)
  1189. implicit none
  1190. ! Arguments
  1191. type(proj_info), intent(inout) :: proj
  1192. ! Local variables
  1193. real :: comp_lat, comp_lon
  1194. logical :: global_domain
  1195. proj%hemi = 1.0
  1196. ! Try to determine whether this domain has global coverage
  1197. if (abs(proj%lat1 - proj%latinc/2. + 90.) < 0.001 .and. &
  1198. abs(mod(proj%lon1 - proj%loninc/2. - proj%stdlon,360.)) < 0.001) then
  1199. global_domain = .true.
  1200. else
  1201. global_domain = .false.
  1202. end if
  1203. if (abs(proj%lat0) /= 90. .and. .not.global_domain) then
  1204. call rotate_coords(proj%lat1,proj%lon1,comp_lat,comp_lon,proj%lat0,proj%lon0,proj%stdlon,-1)
  1205. proj%lat1 = comp_lat
  1206. proj%lon1 = comp_lon
  1207. end if
  1208. END SUBROUTINE set_cassini
  1209. SUBROUTINE llij_cassini(lat, lon, proj, i, j)
  1210. implicit none
  1211. ! Arguments
  1212. real, intent(in) :: lat, lon
  1213. real, intent(out) :: i, j
  1214. type(proj_info), intent(in) :: proj
  1215. ! Local variables
  1216. real :: comp_lat, comp_lon
  1217. ! Convert geographic to computational lat/lon
  1218. if (abs(proj%lat0) /= 90.) then
  1219. call rotate_coords(lat,lon,comp_lat,comp_lon,proj%lat0,proj%lon0,proj%stdlon,-1)
  1220. else
  1221. comp_lat = lat
  1222. comp_lon = lon
  1223. end if
  1224. ! Convert computational lat/lon to i/j
  1225. call llij_cyl(comp_lat, comp_lon, proj, i, j)
  1226. END SUBROUTINE llij_cassini
  1227. SUBROUTINE ijll_cassini(i, j, proj, lat, lon)
  1228. implicit none
  1229. ! Arguments
  1230. real, intent(in) :: i, j
  1231. real, intent(out) :: lat, lon
  1232. type(proj_info), intent(in) :: proj
  1233. ! Local variables
  1234. real :: comp_lat, comp_lon
  1235. ! Convert i/j to computational lat/lon
  1236. call ijll_cyl(i, j, proj, comp_lat, comp_lon)
  1237. ! Convert computational to geographic lat/lon
  1238. if (abs(proj%lat0) /= 90.) then
  1239. call rotate_coords(comp_lat,comp_lon,lat,lon,proj%lat0,proj%lon0,proj%stdlon,1)
  1240. else
  1241. lat = comp_lat
  1242. lon = comp_lon
  1243. end if
  1244. END SUBROUTINE ijll_cassini
  1245. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  1246. ! Purpose: Converts between computational and geographic lat/lon for Cassini
  1247. !
  1248. ! Notes: This routine was provided by Bill Skamarock, 2007-03-27
  1249. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  1250. SUBROUTINE rotate_coords(ilat,ilon,olat,olon,lat_np,lon_np,lon_0,direction)
  1251. IMPLICIT NONE
  1252. REAL, INTENT(IN ) :: ilat, ilon
  1253. REAL, INTENT( OUT) :: olat, olon
  1254. REAL, INTENT(IN ) :: lat_np, lon_np, lon_0
  1255. INTEGER, INTENT(IN ), OPTIONAL :: direction
  1256. ! >=0, default : computational -> geographical
  1257. ! < 0 : geographical -> computational
  1258. REAL :: rlat, rlon
  1259. REAL :: phi_np, lam_np, lam_0, dlam
  1260. REAL :: sinphi, cosphi, coslam, sinlam
  1261. ! Convert all angles to radians
  1262. phi_np = lat_np * rad_per_deg
  1263. lam_np = lon_np * rad_per_deg
  1264. lam_0 = lon_0 * rad_per_deg
  1265. rlat = ilat * rad_per_deg
  1266. rlon = ilon * rad_per_deg
  1267. IF (PRESENT(direction) .AND. (direction < 0)) THEN
  1268. ! The equations are exactly the same except for one small difference
  1269. ! with respect to longitude ...
  1270. dlam = PI - lam_0
  1271. ELSE
  1272. dlam = lam_np
  1273. END IF
  1274. sinphi = COS(phi_np)*COS(rlat)*COS(rlon-dlam) + SIN(phi_np)*SIN(rlat)
  1275. cosphi = SQRT(1.-sinphi*sinphi)
  1276. coslam = SIN(phi_np)*COS(rlat)*COS(rlon-dlam) - COS(phi_np)*SIN(rlat)
  1277. sinlam = COS(rlat)*SIN(rlon-dlam)
  1278. IF ( cosphi /= 0. ) THEN
  1279. coslam = coslam/cosphi
  1280. sinlam = sinlam/cosphi
  1281. END IF
  1282. olat = deg_per_rad*ASIN(sinphi)
  1283. olon = deg_per_rad*(ATAN2(sinlam,coslam)-dlam-lam_0+lam_np)
  1284. ! Both of my F90 text books prefer the DO-EXIT form, and claim it is faster
  1285. ! when optimization is turned on (as we will always do...)
  1286. DO
  1287. IF (olon >= -180.) EXIT
  1288. olon = olon + 360.
  1289. END DO
  1290. DO
  1291. IF (olon <= 180.) EXIT
  1292. olon = olon - 360.
  1293. END DO
  1294. END SUBROUTINE rotate_coords
  1295. SUBROUTINE llij_rotlatlon(lat, lon, proj, i_real, j_real)
  1296. IMPLICIT NONE
  1297. ! Arguments
  1298. REAL, INTENT(IN) :: lat, lon
  1299. REAL :: i, j
  1300. REAL, INTENT(OUT) :: i_real, j_real
  1301. TYPE (proj_info), INTENT(IN) :: proj
  1302. ! Local variables
  1303. INTEGER :: ii,imt,jj,jmt,k,krows,ncol,nrow,iri
  1304. REAL(KIND=HIGH) :: dphd,dlmd !Grid increments, degrees
  1305. REAL(KIND=HIGH) :: glatd !Geographic latitude, positive north
  1306. REAL(KIND=HIGH) :: glond !Geographic longitude, positive west
  1307. REAL(KIND=HIGH) :: col,d1,d2,d2r,dlm,dlm1,dlm2,dph,glat,glon, &
  1308. pi,r2d,row,tlat,tlat1,tlat2, &
  1309. tlon,tlon1,tlon2,tph0,tlm0,x,y,z
  1310. glatd = lat
  1311. glond = -lon
  1312. dphd = proj%phi/REAL((proj%jydim-1)/2)
  1313. dlmd = proj%lambda/REAL(proj%ixdim-1)
  1314. pi = ACOS(-1.0)
  1315. d2r = pi/180.
  1316. r2d = 1./d2r
  1317. imt = 2*proj%ixdim-1
  1318. jmt = proj%jydim/2+1
  1319. glat = glatd*d2r
  1320. glon = glond*d2r
  1321. dph = dphd*d2r
  1322. dlm = dlmd*d2r
  1323. tph0 = proj%lat1*d2r
  1324. tlm0 = -proj%lon1*d2r
  1325. x = COS(tph0)*COS(glat)*COS(glon-tlm0)+SIN(tph0)*SIN(glat)
  1326. y = -COS(glat)*SIN(glon-tlm0)
  1327. z = COS(tph0)*SIN(glat)-SIN(tph0)*COS(glat)*COS(glon-tlm0)
  1328. tlat = r2d*ATAN(z/SQRT(x*x+y*y))
  1329. tlon = r2d*ATAN(y/x)
  1330. row = tlat/dphd+jmt
  1331. col = tlon/dlmd+proj%ixdim
  1332. if ( (row - INT(row)) .gt. 0.999) then
  1333. row = row + 0.0002
  1334. else if ( (col - INT(col)) .gt. 0.999) then
  1335. col = col + 0.0002
  1336. end if
  1337. nrow = INT(row)
  1338. ncol = INT(col)
  1339. ! nrow = NINT(row)
  1340. ! ncol = NINT(col)
  1341. tlat = tlat*d2r
  1342. tlon = tlon*d2r
  1343. IF (proj%stagger == HH) THEN
  1344. IF (mod(nrow,2) .eq. 0) then
  1345. i_real = col / 2.0
  1346. ELSE
  1347. i_real = col / 2.0 + 0.5
  1348. ENDIF
  1349. j_real=row
  1350. IF ((abs(MOD(nrow,2)) == 1 .AND. abs(MOD(ncol,2)) == 1) .OR. &
  1351. (MOD(nrow,2) == 0 .AND. MOD(ncol,2) == 0)) THEN
  1352. tlat1 = (nrow-jmt)*dph
  1353. tlat2 = tlat1+dph
  1354. tlon1 = (ncol-proj%ixdim)*dlm
  1355. tlon2 = tlon1+dlm
  1356. dlm1 = tlon-tlon1
  1357. dlm2 = tlon-tlon2
  1358. d1 = ACOS(COS(tlat)*COS(tlat1)*COS(dlm1)+SIN(tlat)*SIN(tlat1))
  1359. d2 = ACOS(COS(tlat)*COS(tlat2)*COS(dlm2)+SIN(tlat)*SIN(tlat2))
  1360. IF (d1 > d2) THEN
  1361. nrow = nrow+1
  1362. ncol = ncol+1
  1363. END IF
  1364. ELSE
  1365. tlat1 = (nrow+1-jmt)*dph
  1366. tlat2 = tlat1-dph
  1367. tlon1 = (ncol-proj%ixdim)*dlm
  1368. tlon2 = tlon1+dlm
  1369. dlm1 = tlon-tlon1
  1370. dlm2 = tlon-tlon2
  1371. d1 = ACOS(COS(tlat)*COS(tlat1)*COS(dlm1)+SIN(tlat)*SIN(tlat1))
  1372. d2 = ACOS(COS(tlat)*COS(tlat2)*COS(dlm2)+SIN(tlat)*SIN(tlat2))
  1373. IF (d1 < d2) THEN
  1374. nrow = nrow+1
  1375. ELSE
  1376. ncol = ncol+1
  1377. END IF
  1378. END IF
  1379. ELSE IF (proj%stagger == VV) THEN
  1380. IF (mod(nrow,2) .eq. 0) then
  1381. i_real = col / 2.0 + 0.5
  1382. ELSE
  1383. i_real = col / 2.0
  1384. ENDIF
  1385. j_real=row
  1386. IF ((MOD(nrow,2) == 0 .AND. abs(MOD(ncol,2)) == 1) .OR. &
  1387. (abs(MOD(nrow,2)) == 1 .AND. MOD(ncol,2) == 0)) THEN
  1388. tlat1 = (nrow-jmt)*dph
  1389. tlat2 = tlat1+dph
  1390. tlon1 = (ncol-proj%ixdim)*dlm
  1391. tlon2 = tlon1+dlm
  1392. dlm1 = tlon-tlon1
  1393. dlm2 = tlon-tlon2
  1394. d1 = ACOS(COS(tlat)*COS(tlat1)*COS(dlm1)+SIN(tlat)*SIN(tlat1))
  1395. d2 = ACOS(COS(tlat)*COS(tlat2)*COS(dlm2)+SIN(tlat)*SIN(tlat2))
  1396. IF (d1 > d2) THEN
  1397. nrow = nrow+1
  1398. ncol = ncol+1
  1399. END IF
  1400. ELSE
  1401. tlat1 = (nrow+1-jmt)*dph
  1402. tlat2 = tlat1-dph
  1403. tlon1 = (ncol-proj%ixdim)*dlm
  1404. tlon2 = tlon1+dlm
  1405. dlm1 = tlon-tlon1
  1406. dlm2 = tlon-tlon2
  1407. d1 = ACOS(COS(tlat)*COS(tlat1)*COS(dlm1)+SIN(tlat)*SIN(tlat1))
  1408. d2 = ACOS(COS(tlat)*COS(tlat2)*COS(dlm2)+SIN(tlat)*SIN(tlat2))
  1409. IF (d1 < d2) THEN
  1410. nrow = nrow+1
  1411. ELSE
  1412. ncol = ncol+1
  1413. END IF
  1414. END IF
  1415. END IF
  1416. !!! Added next line as a Kludge - not yet understood why needed
  1417. if (ncol .le. 0) ncol=ncol-1
  1418. jj = nrow
  1419. ii = ncol/2
  1420. IF (proj%stagger == HH) THEN
  1421. IF (abs(MOD(jj,2)) == 1) ii = ii+1
  1422. ELSE IF (proj%stagger == VV) THEN
  1423. IF (MOD(jj,2) == 0) ii=ii+1
  1424. END IF
  1425. i = REAL(ii)
  1426. j = REAL(jj)
  1427. END SUBROUTINE llij_rotlatlon
  1428. SUBROUTINE ijll_rotlatlon(i, j, proj, lat,lon)
  1429. IMPLICIT NONE
  1430. ! Arguments
  1431. REAL, INTENT(IN) :: i, j
  1432. REAL, INTENT(OUT) :: lat, lon
  1433. TYPE (proj_info), INTENT(IN) :: proj
  1434. ! Local variables
  1435. INTEGER :: ih,jh
  1436. INTEGER :: midcol,midrow,ncol,iadd1,iadd2,imt,jh2,knrow,krem,kv,nrow
  1437. REAL :: i_work, j_work
  1438. REAL :: dphd,dlmd !Grid increments, degrees
  1439. REAL(KIND=HIGH) :: arg1,arg2,d2r,fctr,glatr,glatd,glond,pi, &
  1440. r2d,tlatd,tlond,tlatr,tlonr,tlm0,tph0
  1441. REAL :: col
  1442. i_work = i
  1443. j_work = j
  1444. if ( (j - INT(j)) .gt. 0.999) then
  1445. j_work = j + 0.0002
  1446. endif
  1447. jh = INT(j_work)
  1448. dphd = proj%phi/REAL((proj%jydim-1)/2)
  1449. dlmd = proj%lambda/REAL(proj%ixdim-1)
  1450. pi = ACOS(-1.0)
  1451. d2r = pi/180.
  1452. r2d = 1./d2r
  1453. tph0 = proj%lat1*d2r
  1454. tlm0 = -proj%lon1*d2r
  1455. midrow = (proj%jydim+1)/2
  1456. midcol = proj%ixdim
  1457. col = 2*i_work-1+abs(MOD(jh+1,2))
  1458. tlatd = (j_work-midrow)*dphd
  1459. tlond = (col-midcol)*dlmd
  1460. IF (proj%stagger == VV) THEN
  1461. if (mod(jh,2) .eq. 0) then
  1462. tlond = tlond - DLMD
  1463. else
  1464. tlond = tlond + DLMD
  1465. end if
  1466. END IF
  1467. tlatr = tlatd*d2r
  1468. tlonr = tlond*d2r
  1469. arg1 = SIN(tlatr)*COS(tph0)+COS(tlatr)*SIN(tph0)*COS(tlonr)
  1470. glatr = ASIN(arg1)
  1471. glatd = glatr*r2d
  1472. arg2 = COS(tlatr)*COS(tlonr)/(COS(glatr)*COS(tph0))-TAN(glatr)*TAN(tph0)
  1473. IF (ABS(arg2) > 1.) arg2 = ABS(arg2)/arg2
  1474. fctr = 1.
  1475. IF (tlond > 0.) fctr = -1.
  1476. glond = tlm0*r2d+fctr*ACOS(arg2)*r2d
  1477. lat = glatd
  1478. lon = -glond
  1479. IF (lon .GT. +180.) lon = lon - 360.
  1480. IF (lon .LT. -180.) lon = lon + 360.
  1481. END SUBROUTINE ijll_rotlatlon
  1482. SUBROUTINE set_gauss(proj)
  1483. IMPLICIT NONE
  1484. ! Argument
  1485. type (proj_info), intent(inout) :: proj
  1486. ! Initialize the array that will hold the Gaussian latitudes.
  1487. IF ( ASSOCIATED( proj%gauss_lat ) ) THEN
  1488. DEALLOCATE ( proj%gauss_lat )
  1489. END IF
  1490. ! Get the needed space for our array.
  1491. ALLOCATE ( proj%gauss_lat(proj%nlat*2) )
  1492. ! Compute the Gaussian latitudes.
  1493. CALL gausll( proj%nlat*2 , proj%gauss_lat )
  1494. ! Now, these could be upside down from what we want, so let's check.
  1495. ! We take advantage of the equatorial symmetry to remove any sort of
  1496. ! array re-ordering.
  1497. IF ( ABS(proj%gauss_lat(1) - proj%lat1) .GT. 0.01 ) THEN
  1498. proj%gauss_lat = -1. * proj%gauss_lat
  1499. END IF
  1500. ! Just a sanity check.
  1501. IF ( ABS(proj%gauss_lat(1) - proj%lat1) .GT. 0.01 ) THEN
  1502. PRINT '(A)','Oops, something is not right with the Gaussian latitude computation.'
  1503. PRINT '(A,F8.3,A)','The input data gave the starting latitude as ',proj%lat1,'.'
  1504. PRINT '(A,F8.3,A)','This routine computed the starting latitude as +-',ABS(proj%gauss_lat(1)),'.'
  1505. PRINT '(A,F8.3,A)','The difference is larger than 0.01 degrees, which is not expected.'
  1506. CALL wrf_error_fatal ( 'Gaussian_latitude_computation' )
  1507. END IF
  1508. END SUBROUTINE set_gauss
  1509. SUBROUTINE gausll ( nlat , lat_sp )
  1510. IMPLICIT NONE
  1511. INTEGER :: nlat , i
  1512. REAL (KIND=HIGH) , PARAMETER :: pi = 3.141592653589793
  1513. REAL (KIND=HIGH) , DIMENSION(nlat) :: cosc , gwt , sinc , colat , wos2 , lat
  1514. REAL , DIMENSION(nlat) :: lat_sp
  1515. CALL lggaus(nlat, cosc, gwt, sinc, colat, wos2)
  1516. DO i = 1, nlat
  1517. lat(i) = ACOS(sinc(i)) * 180._HIGH / pi
  1518. IF (i.gt.nlat/2) lat(i) = -lat(i)
  1519. END DO
  1520. lat_sp = REAL(lat)
  1521. END SUBROUTINE gausll
  1522. SUBROUTINE lggaus( nlat, cosc, gwt, sinc, colat, wos2 )
  1523. IMPLICIT NONE
  1524. ! LGGAUS finds the Gaussian latitudes by finding the roots of the
  1525. ! ordinary Legendre polynomial of degree NLAT using Newton's
  1526. ! iteration method.
  1527. ! On entry:
  1528. integer NLAT ! the number of latitudes (degree of the polynomial)
  1529. ! On exit: for each Gaussian latitude
  1530. ! COSC - cos(colatitude) or sin(latitude)
  1531. ! GWT - the Gaussian weights
  1532. ! SINC - sin(colatitude) or cos(latitude)
  1533. ! COLAT - the colatitudes in radians
  1534. ! WOS2 - Gaussian weight over sin**2(colatitude)
  1535. REAL (KIND=HIGH) , DIMENSION(nlat) :: cosc , gwt , sinc , colat , wos2
  1536. REAL (KIND=HIGH) , PARAMETER :: pi = 3.141592653589793
  1537. ! Convergence criterion for iteration of cos latitude
  1538. REAL , PARAMETER :: xlim = 1.0E-14
  1539. INTEGER :: nzero, i, j
  1540. REAL (KIND=HIGH) :: fi, fi1, a, b, g, gm, gp, gt, delta, c, d
  1541. ! The number of zeros between pole and equator
  1542. nzero = nlat/2
  1543. ! Set first guess for cos(colat)
  1544. DO i=1,nzero
  1545. cosc(i) = SIN( (i-0.5)*pi/nlat + pi*0.5 )
  1546. END DO
  1547. ! Constants for determining the derivative of the polynomial
  1548. fi = nlat
  1549. fi1 = fi+1.0
  1550. a = fi*fi1 / SQRT(4.0*fi1*fi1-1.0)
  1551. b = fi1*fi / SQRT(4.0*fi*fi-1.0)
  1552. ! Loop over latitudes, iterating the search for each root
  1553. DO i=1,nzero
  1554. j=0
  1555. ! Determine the value of the ordinary Legendre polynomial for
  1556. ! the current guess root
  1557. DO
  1558. CALL lgord( g, cosc(i), nlat )
  1559. ! Determine the derivative of the polynomial at this point
  1560. CALL lgord( gm, cosc(i), nlat-1 )
  1561. CALL lgord( gp, cosc(i), nlat+1 )
  1562. gt = (cosc(i)*cosc(i)-1.0) / (a*gp-b*gm)
  1563. ! Update the estimate of the root
  1564. delta = g*gt
  1565. cosc(i) = cosc(i) - delta
  1566. ! If convergence criterion has not been met, keep trying
  1567. j = j+1
  1568. IF( ABS(delta).GT.xlim ) CYCLE
  1569. ! Determine the Gaussian weights
  1570. c = 2.0 *( 1.0-cosc(i)*cosc(i) )
  1571. CALL lgord( d, cosc(i), nlat-1 )
  1572. d = d*d*fi*fi
  1573. gwt(i) = c *( fi-0.5 ) / d
  1574. EXIT
  1575. END DO
  1576. END DO
  1577. ! Determine the colatitudes and sin(colat) and weights over sin**2
  1578. DO i=1,nzero
  1579. colat(i)= ACOS(cosc(i))
  1580. sinc(i) = SIN(colat(i))
  1581. wos2(i) = gwt(i) /( sinc(i)*sinc(i) )
  1582. END DO
  1583. ! If NLAT is odd, set values at the equator
  1584. IF( MOD(nlat,2) .NE. 0 ) THEN
  1585. i = nzero+1
  1586. cosc(i) = 0.0
  1587. c = 2.0
  1588. CALL lgord( d, cosc(i), nlat-1 )
  1589. d = d*d*fi*fi
  1590. gwt(i) = c *( fi-0.5 ) / d
  1591. colat(i)= pi*0.5
  1592. sinc(i) = 1.0
  1593. wos2(i) = gwt(i)
  1594. END IF
  1595. ! Determine the southern hemisphere values by symmetry
  1596. DO i=nlat-nzero+1,nlat
  1597. cosc(i) =-cosc(nlat+1-i)
  1598. gwt(i) = gwt(nlat+1-i)
  1599. colat(i)= pi-colat(nlat+1-i)
  1600. sinc(i) = sinc(nlat+1-i)
  1601. wos2(i) = wos2(nlat+1-i)
  1602. END DO
  1603. END SUBROUTINE lggaus
  1604. SUBROUTINE lgord( f, cosc, n )
  1605. IMPLICIT NONE
  1606. ! LGORD calculates the value of an ordinary Legendre polynomial at a
  1607. ! specific latitude.
  1608. ! On entry:
  1609. ! cosc - COS(colatitude)
  1610. ! n - the degree of the polynomial
  1611. ! On exit:
  1612. ! f - the value of the Legendre polynomial of degree N at
  1613. ! latitude ASIN(cosc)
  1614. REAL (KIND=HIGH) :: s1, c4, a, b, fk, f, cosc, colat, c1, fn, ang
  1615. INTEGER :: n, k
  1616. ! Determine the colatitude
  1617. colat = ACOS(cosc)
  1618. c1 = SQRT(2.0_HIGH)
  1619. DO k=1,n
  1620. c1 = c1 * SQRT( 1.0 - 1.0/(4*k*k) )
  1621. END DO
  1622. fn = n
  1623. ang= fn * colat
  1624. s1 = 0.0
  1625. c4 = 1.0
  1626. a =-1.0
  1627. b = 0.0
  1628. DO k=0,n,2
  1629. IF (k.eq.n) c4 = 0.5 * c4
  1630. s1 = s1 + c4 * COS(ang)
  1631. a = a + 2.0
  1632. b = b + 1.0
  1633. fk = k
  1634. ang= colat * (fn-fk-2.0)
  1635. c4 = ( a * (fn-b+1.0) / ( b * (fn+fn-a) ) ) * c4
  1636. END DO
  1637. f = s1 * c1
  1638. END SUBROUTINE lgord
  1639. SUBROUTINE llij_gauss (lat, lon, proj, i, j)
  1640. IMPLICIT NONE
  1641. REAL , INTENT(IN) :: lat, lon
  1642. REAL , INTENT(OUT) :: i, j
  1643. TYPE (proj_info), INTENT(IN) :: proj
  1644. INTEGER :: n , n_low
  1645. LOGICAL :: found = .FALSE.
  1646. REAL :: diff_1 , diff_nlat
  1647. ! The easy one first, get the x location. The calling routine has already made
  1648. ! sure that the necessary assumptions concerning the sign of the deltalon and the
  1649. ! relative east/west'ness of the longitude and the starting longitude are consistent
  1650. ! to allow this easy computation.
  1651. i = ( lon - proj%lon1 ) / proj%loninc + 1.
  1652. ! Since this is a global data set, we need to be concerned about wrapping the
  1653. ! fields around the globe.
  1654. ! IF ( ( proj%loninc .GT. 0 ) .AND. &
  1655. ! ( FLOOR((lon-proj%lon1)/proj%loninc) + 1 .GE. proj%ixdim ) .AND. &
  1656. ! ( lon + proj%loninc .GE. proj%lon1 + 360 ) ) THEN
  1657. !! BUG: We may need to set proj%wrap, but proj is intent(in)
  1658. !! WHAT IS THIS USED FOR?
  1659. !! proj%wrap = .TRUE.
  1660. ! i = proj%ixdim
  1661. ! ELSE IF ( ( proj%loninc .LT. 0 ) .AND. &
  1662. ! ( FLOOR((lon-proj%lon1)/proj%loninc) + 1 .GE. proj%ixdim ) .AND. &
  1663. ! ( lon + proj%loninc .LE. proj%lon1 - 360 ) ) THEN
  1664. ! ! BUG: We may need to set proj%wrap, but proj is intent(in)
  1665. ! ! WHAT IS THIS USED FOR?
  1666. ! ! proj%wrap = .TRUE.
  1667. ! i = proj%ixdim
  1668. ! END IF
  1669. ! Yet another quicky test, can we find bounding values? If not, then we may be
  1670. ! dealing with putting data to a polar projection, so just give them them maximal
  1671. ! value for the location. This is an OK assumption for the interpolation across the
  1672. ! top of the pole, given how close the longitude lines are.
  1673. IF ( ABS(lat) .GT. ABS(proj%gauss_lat(1)) ) THEN
  1674. diff_1 = lat - proj%gauss_lat(1)
  1675. diff_nlat = lat - proj%gauss_lat(proj%nlat*2)
  1676. IF ( ABS(diff_1) .LT. ABS(diff_nlat) ) THEN
  1677. j = 1
  1678. ELSE
  1679. j = proj%nlat*2
  1680. END IF
  1681. ! If the latitude is between the two bounding values, we have to search and interpolate.
  1682. ELSE
  1683. DO n = 1 , proj%nlat*2 -1
  1684. IF ( ( proj%gauss_lat(n) - lat ) * ( proj%gauss_lat(n+1) - lat ) .LE. 0 ) THEN
  1685. found = .TRUE.
  1686. n_low = n
  1687. EXIT
  1688. END IF
  1689. END DO
  1690. ! Everything still OK?
  1691. IF ( .NOT. found ) THEN
  1692. PRINT '(A)','Troubles in river city. No bounding values of latitude found in the Gaussian routines.'
  1693. CALL wrf_error_fatal ( 'Gee_no_bounding_lats_Gaussian' )
  1694. END IF
  1695. j = ( ( proj%gauss_lat(n_low) - lat ) * ( n_low + 1 ) + &
  1696. ( lat - proj%gauss_lat(n_low+1) ) * ( n_low ) ) / &
  1697. ( proj%gauss_lat(n_low) - proj%gauss_lat(n_low+1) )
  1698. END IF
  1699. END SUBROUTINE llij_gauss
  1700. END MODULE module_llxy