{-# LANGUAGE FlexibleInstances #-}
module Data.Geo.Jord.Transform
( NTransform(..)
, ETransform(..)
, nvectorToLatLong
, latLongToNVector
, ecefToNVector
, nvectorToEcef
, geodeticHeight
) where
import Data.Geo.Jord.Angle
import Data.Geo.Jord.AngularPosition
import Data.Geo.Jord.Earth
import Data.Geo.Jord.EcefPosition
import Data.Geo.Jord.LatLong
import Data.Geo.Jord.Length
import Data.Geo.Jord.NVector
import Data.Geo.Jord.Quantity
import Data.Geo.Jord.Vector3d
class NTransform a where
toNVector :: a -> AngularPosition NVector
fromNVector :: AngularPosition NVector -> a
instance NTransform NVector where
toNVector nv = AngularPosition nv zero
fromNVector = pos
instance NTransform LatLong where
toNVector ll = AngularPosition (latLongToNVector ll) zero
fromNVector = nvectorToLatLong . pos
instance NTransform (AngularPosition NVector) where
toNVector = id
fromNVector = id
instance NTransform (AngularPosition LatLong) where
toNVector (AngularPosition ll h) = AngularPosition (latLongToNVector ll) h
fromNVector (AngularPosition nv h) = AngularPosition (nvectorToLatLong nv) h
class ETransform a where
toEcef :: a -> Earth -> EcefPosition
fromEcef :: EcefPosition -> Earth -> a
instance ETransform NVector where
fromEcef p e = pos (ecefToNVector p e)
toEcef v = nvectorToEcef (nvectorHeight v zero)
instance ETransform LatLong where
fromEcef p e = fromNVector (nvectorHeight (fromEcef p e :: NVector) zero)
toEcef = toEcef . toNVector
instance ETransform (AngularPosition NVector) where
fromEcef = ecefToNVector
toEcef = nvectorToEcef
instance ETransform (AngularPosition LatLong) where
fromEcef p e = fromNVector (ecefToNVector p e)
toEcef = nvectorToEcef . toNVector
instance ETransform EcefPosition where
fromEcef p _ = p
toEcef p _ = p
nvectorToLatLong :: NVector -> LatLong
nvectorToLatLong nv = latLong lat lon
where
v = vec nv
lat = atan2' (vz v) (sqrt (vx v * vx v + vy v * vy v))
lon = atan2' (vy v) (vx v)
latLongToNVector :: LatLong -> NVector
latLongToNVector ll = nvector x' y' z'
where
lat = latitude ll
lon = longitude ll
cl = cos' lat
x' = cl * cos' lon
y' = cl * sin' lon
z' = sin' lat
ecefToNVector :: EcefPosition -> Earth -> AngularPosition NVector
ecefToNVector ep e@(Ellipsoidal el) = nvectorHeight (nvecEllipsoidal d e2 k px py pz) (metres h)
where
ev = vec ep
e' = eccentricity e
e2 = e' * e'
e4 = e2 * e2
a = toMetres (equatorialRadius el)
a2 = a * a
px = vx ev
py = vy ev
pz = vz ev
p = (px * px + py * py) / a2
q = ((1 - e2) / a2) * (pz * pz)
r = (p + q - e4) / 6.0
s = (e4 * p * q) / (4.0 * r * r * r)
t = (1.0 + s + sqrt (s * (2.0 + s))) ** (1 / 3)
u = r * (1.0 + t + 1.0 / t)
v = sqrt (u * u + q * e4)
w = e2 * (u + v - q) / (2.0 * v)
k = sqrt (u + v + w * w) - w
d = k * sqrt (px * px + py * py) / (k + e2)
h = ((k + e2 - 1.0) / k) * sqrt (d * d + pz * pz)
ecefToNVector p (Spherical r) = nvectorHeight (nvector (vx nv) (vy nv) (vz nv)) h
where
ev = vec p
nv = vunit ev
h = sub (metres (vnorm ev)) r
nvecEllipsoidal :: Double -> Double -> Double -> Double -> Double -> Double -> NVector
nvecEllipsoidal d e2 k px py pz = nvector nx' ny' nz'
where
s = 1.0 / sqrt (d * d + pz * pz)
a = k / (k + e2)
nx' = s * a * px
ny' = s * a * py
nz' = s * pz
nvectorToEcef :: AngularPosition NVector -> Earth -> EcefPosition
nvectorToEcef (AngularPosition nv h) e@(Ellipsoidal el) = ecef ex' ey' ez'
where
v = vec nv
uv = vunit v
a = toMetres (equatorialRadius el)
b = toMetres (polarRadius e)
nx' = vx uv
ny' = vy uv
nz' = vz uv
m = (a * a) / (b * b)
n = b / sqrt ((nx' * nx' * m) + (ny' * ny' * m) + (nz' * nz'))
h' = toMetres h
ex' = metres (n * m * nx' + h' * nx')
ey' = metres (n * m * ny' + h' * ny')
ez' = metres (n * nz' + h' * nz')
nvectorToEcef (AngularPosition nv h) (Spherical r) = ecefMetres (vx ev) (vy ev) (vz ev)
where
unv = vunit . vec $ nv
n = add h r
ev = vscale unv (toMetres n)
geodeticHeight :: EcefPosition -> Earth -> Length
geodeticHeight p e = height (ecefToNVector p e)