Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
ElmerCSC
GitHub Repository: ElmerCSC/elmerfem
Path: blob/devel/elmerice/Utils/ProjUtils.F90
3203 views
1
!/*****************************************************************************/
2
! *
3
! * Elmer, A Finite Element Software for Multiphysical Problems
4
! *
5
! * Copyright 1st April 1995 - , CSC - IT Center for Science Ltd., Finland
6
! *
7
! * This library is free software; you can redistribute it and/or
8
! * modify it under the terms of the GNU Lesser General Public
9
! * License as published by the Free Software Foundation; either
10
! * version 2.1 of the License, or (at your option) any later version.
11
! *
12
! * This library is distributed in the hope that it will be useful,
13
! * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
! * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15
! * Lesser General Public License for more details.
16
! *
17
! * You should have received a copy of the GNU Lesser General Public
18
! * License along with this library (in file ../LGPL-2.1); if not, write
19
! * to the Free Software Foundation, Inc., 51 Franklin Street,
20
! * Fifth Floor, Boston, MA 02110-1301 USA
21
! *
22
! *****************************************************************************/
23
!
24
!/******************************************************************************
25
! *
26
! * Authors: fabien Gillet-Chaulet
27
! * Email: [email protected]
28
! * Web: http://elmerice.elmerfem.org
29
! *
30
! * Original Date: 3 May 2022
31
! *
32
! ******************************************************************************/
33
!--------------------------------------------------------------------------------
34
!> Module containing utility subroutines for geographical transformations
35
!> utility routines for fwd (LonLat => xy) and inv. (xy => LonLat) projections
36
!> Currently supported projections:
37
! > polar stereographic projections north and south
38
! > generic from proj4 definition (requires fortrangis libraries with proj support)
39
!--------------------------------------------------------------------------------
40
MODULE ProjUtils
41
USE DefUtils
42
43
#ifdef HAVE_PROJ
44
USE fortranc
45
USE proj
46
#endif
47
48
IMPLICIT NONE
49
50
INTERFACE proj_inv
51
MODULE PROCEDURE projinv_proj4,projinv_stereo
52
END INTERFACE
53
INTERFACE proj_fwd
54
MODULE PROCEDURE projfwd_proj4,projfwd_stereo
55
END INTERFACE
56
57
LOGICAL :: PjInitialized=.FALSE.
58
CHARACTER(LEN=MAX_NAME_LEN) :: proj_type,proj_string
59
REAL(KIND=dp) :: RefLon,RefLat
60
REAL(KIND=dp) :: MinLon=0._dp,MinLat=0._dp,MaxLon=90._dp,MaxLat=90._dp
61
REAL(KIND=dp) :: xmax,ymax,xmin,ymin
62
63
#ifdef HAVE_PROJ
64
TYPE(pj_object) :: pj
65
#endif
66
67
!WGS84 ellipsoid parameters : radius and flattening
68
real(kind=dp),parameter :: a=6378137.0_dp,f=1._dp/298.257223563
69
real(kind=dp),parameter :: e2=2._dp*f-f*f,e=sqrt(e2)
70
71
real(kind=dp),parameter :: rad2deg=180._dp/Pi
72
real(kind=dp),parameter :: deg2rad=Pi/180._dp
73
CONTAINS
74
75
!------------------------------------------------------------------------------
76
! generic xy2LonLat
77
!------------------------------------------------------------------------------
78
SUBROUTINE xy2LonLat(x,y,Lon,Lat)
79
REAL(KIND=dp),INTENT(IN) :: x,y
80
REAL(KIND=dp),INTENT(OUT) :: lon,lat
81
82
IF (.NOT.PjInitialized) CALL ProjINIT
83
84
SELECT CASE(proj_type)
85
CASE('polar stereographic north')
86
CALL proj_inv(x,y,Lon,Lat,RefLon,RefLat)
87
Lon=Lon*rad2deg
88
Lat=Lat*rad2deg
89
CASE('polar stereographic south')
90
CALL proj_inv(-x,-y,Lon,Lat,-RefLon,-RefLat)
91
Lon=-Lon*rad2deg
92
Lat=-Lat*rad2deg
93
94
CASE('regular')
95
Lon=MinLon+(x-xmin)*(MaxLon-MinLon)/(xmax-xmin)
96
Lat=MinLat+(y-ymin)*(MaxLat-MinLat)/(ymax-ymin)
97
98
#ifdef HAVE_PROJ
99
CASE('proj4')
100
CALL proj_inv(x,y,Lon,Lat)
101
#endif
102
CASE DEFAULT
103
CALL FATAL('xy2LonLat','unsuported projection type: '//TRIM(proj_type))
104
END SELECT
105
END SUBROUTINE xy2LonLat
106
107
!------------------------------------------------------------------------------
108
! generic LonLat (degrees) => xy (m)
109
!------------------------------------------------------------------------------
110
SUBROUTINE LonLat2xy(Lon,Lat,x,y)
111
REAL(KIND=dp),INTENT(IN) :: lon,lat
112
REAL(KIND=dp),INTENT(OUT) :: x,y
113
114
IF (.NOT.PjInitialized) CALL ProjINIT
115
116
SELECT CASE(proj_type)
117
CASE('polar stereographic north')
118
CALL proj_fwd(Lon*deg2rad,Lat*deg2rad,x,y,RefLon,RefLat)
119
CASE('polar stereographic south')
120
CALL proj_fwd(-Lon*deg2rad,-Lat*deg2rad,x,y,-RefLon,-RefLat)
121
x=-x
122
y=-y
123
#ifdef HAVE_PROJ
124
CASE('proj4')
125
CALL proj_fwd(Lon,Lat,x,y)
126
#endif
127
CASE DEFAULT
128
CALL FATAL('LonLat2xy','unsuported projection type: '//TRIM(proj_type))
129
END SELECT
130
END SUBROUTINE LonLat2xy
131
132
!------------------------------------------------------------------------------
133
! Initialise proj variables
134
!------------------------------------------------------------------------------
135
SUBROUTINE ProjINIT
136
TYPE(Mesh_t), POINTER :: Mesh
137
LOGICAL :: Parallel
138
LOGICAL :: GotIt
139
REAL(KIND=dp) :: val
140
141
PjInitialized=.TRUE.
142
143
proj_type=ListGetString(GetSimulation(),'projection type',UnFoundFatal=.True.)
144
145
SELECT CASE(proj_type)
146
CASE('polar stereographic north')
147
RefLon = ListGetConstReal(GetSimulation(),'central_meridian',UnFoundFatal=.True.)
148
RefLon = RefLon*deg2rad
149
RefLat = ListGetConstReal(GetSimulation(),'latitude_of_origin',UnFoundFatal=.True.)
150
RefLat = RefLat*deg2rad
151
CASE('polar stereographic south')
152
RefLon = ListGetConstReal(GetSimulation(),'central_meridian',UnFoundFatal=.True.)
153
RefLon=RefLon*deg2rad
154
RefLat = ListGetConstReal(GetSimulation(),'latitude_of_origin',UnFoundFatal=.True.)
155
RefLat = RefLat*deg2rad
156
157
CASE('regular')
158
IF(ParEnv % PEs > 1) Parallel = .TRUE.
159
Mesh => GetMesh()
160
xmax=MAXVAL(Mesh % Nodes % x )
161
ymax=MAXVAL(Mesh % Nodes % y )
162
xmin=MINVAL(Mesh % Nodes % x )
163
ymin=MINVAL(Mesh % Nodes % y )
164
165
IF (Parallel) THEN
166
xmax=ParallelReduction(xmax,2)
167
ymax=ParallelReduction(ymax,2)
168
xmin=ParallelReduction(xmin,1)
169
ymin=ParallelReduction(ymin,1)
170
END IF
171
172
val = ListGetConstReal(GetSimulation(),'Min Latitude',GotIt)
173
IF (GotIt) MinLat=val
174
val = ListGetConstReal(GetSimulation(),'Min Longitude',GotIt)
175
IF (GotIt) MinLon=val
176
val = ListGetConstReal(GetSimulation(),'Max Latitude',GotIt)
177
IF (GotIt) MaxLat=val
178
val = ListGetConstReal(GetSimulation(),'Max Longitude',GotIt)
179
IF (GotIt) MaxLon=val
180
181
IF ((MaxLat-MinLat).LT.0._dp) &
182
CALL FATAL("ProjINIT","(MaxLat-MinLat) <= 0")
183
IF ((MaxLon-MinLon).LT.0._dp) &
184
CALL FATAL("ProjINIT","(MaxLon-MinLon) <= 0")
185
186
#ifdef HAVE_PROJ
187
CASE('proj4')
188
proj_string=ListGetString(GetSimulation(),'proj4',UnFoundFatal=.True.)
189
pj = pj_init_plus(TRIM(proj_string)//CHAR(0))
190
IF (.NOT.pj_associated(pj)) CALL FATAL('ProjINIT','proj not associated')
191
#endif
192
193
CASE DEFAULT
194
CALL FATAL('ProjINIT','unsuported projection type: '//TRIM(proj_type))
195
196
END SELECT
197
END SUBROUTINE ProjINIT
198
199
!------------------------------------------------------------------------------
200
! proj4 : Inverse projection : x,y (m) => Lon,Lat (degrees)
201
!------------------------------------------------------------------------------
202
SUBROUTINE projinv_proj4(x,y,lon,lat)
203
REAL(KIND=dp),INTENT(IN) :: x,y
204
REAL(KIND=dp),INTENT(OUT) :: lon,lat
205
#ifdef HAVE_PROJ
206
TYPE(pjuv_object) :: coordp,coordg
207
208
coordp = pjuv_object(x,y)
209
coordg = pj_inv(coordp, pj)
210
lon = coordg % u * pj_rad_to_deg
211
lat = coordg % v * pj_rad_to_deg
212
#else
213
CALL FATAL('projinv_proj4','proj not supported')
214
#endif
215
END SUBROUTINE projinv_proj4
216
217
!------------------------------------------------------------------------------
218
! proj4 : fwd projection Lon,Lat (degrees) => x,y (m)
219
!------------------------------------------------------------------------------
220
SUBROUTINE projfwd_proj4(lon,lat,x,y)
221
REAL(KIND=dp),INTENT(IN) :: lon,lat
222
REAL(KIND=dp),INTENT(OUT) :: x,y
223
#ifdef HAVE_PROJ
224
TYPE(pjuv_object) :: coordp,coordg
225
226
coordg = pjuv_object(lon*pj_deg_to_rad,lat*pj_deg_to_rad)
227
coordp = pj_fwd(coordg, pj)
228
x = coordp % u
229
y = coordp % v
230
#else
231
CALL FATAL('proj_proj4','proj not supported')
232
#endif
233
END SUBROUTINE projfwd_proj4
234
235
236
!------------------------------------------------------------------------------
237
! north polar stereographic: Inverse projection : x,y (m) => Lon,Lat (rad)
238
! From J. P. Snyder, Map-projections - A working Manual, 1987
239
!------------------------------------------------------------------------------
240
SUBROUTINE projinv_stereo(x,y,lon,lat,rLon,rLat)
241
REAL(KIND=dp),INTENT(IN) :: x,y
242
REAL(KIND=dp),INTENT(IN) :: rLon,rLat
243
REAL(KIND=dp),INTENT(OUT) :: lon,lat
244
REAL(KIND=dp) :: phi_c,phi1,phi,lambda,tc,mc,rho,t,change
245
REAL(KIND=dp),parameter :: tol=1.0d-9
246
247
phi_c=rlat
248
249
! Eq. 20-16
250
lambda=rlon+atan2(x,-y)
251
252
tc=tan(pi/4._dp-phi_c/2._dp)/((1._dp-e*sin(phi_c))/(1._dp+e*sin(phi_c)))**(e/2._dp)
253
mc=cos(phi_c)/sqrt(1._dp-e2*sin(phi_c)*sin(phi_c))
254
rho=sqrt(x*x+y*y)
255
!Eq. 21-40
256
t=rho*tc/(a*mc)
257
258
phi1=pi/2._dp-2._dp*atan(t)
259
260
!Eq. 7-9
261
phi=pi/2._dp-2._dp*atan(t*((1._dp-e*sin(phi1))/(1._dp+e*sin(phi1)))**(e/2._dp))
262
change=2._dp*tol
263
264
do while (change.GT.tol)
265
phi1=phi
266
phi=pi/2._dp-2._dp*atan(t*((1._dp-e*sin(phi1))/(1._dp+e*sin(phi1)))**(e/2._dp))
267
change=abs(phi-phi1)
268
enddo
269
lat=phi
270
lon=lambda
271
272
END SUBROUTINE projinv_stereo
273
274
!------------------------------------------------------------------------------
275
! north polar stereographic: fwd projection Lon,Lat (rad) => x,y (m)
276
! From J. P. Snyder, Map-projections - A working Manual, 1987
277
!------------------------------------------------------------------------------
278
SUBROUTINE projfwd_stereo(lon,lat,x,y,rLon,rlat)
279
REAL(KIND=dp),INTENT(IN) :: lon,lat
280
REAL(KIND=dp),INTENT(IN) :: rLon,rLat
281
REAL(KIND=dp),INTENT(OUT) :: x,y
282
REAL(KIND=dp) :: mc,tc,t,r
283
284
mc=cos(rlat)/sqrt(1._dp-e2*sin(rlat)*sin(rlat))
285
tc=sqrt(((1._dp-sin(rlat))/(1._dp+sin(rlat)))*((1._dp+e*sin(rlat))/(1._dp-e*sin(rlat)))**e)
286
287
! Eq. 15-9a
288
t=sqrt(((1._dp-sin(lat))/(1._dp+sin(lat)))*((1._dp+e*sin(lat))/(1._dp-e*sin(lat)))**e)
289
290
! Eq. 21-34
291
r=a*mc*t/tc
292
293
! Eqs. 21-30; 21-31
294
x=r*sin(lon-rlon)
295
y=-r*cos(lon-rlon)
296
297
END SUBROUTINE projfwd_stereo
298
299
END MODULE ProjUtils
300
301
302