packages feed

jord-1.0.0.0: test/Data/Geo/Jord/GeodesicSpec.hs

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

import Test.Hspec

import Data.Geo.Jord.Geodesic
import Data.Geo.Jord.Position

-- | See Geodesy Test Harness - latlon-ellipsoidal-vincenty  by Chris Veness - TODO link
spec :: Spec
spec = do
    describe "Geodesic for (near) antipodal positions" $ do
        it "handles near-antipodal positions" $
            surfaceDistance (latLongPos 0 0 WGS84) (latLongPos 0.5 179.5 WGS84) `shouldBe`
            Just (kilometres 19936.288578981)
        it "returns Nothing if vincenty fails to converge - inverseGeodesic" $
            inverseGeodesic (latLongPos 0 0 WGS84) (latLongPos 0.5 179.7 WGS84) `shouldBe` Nothing
        it "returns Nothing if vincenty fails to converge - surfaceDistance" $
            surfaceDistance (latLongPos 0 0 WGS84) (latLongPos 0.5 179.7 WGS84) `shouldBe` Nothing
        it "returns Nothing if vincenty fails to converge - initialBearing" $
            initialBearing (latLongPos 0 0 WGS84) (latLongPos 0.5 179.7 WGS84) `shouldBe` Nothing
        it "returns Nothing if vincenty fails to converge - finalBearing" $
            finalBearing (latLongPos 0 0 WGS84) (latLongPos 0.5 179.7 WGS84) `shouldBe` Nothing
        it "handle antipodal positions - surfaceDistance at equator" $
            surfaceDistance (latLongPos 0 0 WGS84) (latLongPos 0 180 WGS84) `shouldBe` Just (kilometres 20003.931458623)
        it "handle antipodal positions - initialBearing at equator" $
            initialBearing (latLongPos 0 0 WGS84) (latLongPos 0 180 WGS84) `shouldBe` Just zero
        it "handle antipodal positions - surfaceDistance between poles" $
            surfaceDistance (northPole WGS84) (southPole WGS84) `shouldBe` Just (kilometres 20003.931458623)
        it "handle antipodal positions - initialBearing between poles" $
            initialBearing (northPole WGS84) (southPole WGS84) `shouldBe` Just zero
    describe "Geodesic for coincident positions" $ do
        let p = wgs84Pos 48 6 zero
        it "returns a distance of 0 and no bearing" $ do
            let i = inverseGeodesic p p
            let ib = i >>= geodesicBearing1
            let fb = i >>= geodesicBearing2
            fmap geodesicLength i `shouldBe` Just zero
            ib `shouldBe` Nothing
            fb `shouldBe` Nothing
            surfaceDistance p p `shouldBe` Just zero
            initialBearing p p `shouldBe` Nothing
            finalBearing p p `shouldBe` Nothing
        it "returns the given position when distance is 0" $ destination p (decimalDegrees 54) zero `shouldBe` Just p
    describe "Geodesic for selected positions" $ do
        let flindersPeak = latLongPos (-37.95103341666667) 144.42486788888888 WGS84
        let buninyong = latLongPos (-37.65282113888889) 143.92649552777777 WGS84
        let le = latLongPos 50.06632 (-5.71475) WGS84
        let jog = latLongPos 58.64402 (-3.07009) WGS84
        it "computes the surface distance - inverse" $ do
            surfaceDistance flindersPeak buninyong `shouldBe` Just (metres 54972.271139)
            surfaceDistance le jog `shouldBe` Just (kilometres 969.954166314)
        it "computes the initial bearing - inverse" $ do
            initialBearing flindersPeak buninyong `shouldBe` Just (decimalDegrees 306.86815920333333)
            initialBearing le jog `shouldBe` Just (decimalDegrees 9.14187748888889)
        it "computes the final bearing - inverse" $ do
            finalBearing flindersPeak buninyong `shouldBe` Just (decimalDegrees 307.17363062944446)
            finalBearing le jog `shouldBe` Just (decimalDegrees 11.297220414166667)
        it "compute the destination - direct" $ do
            destination flindersPeak (decimalDegrees 306.86815920333333) (metres 54972.271139) `shouldBe` Just buninyong
            destination le (decimalDegrees 9.14187748888889) (kilometres 969.954166314) `shouldBe` Just jog
        it "computes the final bearing - direct" $ do
            let fb1 =
                    directGeodesic flindersPeak (decimalDegrees 306.86815920333333) (metres 54972.271139) >>=
                    geodesicBearing2
            fb1 `shouldBe` Just (decimalDegrees 307.17363062944446)
            let fb2 =
                    directGeodesic le (decimalDegrees 9.14187748888889) (kilometres 969.954166314) >>= geodesicBearing2
            fb2 `shouldBe` Just (decimalDegrees 11.297220414166667)
    describe "Surface distance for anti-meridian positions" $
        it "handles positions crossing antimeridian" $
        surfaceDistance (latLongPos 30 120 WGS84) (latLongPos 30 (-120) WGS84) `shouldBe`
        Just (kilometres 10825.924088908)
    describe "Geodesic for quadrants" $
        it "returns the same surface distance in all quadrants" $ do
            let actuals =
                    [ surfaceDistance (latLongPos 30 30 WGS84) (latLongPos 60 60 WGS84)
                    , surfaceDistance (latLongPos 60 60 WGS84) (latLongPos 30 30 WGS84)
                    , surfaceDistance (latLongPos 30 60 WGS84) (latLongPos 60 30 WGS84)
                    , surfaceDistance (latLongPos 60 30 WGS84) (latLongPos 30 60 WGS84)
                    , surfaceDistance (latLongPos 30 (-30) WGS84) (latLongPos 60 (-60) WGS84)
                    , surfaceDistance (latLongPos 60 (-60) WGS84) (latLongPos 30 (-30) WGS84)
                    , surfaceDistance (latLongPos 30 (-60) WGS84) (latLongPos 60 (-30) WGS84)
                    , surfaceDistance (latLongPos 60 (-30) WGS84) (latLongPos 30 (-60) WGS84)
                    , surfaceDistance (latLongPos (-30) (-30) WGS84) (latLongPos (-60) (-60) WGS84)
                    , surfaceDistance (latLongPos (-60) (-60) WGS84) (latLongPos (-30) (-30) WGS84)
                    , surfaceDistance (latLongPos (-30) (-60) WGS84) (latLongPos (-60) (-30) WGS84)
                    , surfaceDistance (latLongPos (-60) (-30) WGS84) (latLongPos (-30) (-60) WGS84)
                    , surfaceDistance (latLongPos (-30) 30 WGS84) (latLongPos (-60) 60 WGS84)
                    , surfaceDistance (latLongPos (-60) 60 WGS84) (latLongPos (-30) 30 WGS84)
                    , surfaceDistance (latLongPos (-30) 60 WGS84) (latLongPos (-60) 30 WGS84)
                    , surfaceDistance (latLongPos (-60) 30 WGS84) (latLongPos (-30) 60 WGS84)
                    ]
            let expecteds = replicate (length actuals) (Just (kilometres 4015.703020938))
            actuals `shouldBe` expecteds
    describe "Inverse geodesic non-convergence" $ do
        it "returns Nothing for antipodal λ > π" $
            surfaceDistance (latLongPos 0 0 WGS84) (latLongPos 0.5 179.7 WGS84) `shouldBe` Nothing
        it "returns Nothing for antipodal convergence" $
            surfaceDistance (latLongPos 5 0 WGS84) (latLongPos (-5.1) 179.4 WGS84) `shouldBe` Nothing