packages feed

linear-geo-0.1.0.0: src/Linear/Geo/ECEF.hs

{-|
Module      : Linear.Geo.ECEF
Copyright   : Travis Whitaker 2023
License     : MIT
Maintainer  : pi.boy.travis@gmail.com
Stability   : Provisional
Portability : Portable (Windows, POSIX)

Earth-centered Earth-fixed (ECEF) coordinates.

-}

{-# LANGUAGE DataKinds
           , DeriveDataTypeable
           , DeriveGeneric
           , DerivingStrategies
           , GeneralizedNewtypeDeriving
           , TypeFamilies
           #-}

module Linear.Geo.ECEF (
    ECEF(..)
  , cross
  , triple
  ) where

import Control.DeepSeq (NFData)

import Control.Monad.Fix (MonadFix)
import Control.Monad.Zip (MonadZip)

import Data.Coerce

import Data.Data (Data)

import Data.Distributive

import qualified Data.Vector as V

import GHC.Generics (Generic)

import qualified Linear.Affine  as L
import qualified Linear.Epsilon as L
import qualified Linear.Matrix  as L
import qualified Linear.Metric  as L
import qualified Linear.V       as L
import qualified Linear.V2      as L
import qualified Linear.V3      as L
import qualified Linear.Vector  as L

-- | R3 vector with the origin at the Earth's center of mass, first basis vector
--   through the intersection of the prime meridian and the equator, and the
--   third basis vector through True North. The origin and basis vectors move
--   and rotate with the Earth through space.
newtype ECEF a = ECEF (L.V3 a)
             deriving stock ( Eq
                            , Ord
                            , Show
                            , Generic
                            , Data
                            , Bounded
                            )
             deriving newtype ( Num
                              , Fractional
                              , Floating
                              , Functor
                              , Applicative
                              , Monad
                              , MonadFix
                              , MonadZip
                              , Foldable
                              , L.Additive
                              , L.Metric
                              , L.Trace
                              , L.Epsilon
                              , NFData
                              )

instance Traversable ECEF where
    traverse f ecef = traverse f (coerce ecef)

instance Distributive ECEF where
    distribute f = ECEF $ L.V3 (fmap (\(ECEF (L.V3 x _ _)) -> x) f)
                               (fmap (\(ECEF (L.V3 _ y _)) -> y) f)
                               (fmap (\(ECEF (L.V3 _ _ z)) -> z) f)

instance L.Finite ECEF where
    type Size ECEF = 3
    toV (ECEF (L.V3 x y z)) = L.V (V.fromListN 3 [x, y, z])
    fromV (L.V v)           = ECEF $ L.V3 (v V.! 0) (v V.! 1) (v V.! 2)

instance L.R1 ECEF where
    _x f (ECEF (L.V3 x y z)) = (\x' -> ECEF (L.V3 x' y z)) <$> f x

instance L.R2 ECEF where
    _y  f (ECEF (L.V3 x y z)) = (\y' -> ECEF (L.V3 x y' z)) <$> f y
    _xy f (ECEF (L.V3 x y z)) = (\(L.V2 x' y') -> ECEF (L.V3 x' y' z))
                            <$> f (L.V2 x y)

instance L.R3 ECEF where
    _z   f (ECEF (L.V3 x y z)) = (\z' -> ECEF (L.V3 x y z')) <$> f z
    _xyz f (ECEF v)            = ECEF <$> f v

instance L.Affine ECEF where
    type Diff ECEF = L.V3
    (ECEF x) .-. (ECEF y) = x L..-. y
    (ECEF x) .+^ y        = ECEF (x L..+^ y)
    (ECEF x) .-^ y        = ECEF (x L..-^ y)

-- | Right-handed orthogonal vector with magnitude equal to the area of the
--   subtended parallelogram.
cross :: Num a => ECEF a -> ECEF a -> ECEF a
cross x y = ECEF $ L.cross (coerce x) (coerce y)

-- | Scalar triple product.
triple :: Num a => ECEF a -> ECEF a -> ECEF a -> a
triple x y z = L.triple (coerce x) (coerce y) (coerce z)