-- | -- Module: Data.Geo.Jord.Length -- Copyright: (c) 2020 Cedric Liegeois -- License: BSD3 -- Maintainer: Cedric Liegeois -- Stability: experimental -- Portability: portable -- -- Types and functions for working with (signed) lengths in metres, kilometres, nautical miles or feet. -- -- In order to use this module you should start with the following imports: -- -- @ -- import Data.Geo.Jord.Length (Length) -- import qualified Data.Geo.Jord.Length as Length -- @ -- module Data.Geo.Jord.Length ( -- * The 'Length' type Length -- * Smart constructors , feet , kilometres , metres , nauticalMiles -- * Read , length , read -- * Conversions , toFeet , toKilometres , toMetres , toMillimetres , toNauticalMiles -- * Misc , add , subtract , zero ) where import Control.Applicative ((<|>)) import Prelude hiding (length, read, subtract) import Text.ParserCombinators.ReadP (ReadP, pfail, readP_to_S, skipSpaces, string) import Text.Read (readMaybe) import Data.Geo.Jord.Parser -- | A length with a resolution of 1 micrometre. newtype Length = Length { micrometre :: Int } deriving (Eq) -- | See 'length'. instance Read Length where readsPrec _ = readP_to_S length -- | Length is shown in metres when absolute value is <= 10 km and in kilometres otherwise. instance Show Length where show l | abs' l <= kilometres 10 = show (toMetres l) ++ "m" | otherwise = show (toKilometres l) ++ "km" instance Ord Length where (<=) (Length l1) (Length l2) = l1 <= l2 -- | Adds 2 lengths. add :: Length -> Length -> Length add a b = Length (micrometre a + micrometre b) -- | Subtracts 2 lengths. subtract :: Length -> Length -> Length subtract a b = Length (micrometre a - micrometre b) -- | 0 length. zero :: Length zero = Length 0 -- | 'Length' from given amount of feet. feet :: Double -> Length feet ft = Length (round (ft * 0.3048 * m2um)) -- | 'Length' from given amount of kilometres. kilometres :: Double -> Length kilometres km = Length (round (km * 1000.0 * m2um)) -- | 'Length' from given amount of metres. metres :: Double -> Length metres m = Length (round (m * m2um)) -- | 'Length' from given amount of nautical miles. nauticalMiles :: Double -> Length nauticalMiles nm = Length (round (nm * 1852.0 * m2um)) -- | Reads a 'Length' from the given string using 'length'. read :: String -> Maybe Length read s = readMaybe s :: (Maybe Length) -- | @toFeet l@ converts @l@ to feet. toFeet :: Length -> Double toFeet (Length l) = fromIntegral l / (0.3048 * m2um) -- | @toKilometres l@ converts @l@ to kilometres. toKilometres :: Length -> Double toKilometres (Length l) = fromIntegral l / (1000.0 * m2um) -- | @toMetres l@ converts @l@ to metres. toMetres :: Length -> Double toMetres (Length l) = fromIntegral l / m2um -- | @toMillimetres l@ converts @l@ to millimetres. toMillimetres :: Length -> Double toMillimetres (Length l) = fromIntegral l / 1000.0 -- | @toNauticalMiles l@ converts @l@ to nautical miles. toNauticalMiles :: Length -> Double toNauticalMiles (Length l) = fromIntegral l / (1852.0 * m2um) -- | Parses and returns a 'Length' formatted as (-)float[m|km|nm|ft]. -- e.g. 3000m, 2.5km, -154nm or 10000ft. -- length :: ReadP Length length = do v <- number skipSpaces u <- string "m" <|> string "km" <|> string "nm" <|> string "ft" case u of "m" -> return (metres v) "km" -> return (kilometres v) "nm" -> return (nauticalMiles v) "ft" -> return (feet v) _ -> pfail -- | metre to micrometre. m2um :: Double m2um = 1000.0 * 1000.0 abs' :: Length -> Length abs' (Length um) = Length (abs um)