packages feed

jord-2.0.0.0: test/Data/Geo/Jord/GeodeticSpec.hs

module Data.Geo.Jord.GeodeticSpec
    ( spec
    ) where

import Data.Maybe (mapMaybe)

import Test.Hspec

import qualified Data.Geo.Jord.Angle as Angle
import qualified Data.Geo.Jord.Geodetic as Geodetic
import qualified Data.Geo.Jord.Length as Length
import Data.Geo.Jord.Models (GRS80(..), Mars2000(..), S84(..), WGS84(..))

spec :: Spec
spec = do
    describe "antipode" $ do
        it "returns the antipodal position" $ do
            Geodetic.antipode (Geodetic.wgs84Pos 45 154) `shouldBe` Geodetic.wgs84Pos (-45) (-26)
            Geodetic.antipode (Geodetic.s84Pos 45 154) `shouldBe` Geodetic.s84Pos (-45) (-26)
            Geodetic.antipode' (Geodetic.latLongHeightPos 45 154 (Length.metres 15000) WGS84) `shouldBe`
                Geodetic.latLongHeightPos (-45) (-26) (Length.metres 15000) WGS84
            Geodetic.antipode' (Geodetic.latLongHeightPos 45 154 (Length.metres 15000) S84) `shouldBe`
                Geodetic.latLongHeightPos (-45) (-26) (Length.metres 15000) S84
        it "returns the south pole when called with the north pole" $ do
            Geodetic.antipode (Geodetic.northPole WGS84) `shouldBe` Geodetic.southPole WGS84
        it "returns the north pole when called with the south pole" $ do
            Geodetic.antipode (Geodetic.southPole WGS84) `shouldBe` Geodetic.northPole WGS84
    describe "poles" $ do
        it "returns 90°, 0° for the north pole" $ do
            Geodetic.latitude (Geodetic.northPole WGS84) `shouldBe` Angle.decimalDegrees 90
            Geodetic.longitude (Geodetic.northPole WGS84) `shouldBe` Angle.zero
        it "returns -90°, 0° for the south pole" $ do
            Geodetic.latitude (Geodetic.southPole WGS84) `shouldBe` Angle.decimalDegrees (-90)
            Geodetic.longitude (Geodetic.southPole WGS84) `shouldBe` Angle.zero
        it "always returns a longitude of 0° at the north pole" $ do
            let longs = (take 37 (iterate (\x -> x + 10 :: Double) (-180.0)))
            fmap (\long -> Geodetic.wgs84Pos 90 long) longs `shouldBe`
                (replicate 37 (Geodetic.northPole WGS84))
        it "always returns a longitude of 0° at the south pole" $ do
            let longs = (take 37 (iterate (\x -> x + 10 :: Double) (-180.0)))
            fmap (\long -> Geodetic.wgs84Pos (-90) long) longs `shouldBe`
                (replicate 37 (Geodetic.southPole WGS84))
    describe "wrapping latitude/longitude" $ do
        it "wraps a Earth position to [-90°, 90°] and [-180°, 180°]" $ do
            let p1 = Geodetic.s84Pos 91 54
            Geodetic.latitude p1 `shouldBe` Angle.decimalDegrees 89
            Geodetic.longitude p1 `shouldBe` Angle.decimalDegrees (-126)
            let p2 = Geodetic.s84Pos 91 (-150)
            Geodetic.latitude p2 `shouldBe` Angle.decimalDegrees 89
            Geodetic.longitude p2 `shouldBe` Angle.decimalDegrees 30
        it "wraps a Mars position longitude to [0°, 360°]" $ do
            let p = Geodetic.latLongPos 89 (-150) Mars2000
            Geodetic.latitude p `shouldBe` Angle.decimalDegrees 89
            Geodetic.longitude p `shouldBe` Angle.decimalDegrees 210
        it "wraps a Mars position to [-90°, 90°] and [0°, 360°]" $ do
            let p = Geodetic.latLongPos 91 (-150) Mars2000
            Geodetic.latitude p `shouldBe` Angle.decimalDegrees 89
            Geodetic.longitude p `shouldBe` Angle.decimalDegrees 30
    describe "wrapping n-vector" $ do
        it "wraps a n-vector Mars position to [-90°, 90°] and [0°, 360°]" $ do
            let p = Geodetic.nvectorPos (-0.8660254037844387) (-0.49999999999999994) 0 Mars2000
            Geodetic.latitude p `shouldBe` Angle.zero
            Geodetic.longitude p `shouldBe` Angle.decimalDegrees 210
    describe "Reading valid DMS text" $ do
        it "reads WGS84 horizontal positions" $ do
            let texts =
                    [ "553621N0130002E"
                    , "5536N01300E"
                    , "55N013E"
                    , "011659S0364900E"
                    , "0116S03649E"
                    , "01S036E"
                    , "473622N1221955W"
                    , "4736N12219W"
                    , "47N122W"
                    , "544807S0681811W"
                    , "5448S06818W"
                    , "54S068W"
                    , "55°36'21''N 013°00'02''E"
                    , "1°16'S,36°49'E"
                    , "47°N 122°W"
                    ]
            let positions =
                    [ Geodetic.wgs84Pos 55.60583333333334 13.000555555555556
                    , Geodetic.wgs84Pos 55.6 13.0
                    , Geodetic.wgs84Pos 55.0 13.0
                    , Geodetic.wgs84Pos (-1.2830555555555556) 36.81666666666667
                    , Geodetic.wgs84Pos (-1.2666666666666666) 36.81666666666667
                    , Geodetic.wgs84Pos (-1.0) 36.0
                    , Geodetic.wgs84Pos 47.60611111111111 (-122.33194444444445)
                    , Geodetic.wgs84Pos 47.6 (-122.31666666666666)
                    , Geodetic.wgs84Pos 47.0 (-122.0)
                    , Geodetic.wgs84Pos (-54.801944444444445) (-68.30305555555556)
                    , Geodetic.wgs84Pos (-54.8) (-68.3)
                    , Geodetic.wgs84Pos (-54.0) (-68.0)
                    , Geodetic.wgs84Pos 55.60583333333334 13.000555555555556
                    , Geodetic.wgs84Pos (-1.2666666666666666) 36.81666666666667
                    , Geodetic.wgs84Pos 47.0 (-122.0)
                    ]
            mapMaybe (`Geodetic.readHorizontalPosition` WGS84) texts `shouldBe` positions
        it "reads positions around the WGS84 ellipsoid" $ do
            let texts = ["55°36'21''N 013°00'02''E 5m", "55°36'21''N 013°00'02''E -5m"]
            let positions =
                    [ Geodetic.latLongHeightPos
                          55.60583333333334
                          13.000555555555556
                          (Length.metres 5)
                          WGS84
                    , Geodetic.latLongHeightPos
                          55.60583333333334
                          13.000555555555556
                          (Length.metres (-5))
                          WGS84
                    ]
            mapMaybe (`Geodetic.readPosition` WGS84) texts `shouldBe` positions
        it "reads positions around the S84 sphere" $ do
            let texts = ["55°36'21''N 013°00'02''E 5m", "55°36'21''N 013°00'02''E -5m"]
            let positions =
                    [ Geodetic.latLongHeightPos
                          55.60583333333334
                          13.000555555555556
                          (Length.metres 5)
                          S84
                    , Geodetic.latLongHeightPos
                          55.60583333333334
                          13.000555555555556
                          (Length.metres (-5))
                          S84
                    ]
            mapMaybe (`Geodetic.readPosition` S84) texts `shouldBe` positions
        it "reads Mars horizontal positions" $ do
            let texts = ["54S360E", "55°36'21''N 341°34'02''E"]
            let positions =
                    [ Geodetic.latLongPos (-54.0) 360 Mars2000
                    , Geodetic.latLongPos 55.60583333333334 341.5672222222222 Mars2000
                    ]
            mapMaybe (`Geodetic.readHorizontalPosition` Mars2000) texts `shouldBe` positions
    describe "Attempting to read invalid DMS text" $ do
        it "fails to read syntactically invalid positions" $ do
            let texts = ["553621K0130002E", "011659S0364900Z", "4736221221955W", "54480S0681811W"]
            mapMaybe (`Geodetic.readHorizontalPosition` WGS84) texts `shouldBe` []
        it "fails to read invalid WGS84 surface positions" $ do
            let texts = ["914807S0681811W", "804807S1811811W"]
            mapMaybe (`Geodetic.readHorizontalPosition` WGS84) texts `shouldBe` []
        it "fails to read invalid Mars surface positions" $ do
            let texts = ["914807S0681811E", "5448S06818W"]
            mapMaybe (`Geodetic.readHorizontalPosition` Mars2000) texts `shouldBe` []
    describe "Showing positions" $ do
        it "shows the N/E position formatted in DMS with symbols" $
            show (Geodetic.latLongHeightPos 55.6058333333 13.00055556 (Length.metres 5) WGS84) `shouldBe`
            "55°36'21.000\"N,13°0'2.000\"E 5.0m (WGS84)"
        it "shows the S/E position formatted in DMS with symbols" $
            show (Geodetic.latLongPos (-1.28305556) 36.81666 GRS80) `shouldBe`
            "1°16'59.000\"S,36°48'59.976\"E (GRS80)"
        it "shows the N/W position formatted in DMS with symbols" $
            show (Geodetic.latLongPos 47.60611 (-122.33194) S84) `shouldBe`
            "47°36'21.996\"N,122°19'54.984\"W (S84)"
        it "shows the S/W position formatted in DMS with symbols" $
            show (Geodetic.latLongPos (-54.80194) (-68.30305) S84) `shouldBe`
            "54°48'6.984\"S,68°18'10.980\"W (S84)"