naqsha-0.2.0.0: Naqsha/Geometry/Internal.hs
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE CPP #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE TypeFamilies #-}
-- | The internal module that exposes the basic geometric types in
-- naqsha. This interface is subject to change and hence use with
-- caution.
module Naqsha.Geometry.Internal
( Angle(..)
, degree , minute, second
, radian
, toDegree, toRadian
, Latitude(..), Longitude(..), lat, lon
) where
import Control.Monad ( liftM )
import Data.Bits ( Bits )
import Data.Fixed
import Data.Group
import Data.Int
import Data.Monoid
import GHC.Real
import Data.Vector.Unboxed ( MVector(..), Vector, Unbox)
import qualified Data.Vector.Generic as GV
import qualified Data.Vector.Generic.Mutable as GVM
import Text.Read
----------------------------- Angles and Angular quantities -----------------------
-- | An abstract angle. Internally, angles are represented as a 64-bit
-- integer with each unit contribute 1/2^64 fraction of a complete
-- circle. This means that angles are accurate up to a resolution of 2
-- π / 2^64 radians. Angles form a group under the angular addition
-- and the fact that these are represented as integers means one can
-- expect high speed accurate angle arithmetic.
--
-- When expressing angles one can use a more convenient notation:
--
-- > myAngle = degree 21.71167
-- > yourAngle = degree 21 <> minute 42 <> second 42
--
newtype Angle = Angle {unAngle :: Int64} deriving (Enum, Eq, Ord, Unbox, Show, Read, Bits)
instance Monoid Angle where
mempty = Angle 0
mappend (Angle x) (Angle y) = Angle $ x + y
mconcat = Angle . sum . map unAngle
instance Group Angle where
invert (Angle x) = Angle $ negate x
instance Bounded Angle where
maxBound = Angle maxBound
minBound = Angle minBound
-- | Express angle in degrees.
degree :: Rational -> Angle
degree = Angle . fromInteger . round . (*scale)
where scale = (2^(64:: Int)) % 360
-- | Express angle in minutes.
minute :: Rational -> Angle
minute = degree . (*scale)
where scale = 1 % 60
-- | Express angle in seconds.
second :: Rational -> Angle
second = degree . (*scale)
where scale = 1 % 3600
-- | Express angle in radians
radian :: Double -> Angle
radian = Angle . round . (*scale)
where scale = (2^(63:: Int)) / pi
---------------------- Decimal representation of angle ----------------------------------
-- | Measure angle in degrees. This conversion may lead to loss of
-- precision.
toDegree :: Fractional r => Angle -> r
toDegree = fromRational . (*conv) . toRational . unAngle
where conv = 360 % (2^(64 :: Int))
-- | Measure angle in radians. This conversion may lead to loss of
-- precision.
toRadian :: Angle -> Double
toRadian = (*conv) . fromIntegral . unAngle
where conv = pi / (2^(63:: Int))
------------------- Making stuff suitable for unboxed vector. --------------------------
newtype instance MVector s Angle = MAngV (MVector s Int64)
newtype instance Vector Angle = AngV (Vector Int64)
instance GVM.MVector MVector Angle where
{-# INLINE basicLength #-}
{-# INLINE basicUnsafeSlice #-}
{-# INLINE basicOverlaps #-}
{-# INLINE basicUnsafeNew #-}
{-# INLINE basicUnsafeReplicate #-}
{-# INLINE basicUnsafeRead #-}
{-# INLINE basicUnsafeWrite #-}
{-# INLINE basicClear #-}
{-# INLINE basicSet #-}
{-# INLINE basicUnsafeCopy #-}
{-# INLINE basicUnsafeGrow #-}
basicLength (MAngV v) = GVM.basicLength v
basicUnsafeSlice i n (MAngV v) = MAngV $ GVM.basicUnsafeSlice i n v
basicOverlaps (MAngV v1) (MAngV v2) = GVM.basicOverlaps v1 v2
basicUnsafeRead (MAngV v) i = Angle `liftM` GVM.basicUnsafeRead v i
basicUnsafeWrite (MAngV v) i (Angle x) = GVM.basicUnsafeWrite v i x
basicClear (MAngV v) = GVM.basicClear v
basicSet (MAngV v) (Angle x) = GVM.basicSet v x
basicUnsafeNew n = MAngV `liftM` GVM.basicUnsafeNew n
basicUnsafeReplicate n (Angle x) = MAngV `liftM` GVM.basicUnsafeReplicate n x
basicUnsafeCopy (MAngV v1) (MAngV v2) = GVM.basicUnsafeCopy v1 v2
basicUnsafeGrow (MAngV v) n = MAngV `liftM` GVM.basicUnsafeGrow v n
#if MIN_VERSION_vector(0,11,0)
basicInitialize (MAngV v) = GVM.basicInitialize v
#endif
instance GV.Vector Vector Angle where
{-# INLINE basicUnsafeFreeze #-}
{-# INLINE basicUnsafeThaw #-}
{-# INLINE basicLength #-}
{-# INLINE basicUnsafeSlice #-}
{-# INLINE basicUnsafeIndexM #-}
{-# INLINE elemseq #-}
basicUnsafeFreeze (MAngV v) = AngV `liftM` GV.basicUnsafeFreeze v
basicUnsafeThaw (AngV v) = MAngV `liftM` GV.basicUnsafeThaw v
basicLength (AngV v) = GV.basicLength v
basicUnsafeSlice i n (AngV v) = AngV $ GV.basicUnsafeSlice i n v
basicUnsafeIndexM (AngV v) i = Angle `liftM` GV.basicUnsafeIndexM v i
basicUnsafeCopy (MAngV mv) (AngV v) = GV.basicUnsafeCopy mv v
elemseq _ (Angle x) = GV.elemseq (undefined :: Vector a) x
------------------------------------- Latitude and Longitude ---------------------------------
-- | The latitude of a point. Positive denotes North of Equator where
-- as negative South.
newtype Latitude = Latitude { unLat :: Angle } deriving (Eq, Ord, Bits)
instance Show Latitude where
show = show . (toDegree :: Angle -> Nano) . unLat
instance Read Latitude where
readPrec = conv <$> readPrec
where conv = lat . degree . (toRational :: Nano -> Rational)
instance Bounded Latitude where
maxBound = lat $ degree 90
minBound = lat $ degree (-90)
-- | Construct latitude out of an angle.
lat :: Angle -> Latitude
lat = Latitude . normLat
-- | normalise latitude values.
normLat :: Angle -> Angle
normLat ang | degree (-90) <= ang && ang < degree 90 = ang
| ang > degree 90 = succ (maxBound <> invert ang)
| otherwise = minBound <> invert ang
-------------------------- Longitude ------------------------------------------
-- | The longitude of a point. Positive denotes East of the Greenwich
-- meridian where as negative denotes West.
newtype Longitude = Longitude { unLong :: Angle }
deriving (Eq, Bounded, Ord, Monoid, Group, Bits)
-- | Convert angles to longitude.
lon :: Angle -> Longitude
lon = Longitude
instance Show Longitude where
show = show . (toDegree :: Angle -> Nano) . unLong
instance Read Longitude where
readPrec = conv <$> readPrec
where conv = lon . degree . (toRational :: Nano -> Rational)
--------------------------- Internal helper functions ------------------------
newtype instance MVector s Latitude = MLatV (MVector s Angle)
newtype instance Vector Latitude = LatV (Vector Angle)
newtype instance MVector s Longitude = MLongV (MVector s Angle)
newtype instance Vector Longitude = LongV (Vector Angle)
-------------------- Instance for Angle --------------------------------------------
-------------------- Instance for latitude --------------------------------------------
instance GVM.MVector MVector Latitude where
{-# INLINE basicLength #-}
{-# INLINE basicUnsafeSlice #-}
{-# INLINE basicOverlaps #-}
{-# INLINE basicUnsafeNew #-}
{-# INLINE basicUnsafeReplicate #-}
{-# INLINE basicUnsafeRead #-}
{-# INLINE basicUnsafeWrite #-}
{-# INLINE basicClear #-}
{-# INLINE basicSet #-}
{-# INLINE basicUnsafeCopy #-}
{-# INLINE basicUnsafeGrow #-}
basicLength (MLatV v) = GVM.basicLength v
basicUnsafeSlice i n (MLatV v) = MLatV $ GVM.basicUnsafeSlice i n v
basicOverlaps (MLatV v1) (MLatV v2) = GVM.basicOverlaps v1 v2
basicUnsafeRead (MLatV v) i = Latitude `liftM` GVM.basicUnsafeRead v i
basicUnsafeWrite (MLatV v) i (Latitude x) = GVM.basicUnsafeWrite v i x
basicClear (MLatV v) = GVM.basicClear v
basicSet (MLatV v) (Latitude x) = GVM.basicSet v x
basicUnsafeNew n = MLatV `liftM` GVM.basicUnsafeNew n
basicUnsafeReplicate n (Latitude x) = MLatV `liftM` GVM.basicUnsafeReplicate n x
basicUnsafeCopy (MLatV v1) (MLatV v2) = GVM.basicUnsafeCopy v1 v2
basicUnsafeGrow (MLatV v) n = MLatV `liftM` GVM.basicUnsafeGrow v n
#if MIN_VERSION_vector(0,11,0)
basicInitialize (MLatV v) = GVM.basicInitialize v
#endif
instance GV.Vector Vector Latitude where
{-# INLINE basicUnsafeFreeze #-}
{-# INLINE basicUnsafeThaw #-}
{-# INLINE basicLength #-}
{-# INLINE basicUnsafeSlice #-}
{-# INLINE basicUnsafeIndexM #-}
{-# INLINE elemseq #-}
basicUnsafeFreeze (MLatV v) = LatV `liftM` GV.basicUnsafeFreeze v
basicUnsafeThaw (LatV v) = MLatV `liftM` GV.basicUnsafeThaw v
basicLength (LatV v) = GV.basicLength v
basicUnsafeSlice i n (LatV v) = LatV $ GV.basicUnsafeSlice i n v
basicUnsafeIndexM (LatV v) i = Latitude `liftM` GV.basicUnsafeIndexM v i
basicUnsafeCopy (MLatV mv) (LatV v) = GV.basicUnsafeCopy mv v
elemseq _ (Latitude x) = GV.elemseq (undefined :: Vector a) x
-------------------------------- Instance for Longitude -----------------------------------
instance GVM.MVector MVector Longitude where
{-# INLINE basicLength #-}
{-# INLINE basicUnsafeSlice #-}
{-# INLINE basicOverlaps #-}
{-# INLINE basicUnsafeNew #-}
{-# INLINE basicUnsafeReplicate #-}
{-# INLINE basicUnsafeRead #-}
{-# INLINE basicUnsafeWrite #-}
{-# INLINE basicClear #-}
{-# INLINE basicSet #-}
{-# INLINE basicUnsafeCopy #-}
{-# INLINE basicUnsafeGrow #-}
basicLength (MLongV v) = GVM.basicLength v
basicUnsafeSlice i n (MLongV v) = MLongV $ GVM.basicUnsafeSlice i n v
basicOverlaps (MLongV v1) (MLongV v2) = GVM.basicOverlaps v1 v2
basicUnsafeRead (MLongV v) i = Longitude `liftM` GVM.basicUnsafeRead v i
basicUnsafeWrite (MLongV v) i (Longitude x) = GVM.basicUnsafeWrite v i x
basicClear (MLongV v) = GVM.basicClear v
basicSet (MLongV v) (Longitude x) = GVM.basicSet v x
basicUnsafeNew n = MLongV `liftM` GVM.basicUnsafeNew n
basicUnsafeReplicate n (Longitude x) = MLongV `liftM` GVM.basicUnsafeReplicate n x
basicUnsafeCopy (MLongV v1) (MLongV v2) = GVM.basicUnsafeCopy v1 v2
basicUnsafeGrow (MLongV v) n = MLongV `liftM` GVM.basicUnsafeGrow v n
#if MIN_VERSION_vector(0,11,0)
basicInitialize (MLongV v) = GVM.basicInitialize v
#endif
instance GV.Vector Vector Longitude where
{-# INLINE basicUnsafeFreeze #-}
{-# INLINE basicUnsafeThaw #-}
{-# INLINE basicLength #-}
{-# INLINE basicUnsafeSlice #-}
{-# INLINE basicUnsafeIndexM #-}
{-# INLINE elemseq #-}
basicUnsafeFreeze (MLongV v) = LongV `liftM` GV.basicUnsafeFreeze v
basicUnsafeThaw (LongV v) = MLongV `liftM` GV.basicUnsafeThaw v
basicLength (LongV v) = GV.basicLength v
basicUnsafeSlice i n (LongV v) = LongV $ GV.basicUnsafeSlice i n v
basicUnsafeIndexM (LongV v) i = Longitude `liftM` GV.basicUnsafeIndexM v i
basicUnsafeCopy (MLongV mv) (LongV v) = GV.basicUnsafeCopy mv v
elemseq _ (Longitude x) = GV.elemseq (undefined :: Vector a) x