packages feed

postgis-trivial-0.0.1.0: src/Database/Postgis/Trivial/Traversable/Geometry.hs

{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE FlexibleContexts #-}

module Database.Postgis.Trivial.Traversable.Geometry where

import GHC.Base hiding ( foldr )
import Control.Monad ( mapM_ )
import Control.Exception ( throw )
import Control.Applicative ( (<$>) )

import Database.Postgis.Trivial.PGISConst
import Database.Postgis.Trivial.Types
import Database.Postgis.Trivial.Internal
import Database.Postgis.Trivial.Cast


-- | Point geometry
data Point p = Point SRID p

instance Castable p => Geometry (Point p) where
    putGeometry (Point srid v) = do
        putHeader srid pgisPoint (dimProps @(Cast p))
        putPointND (toPointND v::Cast p)
    getGeometry = do
        h <- getHeaderPre
        (v::Cast p, srid) <- if lookupType h==pgisPoint
            then makeResult h (skipHeader >> getPointND)
            else throw $
                GeometryError "invalid data for point geometry"
        return (Point srid (fromPointND v::p))

-- | Linestring geometry
data LineString t p = LineString SRID (t p)

instance (GeoChain t, Trans t p) => Geometry (LineString t p) where
    putGeometry (LineString srid vs) = do
        putHeader srid pgisLinestring (dimProps @(Cast p))
        putChain (transTo vs::t (Cast p))
    getGeometry = do
        h <- getHeaderPre
        (vs::t (Cast p), srid) <- if lookupType h==pgisLinestring
            then makeResult h (skipHeader >> getChain)
            else throw $
                GeometryError "invalid data for linestring geometry"
        return (LineString srid (transFrom vs::t p))

-- | Polygon geometry
data Polygon t2 t1 p = Polygon SRID (t2 (t1 p))

instance (Repl t2 (t1 (Cast p)), GeoChain t2, GeoChain t1, Trans t1 p) =>
        Geometry (Polygon t2 t1 p) where
    putGeometry (Polygon srid vss) = do
        putHeader srid pgisPolygon (dimProps @(Cast p))
        putChainLen $ count vss
        mapM_ (\vs -> putChain (transTo vs :: t1 (Cast p))) vss
    getGeometry = do
        h <- getHeaderPre
        (vss::t2 (t1 (Cast p)), srid) <- if lookupType h==pgisPolygon
            then makeResult h (skipHeader >> getChainLen >>= (`repl` getChain))
            else throw $
                GeometryError "invalid data for polygon geometry"
        return (Polygon srid (transFrom <$> vss::t2 (t1 p)))

-- | MultiPoint geometry
data MultiPoint t p = MultiPoint SRID (t p)

instance (Repl t (Cast p), GeoChain t, Trans t p) =>
        Geometry (MultiPoint t p) where
    putGeometry (MultiPoint srid vs) = do
        putHeader srid pgisMultiPoint (dimProps @(Cast p))
        putChainLen $ count vs
        mapM_ (\v -> do
            putGeometry (Point srid v :: Point p)
            ) vs
    getGeometry = do
        h <- getHeaderPre
        (vs::t (Cast p), srid) <- if lookupType h==pgisMultiPoint
            then makeResult h (
                skipHeader >> getChainLen >>= (`repl` (skipHeader >> getPointND))
                )
            else throw $
                GeometryError "invalid data for multipoint geometry"
        return (MultiPoint srid (transFrom vs::t p))

-- | MultiLineString geometry
data MultiLineString t2 t1 p = MultiLineString SRID (t2 (t1 p))

instance (Repl t2 (t1 (Cast p)), GeoChain t2, GeoChain t1, Trans t1 p) =>
        Geometry (MultiLineString t2 t1 p) where
    putGeometry (MultiLineString srid vss) = do
        putHeader srid pgisMultiLinestring (dimProps @(Cast p))
        putChainLen $ count vss
        mapM_ (\vs -> do
            putGeometry (LineString srid vs :: LineString t1 p)
            ) vss
    getGeometry = do
        h <- getHeaderPre
        (vss::t2 (t1 (Cast p)), srid) <- if lookupType h==pgisMultiLinestring
            then makeResult h (
                skipHeader >> getChainLen >>= (`repl` (skipHeader >> getChain))
                )
            else throw $
                GeometryError "invalid data for multilinestring geometry"
        return (MultiLineString srid (transFrom <$> vss::t2 (t1 p)))

-- | MultiPolygon geometry
data MultiPolygon t3 t2 t1 p = MultiPolygon SRID (t3 (t2 (t1 p)))

instance (Repl t3 (t2 (t1 (Cast p))), Repl t2 (t1 (Cast p)), GeoChain t3, GeoChain t2,
        GeoChain t1, Trans t1 p) => Geometry (MultiPolygon t3 t2 t1 p) where
    putGeometry (MultiPolygon srid vsss) = do
        putHeader srid pgisMultiPolygon (dimProps @(Cast p))
        putChainLen $ count vsss
        mapM_ (\vss -> do
            putGeometry (Polygon srid vss :: Polygon t2 t1 p)
            ) vsss
    getGeometry = do
        h <- getHeaderPre
        (vsss::t3 (t2 (t1 (Cast p))), srid) <- if lookupType h==pgisMultiPolygon
            then makeResult h (do
                skipHeader >> getChainLen >>= (`repl` (skipHeader >> getChainLen
                    >>= (`repl` getChain)))
                )
            else throw $
                GeometryError "invalid data for multipolygon geometry"
        return (MultiPolygon srid ((transFrom <$>) <$> vsss::t3 (t2 (t1 p))))

-- | Point putter
putPoint :: Castable p => SRID -> p -> Geo (Point p)
putPoint srid p = Geo (Point srid p)

-- | Point getter
getPoint :: Geo (Point p) -> (SRID, p)
getPoint (Geo (Point srid p)) = (srid, p)

-- | Linestring putter
putLS :: SRID -> t p -> Geo (LineString t p)
putLS srid ps = Geo (LineString srid ps)

-- | LineString getter
getLS :: Geo (LineString t p) -> (SRID, t p)
getLS (Geo (LineString srid vs)) = (srid, vs)

-- | Polygon putter
putPoly :: SRID -> t2 (t1 p) -> Geo (Polygon t2 t1 p)
putPoly srid pss = Geo (Polygon srid pss)

-- | Polygon getter
getPoly :: Geo (Polygon t2 t1 p) -> (SRID, t2 (t1 p))
getPoly (Geo (Polygon srid vss)) = (srid, vss)

-- | MultiPoint putter
putMPoint :: SRID -> t p -> Geo (MultiPoint t p)
putMPoint srid ps = Geo (MultiPoint srid ps)

-- | MultiPoint getter
getMPoint :: Geo (MultiPoint t p) -> (SRID, t p)
getMPoint (Geo (MultiPoint srid vs)) = (srid, vs)

-- | MultiLineString putter
putMLS :: SRID -> t2 (t1 p) -> Geo (MultiLineString t2 t1 p)
putMLS srid pss = Geo (MultiLineString srid pss)

-- | MultiLineString getter
getMLS :: Geo (MultiLineString t2 t1 p) -> (SRID, t2 (t1 p))
getMLS (Geo (MultiLineString srid vs)) = (srid, vs)

-- | MultiPolygon putter
putMPoly :: SRID -> t3 (t2 (t1 p)) -> Geo (MultiPolygon t3 t2 t1 p)
putMPoly srid psss = Geo (MultiPolygon srid psss)

-- | MultiPolygon getter
getMPoly :: Geo (MultiPolygon t3 t2 t1 p) -> (SRID, t3 (t2 (t1 p)))
getMPoly (Geo (MultiPolygon srid vs)) = (srid, vs)