MODULE ProjUtils
USE DefUtils
#ifdef HAVE_PROJ
USE fortranc
USE proj
#endif
IMPLICIT NONE
INTERFACE proj_inv
MODULE PROCEDURE projinv_proj4,projinv_stereo
END INTERFACE
INTERFACE proj_fwd
MODULE PROCEDURE projfwd_proj4,projfwd_stereo
END INTERFACE
LOGICAL :: PjInitialized=.FALSE.
CHARACTER(LEN=MAX_NAME_LEN) :: proj_type,proj_string
REAL(KIND=dp) :: RefLon,RefLat
REAL(KIND=dp) :: MinLon=0._dp,MinLat=0._dp,MaxLon=90._dp,MaxLat=90._dp
REAL(KIND=dp) :: xmax,ymax,xmin,ymin
#ifdef HAVE_PROJ
TYPE(pj_object) :: pj
#endif
real(kind=dp),parameter :: a=6378137.0_dp,f=1._dp/298.257223563
real(kind=dp),parameter :: e2=2._dp*f-f*f,e=sqrt(e2)
real(kind=dp),parameter :: rad2deg=180._dp/Pi
real(kind=dp),parameter :: deg2rad=Pi/180._dp
CONTAINS
SUBROUTINE xy2LonLat(x,y,Lon,Lat)
REAL(KIND=dp),INTENT(IN) :: x,y
REAL(KIND=dp),INTENT(OUT) :: lon,lat
IF (.NOT.PjInitialized) CALL ProjINIT
SELECT CASE(proj_type)
CASE('polar stereographic north')
CALL proj_inv(x,y,Lon,Lat,RefLon,RefLat)
Lon=Lon*rad2deg
Lat=Lat*rad2deg
CASE('polar stereographic south')
CALL proj_inv(-x,-y,Lon,Lat,-RefLon,-RefLat)
Lon=-Lon*rad2deg
Lat=-Lat*rad2deg
CASE('regular')
Lon=MinLon+(x-xmin)*(MaxLon-MinLon)/(xmax-xmin)
Lat=MinLat+(y-ymin)*(MaxLat-MinLat)/(ymax-ymin)
#ifdef HAVE_PROJ
CASE('proj4')
CALL proj_inv(x,y,Lon,Lat)
#endif
CASE DEFAULT
CALL FATAL('xy2LonLat','unsuported projection type: '//TRIM(proj_type))
END SELECT
END SUBROUTINE xy2LonLat
SUBROUTINE LonLat2xy(Lon,Lat,x,y)
REAL(KIND=dp),INTENT(IN) :: lon,lat
REAL(KIND=dp),INTENT(OUT) :: x,y
IF (.NOT.PjInitialized) CALL ProjINIT
SELECT CASE(proj_type)
CASE('polar stereographic north')
CALL proj_fwd(Lon*deg2rad,Lat*deg2rad,x,y,RefLon,RefLat)
CASE('polar stereographic south')
CALL proj_fwd(-Lon*deg2rad,-Lat*deg2rad,x,y,-RefLon,-RefLat)
x=-x
y=-y
#ifdef HAVE_PROJ
CASE('proj4')
CALL proj_fwd(Lon,Lat,x,y)
#endif
CASE DEFAULT
CALL FATAL('LonLat2xy','unsuported projection type: '//TRIM(proj_type))
END SELECT
END SUBROUTINE LonLat2xy
SUBROUTINE ProjINIT
TYPE(Mesh_t), POINTER :: Mesh
LOGICAL :: Parallel
LOGICAL :: GotIt
REAL(KIND=dp) :: val
PjInitialized=.TRUE.
proj_type=ListGetString(GetSimulation(),'projection type',UnFoundFatal=.True.)
SELECT CASE(proj_type)
CASE('polar stereographic north')
RefLon = ListGetConstReal(GetSimulation(),'central_meridian',UnFoundFatal=.True.)
RefLon = RefLon*deg2rad
RefLat = ListGetConstReal(GetSimulation(),'latitude_of_origin',UnFoundFatal=.True.)
RefLat = RefLat*deg2rad
CASE('polar stereographic south')
RefLon = ListGetConstReal(GetSimulation(),'central_meridian',UnFoundFatal=.True.)
RefLon=RefLon*deg2rad
RefLat = ListGetConstReal(GetSimulation(),'latitude_of_origin',UnFoundFatal=.True.)
RefLat = RefLat*deg2rad
CASE('regular')
IF(ParEnv % PEs > 1) Parallel = .TRUE.
Mesh => GetMesh()
xmax=MAXVAL(Mesh % Nodes % x )
ymax=MAXVAL(Mesh % Nodes % y )
xmin=MINVAL(Mesh % Nodes % x )
ymin=MINVAL(Mesh % Nodes % y )
IF (Parallel) THEN
xmax=ParallelReduction(xmax,2)
ymax=ParallelReduction(ymax,2)
xmin=ParallelReduction(xmin,1)
ymin=ParallelReduction(ymin,1)
END IF
val = ListGetConstReal(GetSimulation(),'Min Latitude',GotIt)
IF (GotIt) MinLat=val
val = ListGetConstReal(GetSimulation(),'Min Longitude',GotIt)
IF (GotIt) MinLon=val
val = ListGetConstReal(GetSimulation(),'Max Latitude',GotIt)
IF (GotIt) MaxLat=val
val = ListGetConstReal(GetSimulation(),'Max Longitude',GotIt)
IF (GotIt) MaxLon=val
IF ((MaxLat-MinLat).LT.0._dp) &
CALL FATAL("ProjINIT","(MaxLat-MinLat) <= 0")
IF ((MaxLon-MinLon).LT.0._dp) &
CALL FATAL("ProjINIT","(MaxLon-MinLon) <= 0")
#ifdef HAVE_PROJ
CASE('proj4')
proj_string=ListGetString(GetSimulation(),'proj4',UnFoundFatal=.True.)
pj = pj_init_plus(TRIM(proj_string)//CHAR(0))
IF (.NOT.pj_associated(pj)) CALL FATAL('ProjINIT','proj not associated')
#endif
CASE DEFAULT
CALL FATAL('ProjINIT','unsuported projection type: '//TRIM(proj_type))
END SELECT
END SUBROUTINE ProjINIT
SUBROUTINE projinv_proj4(x,y,lon,lat)
REAL(KIND=dp),INTENT(IN) :: x,y
REAL(KIND=dp),INTENT(OUT) :: lon,lat
#ifdef HAVE_PROJ
TYPE(pjuv_object) :: coordp,coordg
coordp = pjuv_object(x,y)
coordg = pj_inv(coordp, pj)
lon = coordg % u * pj_rad_to_deg
lat = coordg % v * pj_rad_to_deg
#else
CALL FATAL('projinv_proj4','proj not supported')
#endif
END SUBROUTINE projinv_proj4
SUBROUTINE projfwd_proj4(lon,lat,x,y)
REAL(KIND=dp),INTENT(IN) :: lon,lat
REAL(KIND=dp),INTENT(OUT) :: x,y
#ifdef HAVE_PROJ
TYPE(pjuv_object) :: coordp,coordg
coordg = pjuv_object(lon*pj_deg_to_rad,lat*pj_deg_to_rad)
coordp = pj_fwd(coordg, pj)
x = coordp % u
y = coordp % v
#else
CALL FATAL('proj_proj4','proj not supported')
#endif
END SUBROUTINE projfwd_proj4
SUBROUTINE projinv_stereo(x,y,lon,lat,rLon,rLat)
REAL(KIND=dp),INTENT(IN) :: x,y
REAL(KIND=dp),INTENT(IN) :: rLon,rLat
REAL(KIND=dp),INTENT(OUT) :: lon,lat
REAL(KIND=dp) :: phi_c,phi1,phi,lambda,tc,mc,rho,t,change
REAL(KIND=dp),parameter :: tol=1.0d-9
phi_c=rlat
lambda=rlon+atan2(x,-y)
tc=tan(pi/4._dp-phi_c/2._dp)/((1._dp-e*sin(phi_c))/(1._dp+e*sin(phi_c)))**(e/2._dp)
mc=cos(phi_c)/sqrt(1._dp-e2*sin(phi_c)*sin(phi_c))
rho=sqrt(x*x+y*y)
t=rho*tc/(a*mc)
phi1=pi/2._dp-2._dp*atan(t)
phi=pi/2._dp-2._dp*atan(t*((1._dp-e*sin(phi1))/(1._dp+e*sin(phi1)))**(e/2._dp))
change=2._dp*tol
do while (change.GT.tol)
phi1=phi
phi=pi/2._dp-2._dp*atan(t*((1._dp-e*sin(phi1))/(1._dp+e*sin(phi1)))**(e/2._dp))
change=abs(phi-phi1)
enddo
lat=phi
lon=lambda
END SUBROUTINE projinv_stereo
SUBROUTINE projfwd_stereo(lon,lat,x,y,rLon,rlat)
REAL(KIND=dp),INTENT(IN) :: lon,lat
REAL(KIND=dp),INTENT(IN) :: rLon,rLat
REAL(KIND=dp),INTENT(OUT) :: x,y
REAL(KIND=dp) :: mc,tc,t,r
mc=cos(rlat)/sqrt(1._dp-e2*sin(rlat)*sin(rlat))
tc=sqrt(((1._dp-sin(rlat))/(1._dp+sin(rlat)))*((1._dp+e*sin(rlat))/(1._dp-e*sin(rlat)))**e)
t=sqrt(((1._dp-sin(lat))/(1._dp+sin(lat)))*((1._dp+e*sin(lat))/(1._dp-e*sin(lat)))**e)
r=a*mc*t/tc
x=r*sin(lon-rlon)
y=-r*cos(lon-rlon)
END SUBROUTINE projfwd_stereo
END MODULE ProjUtils