module Data.Geo.Jord.Positions
(
toGeodetic
, toGeocentric
, transform
, transform'
, transformAt
, transformAt'
) where
import Data.Geo.Jord.Ellipsoid (Ellipsoid, eccentricity, equatorialRadius, isSphere, polarRadius)
import qualified Data.Geo.Jord.Geocentric as Geocentric
import qualified Data.Geo.Jord.Geodetic as Geodetic
import Data.Geo.Jord.Length (Length)
import qualified Data.Geo.Jord.Length as Length
import qualified Data.Geo.Jord.Math3d as Math3d
import Data.Geo.Jord.Model
import Data.Geo.Jord.Tx (Graph, Params, Params15, Params7)
import qualified Data.Geo.Jord.Tx as Tx
toGeodetic :: (Model m) => Geocentric.Position m -> Geodetic.Position m
toGeodetic p = Geodetic.atHeight (Geodetic.nvectorPos' nv (Geocentric.model p)) h
where
(nv, h) = nvectorFromGeocentric (Geocentric.metresCoords p) (surface . Geocentric.model $ p)
toGeocentric :: (Model m) => Geodetic.Position m -> Geocentric.Position m
toGeocentric p =
Geocentric.metresPos (Math3d.v3x c) (Math3d.v3y c) (Math3d.v3z c) (Geodetic.model' p)
where
c = nvectorToGeocentric (Geodetic.nvector p, Geodetic.height p) (surface . Geodetic.model' $ p)
transform ::
(Ellipsoidal a, Ellipsoidal b)
=> Geocentric.Position a
-> b
-> Graph Params7
-> Maybe (Geocentric.Position b)
transform p1 m2 g = transformGraph p1 m2 g id
transform' ::
(Ellipsoidal a, Ellipsoidal b)
=> Geocentric.Position a
-> b
-> Params7
-> Geocentric.Position b
transform' = transformOne
transformAt ::
(EllipsoidalT0 a, EllipsoidalT0 b)
=> Geocentric.Position a
-> Epoch
-> b
-> Graph Params15
-> Maybe (Geocentric.Position b)
transformAt p1 e m2 g = transformGraph p1 m2 g (Tx.paramsAt e)
transformAt' ::
(EllipsoidalT0 a, EllipsoidalT0 b)
=> Geocentric.Position a
-> Epoch
-> b
-> Params15
-> Geocentric.Position b
transformAt' p1 e m2 ps = transformOne p1 m2 (Tx.paramsAt e ps)
nvectorToGeocentric :: (Math3d.V3, Length) -> Ellipsoid -> Math3d.V3
nvectorToGeocentric (nv, h) e
| isSphere e = nvectorToGeocentricS (nv, h) (equatorialRadius e)
| otherwise = nvectorToGeocentricE (nv, h) e
nvectorToGeocentricS :: (Math3d.V3, Length) -> Length -> Math3d.V3
nvectorToGeocentricS (nv, h) r = Math3d.scale nv (Length.toMetres n)
where
n = Length.add h r
nvectorToGeocentricE :: (Math3d.V3, Length) -> Ellipsoid -> Math3d.V3
nvectorToGeocentricE (nv, h) e = Math3d.vec3 cx cy cz
where
nx = Math3d.v3x nv
ny = Math3d.v3y nv
nz = Math3d.v3z nv
a = Length.toMetres . equatorialRadius $ e
b = Length.toMetres . polarRadius $ e
m = (a * a) / (b * b)
n = b / sqrt ((nx * nx * m) + (ny * ny * m) + (nz * nz))
h' = Length.toMetres h
cx = n * m * nx + h' * nx
cy = n * m * ny + h' * ny
cz = n * nz + h' * nz
nvectorFromGeocentric :: Math3d.V3 -> Ellipsoid -> (Math3d.V3, Length)
nvectorFromGeocentric g e
| isSphere e = nvectorFromGeocentricS g (equatorialRadius e)
| otherwise = nvectorFromGeocentricE g e
nvectorFromGeocentricS :: Math3d.V3 -> Length -> (Math3d.V3, Length)
nvectorFromGeocentricS g r = (Math3d.unit g, h)
where
h = Length.subtract (Length.metres (Math3d.norm g)) r
nvectorFromGeocentricE :: Math3d.V3 -> Ellipsoid -> (Math3d.V3, Length)
nvectorFromGeocentricE pv e = (nvecEllipsoidal d e2 k px py pz, Length.metres h)
where
px = Math3d.v3x pv
py = Math3d.v3y pv
pz = Math3d.v3z pv
e' = eccentricity e
e2 = e' * e'
e4 = e2 * e2
a = Length.toMetres . equatorialRadius $ e
a2 = a * a
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)
nvecEllipsoidal :: Double -> Double -> Double -> Double -> Double -> Double -> Math3d.V3
nvecEllipsoidal d e2 k px py pz = Math3d.vec3 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
transformGraph ::
(Ellipsoidal a, Ellipsoidal b, Params p)
=> Geocentric.Position a
-> b
-> Graph p
-> (p -> Params7)
-> Maybe (Geocentric.Position b)
transformGraph p1 m2 g f =
case ps of
[] -> Nothing
_ -> Just (Geocentric.metresPos' v2 m2)
where
mi1 = modelId . Geocentric.model $ p1
mi2 = modelId m2
ps = Tx.paramsBetween mi1 mi2 g
v2 = foldl (\gc p -> Tx.apply gc (f p)) (Geocentric.metresCoords p1) ps
transformOne ::
(Ellipsoidal a, Ellipsoidal b)
=> Geocentric.Position a
-> b
-> Params7
-> Geocentric.Position b
transformOne p1 m2 ps = Geocentric.metresPos' v2 m2
where
v2 = Tx.apply (Geocentric.metresCoords p1) ps