module Data.Geo.Jord.GeoPos
(
GeoPos(latitude, longitude)
, latLong
, latLongE
, latLongF
, latLongDecimal
, latLongDecimalE
, latLongDecimalF
, readGeoPos
, readGeoPosE
, readGeoPosF
, toDecimalDegrees'
) where
import Control.Applicative hiding (many)
import Control.Monad.Fail
import Data.Char
import Data.Geo.Jord.Angle
import Data.Geo.Jord.Parse
import Data.Maybe
import Prelude hiding (fail)
import Text.ParserCombinators.ReadP
import Text.Read hiding (pfail)
data GeoPos = GeoPos
{ latitude :: Angle
, longitude :: Angle
} deriving (Eq)
instance Read GeoPos where
readsPrec _ = readP_to_S geo
instance Show GeoPos where
show (GeoPos lat lon) = showLat lat ++ "," ++ showLon lon
latLong :: Angle -> Angle -> GeoPos
latLong lat lon =
fromMaybe
(error ("Invalid latitude=" ++ show lat ++ " or longitude=" ++ show lon))
(latLongF lat lon)
latLongE :: Angle -> Angle -> Either String GeoPos
latLongE lat lon
| not (isWithin lat (decimalDegrees (-90)) (decimalDegrees 90)) =
Left ("Invalid latitude=" ++ show lat)
| not (isWithin lon (decimalDegrees (-180)) (decimalDegrees 180)) =
Left ("Invalid longitude=" ++ show lon)
| otherwise = Right (GeoPos lat lon)
latLongF :: (MonadFail m) => Angle -> Angle -> m GeoPos
latLongF lat lon =
case e of
Left err -> fail err
Right g -> return g
where
e = latLongE lat lon
latLongDecimal :: Double -> Double -> GeoPos
latLongDecimal lat lon = latLong (decimalDegrees lat) (decimalDegrees lon)
latLongDecimalE :: Double -> Double -> Either String GeoPos
latLongDecimalE lat lon = latLongE (decimalDegrees lat) (decimalDegrees lon)
latLongDecimalF :: (MonadFail m) => Double -> Double -> m GeoPos
latLongDecimalF lat lon = latLongF (decimalDegrees lat) (decimalDegrees lon)
readGeoPos :: String -> GeoPos
readGeoPos s = read s :: GeoPos
readGeoPosE :: String -> Either String GeoPos
readGeoPosE s =
case readMaybe s of
Nothing -> Left ("couldn't read geo pos " ++ s)
Just g -> Right g
readGeoPosF :: (MonadFail m) => String -> m GeoPos
readGeoPosF s =
let pg = readGeoPosE s
in case pg of
Left e -> fail e
Right g -> return g
toDecimalDegrees' :: GeoPos -> (Double, Double)
toDecimalDegrees' g = (toDecimalDegrees (latitude g), toDecimalDegrees (longitude g))
geo :: ReadP GeoPos
geo = block <|> human
block :: ReadP GeoPos
block = do
lat <- blat
lon <- blon
latLongF lat lon
blat :: ReadP Angle
blat = do
d' <- digits 2
(m', s') <- option (0, 0) (ms <|> m)
h <- hemisphere
if h == 'N'
then dmsF d' m' s' 0
else dmsF (-d') m' s' 0
blon :: ReadP Angle
blon = do
d' <- digits 3
(m', s') <- option (0, 0) (ms <|> m)
m'' <- meridian
if m'' == 'E'
then dmsF d' m' s' 0
else dmsF (-d') m' s' 0
hemisphere :: ReadP Char
hemisphere = char 'N' <|> char 'S'
meridian :: ReadP Char
meridian = char 'E' <|> char 'W'
ms :: ReadP (Int, Int)
ms = do
m' <- digits 2
s' <- digits 2
return (m', s')
m :: ReadP (Int, Int)
m = do
m' <- digits 2
return (m', 0)
human :: ReadP GeoPos
human = do
lat <- hlat
_ <- char ' ' <|> char ','
lon <- hlon
latLongF lat lon
hlat :: ReadP Angle
hlat = do
lat <- angle
h <- hemisphere
if h == 'N'
then return lat
else return (negate' lat)
hlon :: ReadP Angle
hlon = do
lon <- angle
m' <- meridian
if m' == 'E'
then return lon
else return (negate' lon)
showLat :: Angle -> String
showLat lat
| isNegative lat = show (negate' lat) ++ "S"
| otherwise = show lat ++ "N"
showLon :: Angle -> String
showLon lon
| isNegative lon = show (negate' lon) ++ "W"
| otherwise = show lon ++ "E"