diff --git a/ChangeLog.md b/ChangeLog.md
new file mode 100644
--- /dev/null
+++ b/ChangeLog.md
@@ -0,0 +1,3 @@
+# Changelog
+
+## Unreleased changes
diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,28 @@
+BSD 3-Clause License
+
+Copyright (c) 2024, Igor Chudaev
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+   list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice,
+   this list of conditions and the following disclaimer in the documentation
+   and/or other materials provided with the distribution.
+
+3. Neither the name of the copyright holder nor the names of its
+   contributors may be used to endorse or promote products derived from
+   this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/README.md b/README.md
new file mode 100644
--- /dev/null
+++ b/README.md
@@ -0,0 +1,63 @@
+# postgis-trivial
+
+Haskell Postgresql/PostGIS DB Driver
+
+This library provides methods which allow direct use of user-defined Haskell data with Postgresql/PostGIS databases. It is based on [postgresql-simple](https://hackage.haskell.org/package/postgresql-simple) and can be used with other postgresql-simple capabilities.
+
+The main interface module `Database.PostGIS.Trivial` allows PostGIS to work with geospatial (point) data enclosed in `Traversable` data structures. If the most inner data structures are `Unboxed` vectors, then use the functions and types defined in `Database.PostGIS.Trivial.Unboxed`. And, if the most inner data structures are `Storable` vectors then use `Database.PostGIS.Trivial.Storable` module.
+
+## Synopsis
+
+### Default types
+
+- P2D for 2D points
+
+- P3DZ, P3DM for 3D points
+
+- P4D for points with z and m coordinates
+
+You can freely use them for your data. Unboxed and Storable types are in the corresponding modules (thay have appending 'U' and 'S' letters in names).
+
+### User-defined types
+
+Ensure that user geometry data points is correctly translated into the internal default points data as in the example.
+
+```haskell
+{-# LANGUAGE TypeFamilies #-}
+
+data LatLon =       -- example of 2D geospatial point data
+        LatLon !Double !Double
+        deriving (Show, Eq)
+
+type instance Cast LatLon = P2D     -- translation to inner 2D point
+
+instance Castable LatLon where      -- specify translation
+    toPointND (LatLon y x) = Point2D x y
+    fromPointND (Point2D x y) = LatLon y x
+```
+
+Then a structure of type `Traversable t => t LatLon` can be interpreted as `LineString` or `MultiPoint`. Any structure of type
+`(Traversable t1, Traversable t2) => t2 (t1 LatLon)` can be interpreted as `Polygon` or `MultiLineString`. And any structure of type
+`(Traversable t1, Traversable t2, Traversable t3) => t3 (t2 (t1 LatLon))` can be interpreted as `MultiPolygon`. Currently, only following `Traversable`s are supported: `List`, `Data.Vector.Vector`. `Data.IntMap.IntMap` and `Data.Map.Map` have partial support.
+
+By this way you can use these structures in postgresql-simple functions as such:
+
+```haskell
+    ls = [LatLon 1 2, LatLon 1.5 2.5, LatLon 2.5 3, LatLon 1 2]
+    _ <- execute conn "INSERT INTO linestrings (geom) VALUES (?)"
+        (Only (Geo (LineString srid ls)))
+    [Only res] <- query_ conn "SELECT * FROM linestrings LIMIT 1"
+    let Geo (LineString srid' ls') = res
+```
+
+Or the same with suitable helper functions:
+
+```haskell
+    _ <- execute conn "INSERT INTO linestrings (geom) VALUES (?)"
+        (Only (putLS srid ls))
+    [Only res] <- query_ conn "SELECT * FROM linestrings LIMIT 1"
+    let (srid', ls') = getLS res
+```
+
+
+
diff --git a/Setup.hs b/Setup.hs
new file mode 100644
--- /dev/null
+++ b/Setup.hs
@@ -0,0 +1,2 @@
+import Distribution.Simple
+main = defaultMain
diff --git a/include/pgisConst.h b/include/pgisConst.h
new file mode 100644
--- /dev/null
+++ b/include/pgisConst.h
@@ -0,0 +1,35 @@
+/* -----------------------------------------------------------------------------
+ *
+ * LWTYPE numbers, used internally by PostGIS
+ *
+ * ---------------------------------------------------------------------------*/
+
+#define POINTTYPE                1
+#define LINETYPE                 2
+#define POLYGONTYPE              3
+#define MULTIPOINTTYPE           4
+#define MULTILINETYPE            5
+#define MULTIPOLYGONTYPE         6
+#define COLLECTIONTYPE           7
+#define CIRCSTRINGTYPE           8
+#define COMPOUNDTYPE             9
+#define CURVEPOLYTYPE           10
+#define MULTICURVETYPE          11
+#define MULTISURFACETYPE        12
+#define POLYHEDRALSURFACETYPE   13
+#define TRIANGLETYPE            14
+#define TINTYPE                 15
+#define NUMTYPES                16
+
+/* -----------------------------------------------------------------------------
+ *
+ * Flags applied in EWKB to indicate Z/M dimensions and
+ * presence/absence of SRID and bounding boxes
+ *
+ * ---------------------------------------------------------------------------*/
+
+#define WKBZOFFSET  0x80000000
+#define WKBMOFFSET  0x40000000
+#define WKBSRIDFLAG 0x20000000
+#define WKBBBOXFLAG 0x10000000
+
diff --git a/package.yaml b/package.yaml
new file mode 100644
--- /dev/null
+++ b/package.yaml
@@ -0,0 +1,92 @@
+name:       postgis-trivial
+version:    0.0.1.0
+github:     "igor720/postgis-trivial"
+license:    BSD3
+author:     "Igor Chudaev"
+maintainer: "igor720@gmail.com"
+
+extra-source-files:
+- README.md
+- ChangeLog.md
+- package.yaml
+- include/pgisConst.h
+
+# Metadata used when publishing your package
+synopsis:   PostGIS extention driver based on postgresql-simple package
+category:   Database driver
+
+# To avoid duplicated efforts in documentation and dealing with the
+# complications of embedding Haddock markup inside cabal files, it is
+# common to point users to the README.md file.
+description: Please see the README on GitHub at <https://github.com/igor720/postgis-trivial#README.md>
+
+dependencies:
+  - base >= 4.7 && < 5
+  - binary >= 0.7 && < 0.9
+  - bytestring > 0.9 && < 0.12
+  - bytestring-lexing >= 0.4 && < 0.6
+  - containers >= 0.6 && < 0.7
+  - cpu >= 0.1 && < 0.2
+  - data-binary-ieee754 >= 0.4 && < 0.5
+  - mtl >= 2.1 && < 2.4
+  - postgresql-simple >= 0.6 && < 0.8
+  - storable-record >= 0.0 && < 0.1
+  - vector >= 0.12 && < 0.14
+
+library:
+  exposed-modules:
+    - Database.Postgis.Trivial
+    - Database.Postgis.Trivial.Types
+    - Database.Postgis.Trivial.Cast
+    - Database.Postgis.Trivial.Traversable.PointND
+    - Database.Postgis.Trivial.Traversable.Geometry
+    - Database.Postgis.Trivial.Unboxed
+    - Database.Postgis.Trivial.Unboxed.PointND
+    - Database.Postgis.Trivial.Unboxed.Geometry
+    - Database.Postgis.Trivial.Storable
+    - Database.Postgis.Trivial.Storable.PointND
+    - Database.Postgis.Trivial.Storable.Geometry
+  other-modules:
+    - Database.Postgis.Trivial.PGISConst
+    - Database.Postgis.Trivial.Internal
+  source-dirs:
+    - src
+  default-extensions: NoImplicitPrelude
+  ghc-options:
+    - -Wall
+  include-dirs:
+    - include
+  install-includes:
+    - pgisConst.h
+
+### See Spec.hs for instructions of testing on running PostGIS.
+tests:
+  job-tests:
+    main: Spec.hs
+    other-modules:
+    - Database.Postgis.Trivial
+    - Database.Postgis.Trivial.Types
+    - Database.Postgis.Trivial.Cast
+    - Database.Postgis.Trivial.PGISConst
+    - Database.Postgis.Trivial.Internal
+    - Database.Postgis.Trivial.Traversable.PointND
+    - Database.Postgis.Trivial.Traversable.Geometry
+    - Database.Postgis.Trivial.Unboxed
+    - Database.Postgis.Trivial.Unboxed.PointND
+    - Database.Postgis.Trivial.Unboxed.Geometry
+    - Database.Postgis.Trivial.Storable
+    - Database.Postgis.Trivial.Storable.PointND
+    - Database.Postgis.Trivial.Storable.Geometry
+    - Tests.Traversable
+    - Tests.Unboxed
+    - Tests.Storable
+    source-dirs:
+    - src
+    - test
+    default-extensions:
+    - NoImplicitPrelude
+    ghc-options:
+    - -Wall
+    dependencies:
+    - postgis-trivial
+    - HUnit >= 1.6 && < 1.7
diff --git a/postgis-trivial.cabal b/postgis-trivial.cabal
new file mode 100644
--- /dev/null
+++ b/postgis-trivial.cabal
@@ -0,0 +1,108 @@
+cabal-version: 1.12
+
+-- This file has been generated from package.yaml by hpack version 0.36.0.
+--
+-- see: https://github.com/sol/hpack
+
+name:           postgis-trivial
+version:        0.0.1.0
+synopsis:       PostGIS extention driver based on postgresql-simple package
+description:    Please see the README on GitHub at <https://github.com/igor720/postgis-trivial#README.md>
+category:       Database driver
+homepage:       https://github.com/igor720/postgis-trivial#readme
+bug-reports:    https://github.com/igor720/postgis-trivial/issues
+author:         Igor Chudaev
+maintainer:     igor720@gmail.com
+license:        BSD3
+license-file:   LICENSE
+build-type:     Simple
+extra-source-files:
+    README.md
+    ChangeLog.md
+    package.yaml
+    include/pgisConst.h
+
+source-repository head
+  type: git
+  location: https://github.com/igor720/postgis-trivial
+
+library
+  exposed-modules:
+      Database.Postgis.Trivial
+      Database.Postgis.Trivial.Types
+      Database.Postgis.Trivial.Cast
+      Database.Postgis.Trivial.Traversable.PointND
+      Database.Postgis.Trivial.Traversable.Geometry
+      Database.Postgis.Trivial.Unboxed
+      Database.Postgis.Trivial.Unboxed.PointND
+      Database.Postgis.Trivial.Unboxed.Geometry
+      Database.Postgis.Trivial.Storable
+      Database.Postgis.Trivial.Storable.PointND
+      Database.Postgis.Trivial.Storable.Geometry
+  other-modules:
+      Database.Postgis.Trivial.PGISConst
+      Database.Postgis.Trivial.Internal
+  hs-source-dirs:
+      src
+  default-extensions:
+      NoImplicitPrelude
+  ghc-options: -Wall
+  include-dirs:
+      include
+  install-includes:
+      pgisConst.h
+  build-depends:
+      base >=4.7 && <5
+    , binary >=0.7 && <0.9
+    , bytestring >0.9 && <0.12
+    , bytestring-lexing >=0.4 && <0.6
+    , containers ==0.6.*
+    , cpu ==0.1.*
+    , data-binary-ieee754 ==0.4.*
+    , mtl >=2.1 && <2.4
+    , postgresql-simple >=0.6 && <0.8
+    , storable-record ==0.0.*
+    , vector >=0.12 && <0.14
+  default-language: Haskell2010
+
+test-suite job-tests
+  type: exitcode-stdio-1.0
+  main-is: Spec.hs
+  other-modules:
+      Database.Postgis.Trivial
+      Database.Postgis.Trivial.Types
+      Database.Postgis.Trivial.Cast
+      Database.Postgis.Trivial.PGISConst
+      Database.Postgis.Trivial.Internal
+      Database.Postgis.Trivial.Traversable.PointND
+      Database.Postgis.Trivial.Traversable.Geometry
+      Database.Postgis.Trivial.Unboxed
+      Database.Postgis.Trivial.Unboxed.PointND
+      Database.Postgis.Trivial.Unboxed.Geometry
+      Database.Postgis.Trivial.Storable
+      Database.Postgis.Trivial.Storable.PointND
+      Database.Postgis.Trivial.Storable.Geometry
+      Tests.Traversable
+      Tests.Unboxed
+      Tests.Storable
+  hs-source-dirs:
+      src
+      test
+  default-extensions:
+      NoImplicitPrelude
+  ghc-options: -Wall
+  build-depends:
+      HUnit ==1.6.*
+    , base >=4.7 && <5
+    , binary >=0.7 && <0.9
+    , bytestring >0.9 && <0.12
+    , bytestring-lexing >=0.4 && <0.6
+    , containers ==0.6.*
+    , cpu ==0.1.*
+    , data-binary-ieee754 ==0.4.*
+    , mtl >=2.1 && <2.4
+    , postgis-trivial
+    , postgresql-simple >=0.6 && <0.8
+    , storable-record ==0.0.*
+    , vector >=0.12 && <0.14
+  default-language: Haskell2010
diff --git a/src/Database/Postgis/Trivial.hs b/src/Database/Postgis/Trivial.hs
new file mode 100644
--- /dev/null
+++ b/src/Database/Postgis/Trivial.hs
@@ -0,0 +1,35 @@
+-- | Allows PostGIS to work with Traversable geospatial data enclosed in
+-- Traversable data structures.
+--
+-- Example of suitable data types for LineString and Polygon.
+--
+-- > {-# LANGUAGE TypeFamilies #-}
+-- >
+-- > data LatLon =
+-- >         LatLon !Double !Double
+-- >         deriving (Show, Eq)
+-- >
+-- > type instance Cast LatLon = P2D
+-- >
+-- > instance Castable LatLon where
+-- >     toPointND (LatLon y x) = Point2D x y
+-- >     fromPointND (Point2D x y) = LatLon y x
+-- >
+-- > type LineStringData = [LatLon]
+-- > type PolygonData = [[LatLon], [LatLon]]
+--
+
+module Database.Postgis.Trivial (
+    module Database.Postgis.Trivial.Types
+  , module Database.Postgis.Trivial.Cast
+  , module Database.Postgis.Trivial.Traversable.PointND
+  , module Database.Postgis.Trivial.Traversable.Geometry
+) where
+
+import Database.Postgis.Trivial.Types
+import Database.Postgis.Trivial.Cast
+import Database.Postgis.Trivial.Traversable.PointND
+import Database.Postgis.Trivial.Traversable.Geometry
+
+
+
diff --git a/src/Database/Postgis/Trivial/Cast.hs b/src/Database/Postgis/Trivial/Cast.hs
new file mode 100644
--- /dev/null
+++ b/src/Database/Postgis/Trivial/Cast.hs
@@ -0,0 +1,77 @@
+{-# LANGUAGE FlexibleInstances #-}
+{-# LANGUAGE MultiParamTypeClasses #-}
+{-# LANGUAGE TypeFamilies #-}
+{-# LANGUAGE FlexibleContexts #-}
+
+module Database.Postgis.Trivial.Cast where
+
+import GHC.Base hiding ( foldr )
+import GHC.Num ( Num((+)) )
+import Control.Monad ( replicateM, mapM_ )
+import Data.Functor ( (<$>), (<&>) )
+import Data.Typeable    ( Typeable )
+import Data.Foldable    ( Foldable(..) )
+import Data.Traversable ( Traversable(..) )
+import qualified Data.Vector as V
+import qualified Data.Map as M
+import qualified Data.IntMap as IM
+import Data.List (zip)
+
+import Database.Postgis.Trivial.Types
+import Database.Postgis.Trivial.Internal
+
+
+-- | 'Type cast' type family
+type family Cast p
+
+-- | 'Type cast' procedures
+class (Typeable p, PointND (Cast p)) => Castable p where
+    toPointND :: p -> Cast p
+    fromPointND :: Cast p -> p
+
+-- | Translator of Traversables
+class (Castable p, Traversable t, Typeable t) => Trans t p where
+    transTo :: t p -> t (Cast p)
+    transTo vs = toPointND <$> vs
+    transFrom :: t (Cast p) -> t p
+    transFrom vs = fromPointND <$> vs
+
+instance Castable p => Trans [] p where
+instance Castable p => Trans V.Vector p where
+instance (Castable p, Typeable k) => Trans (M.Map k) p where
+instance Castable p => Trans IM.IntMap p where
+
+-- | Replication procedure for different Traversables
+class (Traversable t, Typeable t) => Repl t b where
+    repl :: Int -> HeaderGetter b -> HeaderGetter (t b)
+
+instance Repl [] b where
+    repl = replicateM
+instance Repl V.Vector b where
+    repl = V.replicateM
+
+-- | Point chain is a base structural component of geometries
+class Traversable t => GeoChain t where
+    count :: t p -> Int
+    putChain :: PointND a => Putter (t a)
+    putChain vs = do
+        putChainLen $ count vs
+        mapM_ putPointND vs
+    getChain :: (Traversable t, PointND a) => HeaderGetter (t a)
+    -- {-# MINIMAL count | putChain #-}
+
+instance GeoChain V.Vector where
+    count = V.length
+    getChain = getChainLen >>= (`V.replicateM` getPointND)
+instance GeoChain [] where
+    count = foldr (\_ r->r+1) (0::Int)
+    getChain = getChainLen >>= (`replicateM` getPointND)
+instance GeoChain (M.Map Int) where
+    count = M.size
+    getChain = (getChainLen >>= (`replicateM` getPointND)) <&>
+        (M.fromList . zip ([0..]::[Int]))
+instance GeoChain IM.IntMap where
+    count = IM.size
+    getChain = (getChainLen >>= (`replicateM` getPointND)) <&>
+        (IM.fromList . zip ([0..]::[Int]))
+
diff --git a/src/Database/Postgis/Trivial/Internal.hs b/src/Database/Postgis/Trivial/Internal.hs
new file mode 100644
--- /dev/null
+++ b/src/Database/Postgis/Trivial/Internal.hs
@@ -0,0 +1,237 @@
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE TupleSections #-}
+
+-----------------------------------------------------------------------------
+-- |
+-- Module      :  Database.Postgis.Trivial.Internal
+--
+-- Low level operations on the Postgis extention of the PostgreSQL Database
+--
+-----------------------------------------------------------------------------
+
+{- Some techniques in this module were taken from
+haskell-postgis <https://hackage.haskell.org/package/haskell-postgis>
+
+Since that package comes with MIT license, we provide it here.
+
+> Copyright (c) 2014 Peter
+>
+> Permission is hereby granted, free of charge, to any person obtaining
+> a copy of this software and associated documentation files (the
+> "Software"), to deal in the Software without restriction, including
+> without limitation the rights to use, copy, modify, merge, publish,
+> distribute, sublicense, and/or sell copies of the Software, and to
+> permit persons to whom the Software is furnished to do so, subject to
+> the following conditions:
+>
+> The above copyright notice and this permission notice shall be included
+> in all copies or substantial portions of the Software.
+>
+> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+> EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+> MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+> IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+> CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+> TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+> SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+-}
+
+
+module Database.Postgis.Trivial.Internal where
+
+import GHC.Base hiding ( foldr )
+import GHC.Num ( Num(..) )
+import GHC.Show ( Show(show) )
+import GHC.Real ( fromIntegral, Integral )
+import System.Endian ( getSystemEndianness, Endianness(..) )
+import Foreign ( Int64, Bits((.&.), (.|.)) )
+import Control.Applicative ( (<$>) )
+import Control.Monad (void )
+import Control.Monad.Reader ( ReaderT(runReaderT), asks, MonadTrans(lift) )
+import Control.Exception ( throw )
+import Data.Foldable ( Foldable(..) )
+import Data.ByteString.Lex.Integral ( packHexadecimal, readHexadecimal )
+import qualified Data.ByteString.Lazy as BL
+import qualified Data.ByteString as BS
+import qualified Data.ByteString.Char8 as BC
+import Data.Binary ( Word8, Word32, Word64, Get, Put, byteSwap32, byteSwap64,
+    Binary(get, put) )
+import Data.Binary.Get ( getLazyByteString, lookAhead )
+import Data.Binary.Put ( putLazyByteString )
+import Data.Maybe ( isJust )
+import Data.Binary.IEEE754 ( wordToDouble, doubleToWord )
+
+import Database.Postgis.Trivial.PGISConst
+import Database.Postgis.Trivial.Types
+
+
+type HeaderGetter = Getter Header
+
+newtype ByteOrder = ByteOrder Endianness deriving Show
+
+-- | Header record
+data Header = Header {
+    _byteOrder  :: ByteOrder
+  , _geoType    :: Word32
+  , _srid       :: SRID
+} deriving Show
+
+instance Binary ByteOrder where
+    get = fromBin <$> getLazyByteString 2
+    put = putLazyByteString . toBin
+
+instance Binary Header where
+    get = getHeader
+    put (Header bo gt s) =
+        put bo >> (putInt . fromIntegral) gt  >> putMaybe s putInt
+
+-- | Serializable class
+class Serializable a where
+    toBin :: a -> BL.ByteString
+    fromBin :: BL.ByteString -> a
+
+instance Serializable ByteOrder where
+    toBin (ByteOrder BigEndian) = "00" :: BL.ByteString
+    toBin (ByteOrder LittleEndian) = "01" :: BL.ByteString
+    fromBin bo = case _fromBin bo :: Word8 of
+        0 -> ByteOrder BigEndian
+        1 -> ByteOrder LittleEndian
+        _ -> throw $ GeometryError $ "Incorrect ByteOrder " ++ show bo
+
+instance Serializable Word32 where
+    toBin = _toBin 8
+    fromBin = _fromBin
+
+instance Serializable Word64 where
+    toBin = _toBin 16
+    fromBin = _fromBin
+
+_toBin :: Integral a => Int -> a -> BL.ByteString
+_toBin l w = case bsRes of
+    Just s -> BL.fromChunks [pad l s]
+    Nothing -> throw $ GeometryError "toBin: cannot convert word"
+    where
+        bsRes = packHexadecimal w
+        pad l' bs' = BS.append (BC.replicate (l' - BS.length bs') '0') bs'
+
+_fromBin :: Integral a => BL.ByteString -> a
+_fromBin bs = case hexRes of
+    Just (v, _) -> v
+    Nothing -> throw $ GeometryError "fromBin: cannot parse hexadecimal"
+    where
+        hexRes = readHexadecimal $ BS.concat . BL.toChunks $ bs
+
+{-# INLINE byteSwapFn #-}
+byteSwapFn :: ByteOrder -> (a -> a) -> a -> a
+byteSwapFn bo f = case bo of
+    ByteOrder BigEndian     -> id
+    ByteOrder LittleEndian  -> f
+
+makeHeader :: SRID -> Word32 -> (Bool, Bool) -> Header
+makeHeader srid geoType (hasM, hasZ) =
+    Header (ByteOrder getSystemEndianness) gt srid where
+        wOr acc (p, h) = if p then h .|. acc else acc
+        gt = foldl wOr geoType
+            [(hasM, wkbM), (hasZ, wkbZ), (isJust srid, wkbSRID)]
+
+{-# INLINE lookupType #-}
+lookupType :: Header -> Word32
+lookupType h = _geoType h .&. ewkbTypeOffset
+
+-- | Putters
+putDouble :: Putter Double
+putDouble = putLazyByteString . toBin .
+    byteSwapFn (ByteOrder getSystemEndianness) byteSwap64 . doubleToWord
+
+putInt :: Putter Int
+putInt = putLazyByteString . toBin .
+    byteSwapFn (ByteOrder getSystemEndianness) byteSwap32 . fromIntegral
+
+{-# INLINE putMaybe #-}
+putMaybe :: Maybe a -> Putter a -> Put
+putMaybe mi = case mi of
+    Just i -> ($ i)
+    Nothing -> \_ -> return ()
+
+{-# INLINE putChainLen #-}
+putChainLen :: Putter Int
+putChainLen = putInt
+
+putHeader :: SRID -> Word32 -> (Bool, Bool) -> Put
+putHeader s geoType (hasMBool, hasZBool) =
+    put $ makeHeader s geoType (hasMBool, hasZBool)
+
+putPoint :: Putter (Double, Double, Maybe Double, Maybe Double)
+putPoint (x, y, Nothing, Nothing)   = putDouble x >> putDouble y
+putPoint (x, y, Just z, Nothing)    = putDouble x >> putDouble y >> putDouble z
+putPoint (x, y, Nothing, Just m)    = putDouble x >> putDouble y >> putDouble m
+putPoint (x, y, Just z, Just m)     = putDouble x >> putDouble y >> putDouble z
+                                        >> putDouble m
+
+{-# INLINE putPointND #-}
+putPointND :: PointND a => Putter a
+putPointND a = putPoint $ components a
+
+-- | BinGetters
+getHeader :: Get Header
+getHeader = do
+    bo <- get
+    t <- fromIntegral <$>  _getInt bo
+    s <- if t .&. wkbSRID > 0
+        then Just . fromIntegral <$> _getInt bo
+        else return Nothing
+    return $ Header bo t s
+
+{-# INLINE skipHeader #-}
+skipHeader :: HeaderGetter ()
+skipHeader = void (lift getHeader)
+
+{-# INLINE getHeaderPre #-}
+getHeaderPre :: Get Header
+getHeaderPre = lookAhead get
+
+getPoint :: HeaderGetter (Double, Double, Maybe Double, Maybe Double)
+getPoint = do
+    gt <- asks _geoType
+    let hasM = (gt .&. wkbM) > 0
+        hasZ = (gt .&. wkbZ) > 0
+    x <- getDouble
+    y <- getDouble
+    zMb <- if hasZ then Just <$> getDouble else return Nothing
+    mMb <- if hasM then Just <$> getDouble else return Nothing
+    return (x, y, zMb, mMb)
+
+{-# INLINE getPointND #-}
+getPointND :: PointND a => HeaderGetter a
+getPointND = do
+    (x, y, zMb, mMb) <- getPoint
+    return $ fromComponents (x, y, zMb, mMb)
+
+{-# INLINE getNumber #-}
+getNumber :: (Serializable a) => (a -> a) -> Int64 -> ByteOrder -> Get a
+getNumber f l bo  = do
+    bs <- getLazyByteString l
+    case bo of
+        ByteOrder BigEndian     -> return $ fromBin bs
+        ByteOrder LittleEndian  -> return . f . fromBin $ bs
+
+_getInt :: ByteOrder -> Get Int
+_getInt = fmap fromIntegral . getNumber byteSwap32 8
+
+getInt :: HeaderGetter Int
+getInt = asks _byteOrder >>= lift . _getInt
+
+_getDouble :: ByteOrder -> Get Double
+_getDouble = fmap wordToDouble . getNumber byteSwap64 16
+
+getDouble :: HeaderGetter Double
+getDouble = asks _byteOrder >>= lift . _getDouble
+
+{-# INLINE getChainLen #-}
+getChainLen :: HeaderGetter Int
+getChainLen = getInt
+
+-- | Geometry returning function
+makeResult :: Header -> HeaderGetter a -> Get (a, SRID)
+makeResult h getter = (,_srid h) <$> runReaderT getter h
+
diff --git a/src/Database/Postgis/Trivial/PGISConst.hsc b/src/Database/Postgis/Trivial/PGISConst.hsc
new file mode 100644
--- /dev/null
+++ b/src/Database/Postgis/Trivial/PGISConst.hsc
@@ -0,0 +1,29 @@
+{-# LANGUAGE CPP #-}
+
+module Database.Postgis.Trivial.PGISConst where
+
+import Foreign
+
+#include <pgisConst.h>
+
+wkbZ :: Word32
+wkbZ    = #const WKBZOFFSET
+wkbM :: Word32
+wkbM    = #const WKBMOFFSET
+wkbSRID :: Word32
+wkbSRID = #const WKBSRIDFLAG
+ewkbTypeOffset :: Word32
+ewkbTypeOffset = 0x1fffffff
+
+pgisPoint :: Word32
+pgisPoint = #const POINTTYPE
+pgisLinestring :: Word32
+pgisLinestring = #const LINETYPE
+pgisPolygon :: Word32
+pgisPolygon = #const POLYGONTYPE
+pgisMultiPoint :: Word32
+pgisMultiPoint = #const MULTIPOINTTYPE
+pgisMultiLinestring :: Word32
+pgisMultiLinestring = #const MULTILINETYPE
+pgisMultiPolygon :: Word32
+pgisMultiPolygon = #const MULTIPOLYGONTYPE
diff --git a/src/Database/Postgis/Trivial/Storable.hs b/src/Database/Postgis/Trivial/Storable.hs
new file mode 100644
--- /dev/null
+++ b/src/Database/Postgis/Trivial/Storable.hs
@@ -0,0 +1,50 @@
+-- | Allows PostGIS to work with geospatial data enclosed in
+-- Traversable data structures with Storable Vectors as the most
+-- inner structures.
+--
+-- Example of suitable data types for LineString and Polygon.
+--
+-- > {-# LANGUAGE TypeFamilies #-}
+-- >
+-- > import qualified Data.Vector.Storable as VS
+-- > import Foreign.Storable.Record as Store
+-- > import Foreign.Storable ( Storable (..) )
+-- >
+-- > data GeoDuo = GeoDuo
+-- >         { dx :: !Double
+-- >         , dy :: !Double
+-- >         } deriving (Show, Eq)
+-- >
+-- > storeGeoDuo :: Store.Dictionary GeoDuo
+-- > storeGeoDuo = Store.run $
+-- >     liftA2 GeoDuo
+-- >         (Store.element dx)
+-- >         (Store.element dy)
+-- >
+-- > instance Storable GeoDuo where
+-- >    sizeOf = Store.sizeOf storeGeoDuo
+-- >    alignment = Store.alignment storeGeoDuo
+-- >    peek = Store.peek storeGeoDuo
+-- >    poke = Store.poke storeGeoDuo
+-- >
+-- > type instance Cast GeoDuo = P2D
+-- >
+-- > instance Castable GeoDuo where
+-- >     toPointND (GeoDuo y x) = Point2D x y
+-- >     fromPointND (Point2D x y) = GeoDuo y x
+-- >
+-- > type LineStringData = VS.Vector GeoDuo
+-- > type PolygonData = [VS.Vector GeoDuo]
+--
+
+module Database.Postgis.Trivial.Storable (
+    module Database.Postgis.Trivial.Types
+  , module Database.Postgis.Trivial.Cast
+  , module Database.Postgis.Trivial.Storable.PointND
+  , module Database.Postgis.Trivial.Storable.Geometry
+) where
+
+import Database.Postgis.Trivial.Types
+import Database.Postgis.Trivial.Cast
+import Database.Postgis.Trivial.Storable.PointND
+import Database.Postgis.Trivial.Storable.Geometry
diff --git a/src/Database/Postgis/Trivial/Storable/Geometry.hs b/src/Database/Postgis/Trivial/Storable/Geometry.hs
new file mode 100644
--- /dev/null
+++ b/src/Database/Postgis/Trivial/Storable/Geometry.hs
@@ -0,0 +1,204 @@
+{-# LANGUAGE ScopedTypeVariables #-}
+{-# LANGUAGE TypeApplications #-}
+{-# LANGUAGE UndecidableInstances #-}
+{-# LANGUAGE FlexibleContexts #-}
+
+module Database.Postgis.Trivial.Storable.Geometry where
+
+import GHC.Base hiding ( foldr )
+import Control.Monad ( mapM_ )
+import Control.Exception ( throw )
+import Control.Applicative ( (<$>) )
+import qualified Data.Vector.Storable as VS
+
+import Database.Postgis.Trivial.PGISConst
+import Database.Postgis.Trivial.Types
+import Database.Postgis.Trivial.Internal
+import Database.Postgis.Trivial.Cast
+
+
+-- | Translator of Unboxed vectors (direct)
+transToS :: (Castable p, VS.Storable p, VS.Storable (Cast p)) =>
+        VS.Vector p -> VS.Vector (Cast p)
+transToS = VS.map toPointND
+
+-- | Translator of Unboxed vectors (reverse)
+transFromS :: (Castable p, VS.Storable p, VS.Storable (Cast p)) =>
+        VS.Vector (Cast p) -> VS.Vector p
+transFromS = VS.map fromPointND
+
+-- | Chain putter
+putChainS :: (PointND a, VS.Storable a) => Putter (VS.Vector a)
+putChainS vs = do
+        putChainLen $ VS.length vs
+        VS.mapM_ putPointND vs
+
+-- | Chain getter
+getChainS :: (PointND a, VS.Storable a) => HeaderGetter (VS.Vector a)
+getChainS = getChainLen >>= (`VS.replicateM` getPointND)
+
+-- | Point geometry
+data Point p = Point SRID p
+
+instance (Castable p, VS.Storable 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 p = LineString SRID (VS.Vector p)
+
+instance (Castable p, VS.Storable p, VS.Storable (Cast p)) =>
+        Geometry (LineString p) where
+    putGeometry (LineString srid vs) = do
+        putHeader srid pgisLinestring (dimProps @(Cast p))
+        putChainS (transToS (vs::VS.Vector p)::VS.Vector (Cast p))
+    getGeometry = do
+        h <- getHeaderPre
+        (vs::VS.Vector (Cast p), srid) <-
+            if lookupType h==pgisLinestring
+            then makeResult h (skipHeader >> getChainS)
+            else throw $
+                GeometryError "invalid data for linestring geometry"
+        return (LineString srid (transFromS vs::VS.Vector p))
+
+-- | Polygon geometry
+data Polygon t2 p = Polygon SRID (t2 (VS.Vector p))
+
+instance (Castable p, VS.Storable p, VS.Storable (Cast p), GeoChain t2,
+        Repl t2 (VS.Vector (Cast p))) => Geometry (Polygon t2 p) where
+    putGeometry (Polygon srid vss) = do
+        putHeader srid pgisPolygon (dimProps @(Cast p))
+        putChainLen $ count vss
+        mapM_ (\vs -> putChainS (transToS vs::VS.Vector (Cast p))) vss
+    getGeometry = do
+        h <- getHeaderPre
+        (vss::t2 (VS.Vector (Cast p)), srid) <- if lookupType h==pgisPolygon
+            then makeResult h (skipHeader >> getChainLen >>= (`repl` getChainS))
+            else throw $
+                GeometryError "invalid data for polygon geometry"
+        return (Polygon srid (transFromS <$> vss::t2 (VS.Vector p)))
+
+-- | MultiPoint geometry
+data MultiPoint p = MultiPoint SRID (VS.Vector p)
+
+instance (Castable p, VS.Storable p, VS.Storable (Cast p)) =>
+        Geometry (MultiPoint p) where
+    putGeometry (MultiPoint srid vs) = do
+        putHeader srid pgisMultiPoint (dimProps @(Cast p))
+        putChainLen $ VS.length vs
+        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 >>=
+                    (`VS.replicateM` (skipHeader >> getPointND))
+                )
+            else throw $
+                GeometryError "invalid data for multipoint geometry"
+        return (MultiPoint srid (transFromS vs::VS.Vector p))
+
+-- | MultiLineString geometry
+data MultiLineString t2 p = MultiLineString SRID (t2 (VS.Vector p))
+
+instance (Castable p, VS.Storable p, VS.Storable (Cast p), GeoChain t2,
+        Repl t2 (VS.Vector (Cast p))) => Geometry (MultiLineString t2 p) where
+    putGeometry (MultiLineString srid vss) = do
+        putHeader srid pgisMultiLinestring (dimProps @(Cast p))
+        putChainLen $ count vss
+        mapM_ (\vs -> do
+            putGeometry (LineString srid vs :: LineString p)
+            ) vss
+    getGeometry = do
+        h <- getHeaderPre
+        (vss::t2 (t1 (Cast p)), srid) <- if lookupType h==pgisMultiLinestring
+            then makeResult h (
+                skipHeader >> getChainLen >>= (`repl` (skipHeader >> getChainS))
+                )
+            else throw $
+                GeometryError "invalid data for multilinestring geometry"
+        return (MultiLineString srid (transFromS <$> vss::t2 (VS.Vector p)))
+
+-- | MultiPolygon geometry
+data MultiPolygon t3 t2 p = MultiPolygon SRID (t3 (t2 (VS.Vector p)))
+
+instance (Castable p, VS.Storable p, VS.Storable (Cast p),
+        Repl t3 (t2 (VS.Vector (Cast p))), Repl t2 (VS.Vector (Cast p)),
+        GeoChain t2, GeoChain t3) => Geometry (MultiPolygon t3 t2 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 p)
+            ) vsss
+    getGeometry = do
+        h <- getHeaderPre
+        (vsss::t3 (t2 (VS.Vector (Cast p))), srid) <-
+            if lookupType h==pgisMultiPolygon
+            then makeResult h (do
+                skipHeader >> getChainLen >>= (`repl` (skipHeader >> getChainLen
+                    >>= (`repl` getChainS)))
+                )
+            else throw $
+                GeometryError "invalid data for multipolygon geometry"
+        return (MultiPolygon srid ((transFromS <$>) <$>
+            vsss::t3 (t2 (VS.Vector 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 -> VS.Vector p -> Geo (LineString p)
+putLS srid ps = Geo (LineString srid ps)
+
+-- | LineString getter
+getLS :: Geo (LineString p) -> (SRID, VS.Vector p)
+getLS (Geo (LineString srid vs)) = (srid, vs)
+
+-- | Polygon putter
+putPoly :: SRID -> t2 (VS.Vector p) -> Geo (Polygon t2 p)
+putPoly srid pss = Geo (Polygon srid pss)
+
+-- | Polygon getter
+getPoly :: Geo (Polygon t2 p) -> (SRID, t2 (VS.Vector p))
+getPoly (Geo (Polygon srid vss)) = (srid, vss)
+
+-- | MultiPoint putter
+putMPoint :: SRID -> VS.Vector p -> Geo (MultiPoint p)
+putMPoint srid ps = Geo (MultiPoint srid ps)
+
+-- | MultiPoint getter
+getMPoint :: Geo (MultiPoint p) -> (SRID, VS.Vector p)
+getMPoint (Geo (MultiPoint srid vs)) = (srid, vs)
+
+-- | MultiLineString putter
+putMLS :: SRID -> t2 (VS.Vector p) -> Geo (MultiLineString t2 p)
+putMLS srid pss = Geo (MultiLineString srid pss)
+
+-- | MultiLineString getter
+getMLS :: Geo (MultiLineString t2 p) -> (SRID, t2 (VS.Vector p))
+getMLS (Geo (MultiLineString srid vs)) = (srid, vs)
+
+-- | MultiPolygon putter
+putMPoly :: SRID -> t3 (t2 (VS.Vector p)) -> Geo (MultiPolygon t3 t2 p)
+putMPoly srid psss = Geo (MultiPolygon srid psss)
+
+-- | MultiPolygon getter
+getMPoly :: Geo (MultiPolygon t3 t2 p) -> (SRID, t3 (t2 (VS.Vector p)))
+getMPoly (Geo (MultiPolygon srid vs)) = (srid, vs)
+
diff --git a/src/Database/Postgis/Trivial/Storable/PointND.hs b/src/Database/Postgis/Trivial/Storable/PointND.hs
new file mode 100644
--- /dev/null
+++ b/src/Database/Postgis/Trivial/Storable/PointND.hs
@@ -0,0 +1,164 @@
+{-# LANGUAGE TypeFamilies #-}
+
+module Database.Postgis.Trivial.Storable.PointND
+( P2DS (..)
+, P3DZS (..)
+, P3DMS (..)
+, P4DS (..)
+) where
+
+import GHC.Base
+import GHC.Show ( Show )
+import Foreign.Storable ( Storable (..) )
+import Foreign.Storable.Record as Store
+import Control.Exception ( throw )
+
+import Database.Postgis.Trivial.Types
+import Database.Postgis.Trivial.Cast
+
+
+-- | Four arguments LiftA
+liftA4 :: Applicative f
+        => (a -> b -> c -> d -> e) -> f a -> f b -> f c -> f d -> f e
+liftA4 f a b c d = liftA3 f a b c <*> d
+
+-- P2DS =========================================================================
+
+-- | Default Storable 2D point
+data P2DS = Point2DS
+        { xP2DS :: {-# UNPACK #-} !Double
+        , yP2DS :: {-# UNPACK #-} !Double
+        } deriving (Show, Eq)
+
+storeP2DS :: Store.Dictionary P2DS
+storeP2DS = Store.run $
+    liftA2 Point2DS
+        (Store.element xP2DS)
+        (Store.element yP2DS)
+
+instance Storable P2DS where
+   sizeOf = Store.sizeOf storeP2DS
+   alignment = Store.alignment storeP2DS
+   peek = Store.peek storeP2DS
+   poke = Store.poke storeP2DS
+
+instance PointND P2DS where
+    dimProps = (False, False)
+    components (Point2DS x y) = (x, y, Nothing, Nothing)
+    fromComponents (x, y, Nothing, Nothing) = Point2DS x y
+    fromComponents _ = throw $
+        GeometryError "invalid transition from user data type to P2DS"
+
+-- P3DZ ========================================================================
+
+-- | Default Storable 3D point with Z component
+data P3DZS = Point3DZS
+        { xP3DZ :: {-# UNPACK #-} !Double
+        , yP3DZ :: {-# UNPACK #-} !Double
+        , zP3DZ :: {-# UNPACK #-} !Double
+        } deriving (Show, Eq)
+
+storeP3DZ :: Store.Dictionary P3DZS
+storeP3DZ = Store.run $
+    liftA3 Point3DZS
+        (Store.element xP3DZ)
+        (Store.element yP3DZ)
+        (Store.element zP3DZ)
+
+instance Storable P3DZS where
+   sizeOf = Store.sizeOf storeP3DZ
+   alignment = Store.alignment storeP3DZ
+   peek = Store.peek storeP3DZ
+   poke = Store.poke storeP3DZ
+
+instance PointND P3DZS where
+    dimProps = (False, True)
+    components (Point3DZS x y z) = (x, y, Just z, Nothing)
+    fromComponents (x, y, Just z, Nothing) = Point3DZS x y z
+    fromComponents _ = throw $
+        GeometryError "invalid transition from user data type to P3DZ"
+
+-- P3DM ========================================================================
+
+-- | Default Storable 3D point with M component
+data P3DMS = Point3DMS
+        { xP3DM :: {-# UNPACK #-} !Double
+        , yP3DM :: {-# UNPACK #-} !Double
+        , mP3DM :: {-# UNPACK #-} !Double
+        } deriving (Show, Eq)
+
+storeP3DM :: Store.Dictionary P3DMS
+storeP3DM = Store.run $
+    liftA3 Point3DMS
+        (Store.element xP3DM)
+        (Store.element yP3DM)
+        (Store.element mP3DM)
+
+instance Storable P3DMS where
+   sizeOf = Store.sizeOf storeP3DM
+   alignment = Store.alignment storeP3DM
+   peek = Store.peek storeP3DM
+   poke = Store.poke storeP3DM
+
+instance PointND P3DMS where
+    dimProps = (True, False)
+    components (Point3DMS x y m) = (x, y, Just m, Nothing)
+    fromComponents (x, y, Just m, Nothing) = Point3DMS x y m
+    fromComponents _ = throw $
+        GeometryError "invalid transition from user data type to P3DM"
+
+-- P4D =========================================================================
+
+-- | Default Storable point with Z and M component
+data P4DS = Point4DS
+        { xP4D :: {-# UNPACK #-} !Double
+        , yP4D :: {-# UNPACK #-} !Double
+        , zP4D :: {-# UNPACK #-} !Double
+        , mP4D :: {-# UNPACK #-} !Double
+        } deriving (Show, Eq)
+
+storeP4D :: Store.Dictionary P4DS
+storeP4D = Store.run $
+    liftA4 Point4DS
+        (Store.element xP4D)
+        (Store.element yP4D)
+        (Store.element zP4D)
+        (Store.element mP4D)
+
+instance Storable P4DS where
+   sizeOf = Store.sizeOf storeP4D
+   alignment = Store.alignment storeP4D
+   peek = Store.peek storeP4D
+   poke = Store.poke storeP4D
+
+instance PointND P4DS where
+    dimProps = (True, True)
+    components (Point4DS x y z m) = (x, y, Just z, Just m)
+    fromComponents (x, y, Just z, Just m) = Point4DS x y z m
+    fromComponents _ = throw $
+        GeometryError "invalid transition from user data type to P4D"
+
+-- Cast ========================================================================
+
+type instance Cast P2DS = P2DS
+type instance Cast P3DZS = P3DZS
+type instance Cast P3DMS = P3DMS
+type instance Cast P4DS = P4DS
+
+instance Castable P2DS where
+    toPointND = coerce
+    fromPointND = coerce
+instance Castable P3DZS where
+    toPointND = coerce
+    fromPointND = coerce
+instance Castable P3DMS where
+    toPointND = coerce
+    fromPointND = coerce
+instance Castable P4DS where
+    toPointND = coerce
+    fromPointND = coerce
+
+
+
+
+
diff --git a/src/Database/Postgis/Trivial/Traversable/Geometry.hs b/src/Database/Postgis/Trivial/Traversable/Geometry.hs
new file mode 100644
--- /dev/null
+++ b/src/Database/Postgis/Trivial/Traversable/Geometry.hs
@@ -0,0 +1,181 @@
+{-# 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)
+
+
+
+
+
diff --git a/src/Database/Postgis/Trivial/Traversable/PointND.hs b/src/Database/Postgis/Trivial/Traversable/PointND.hs
new file mode 100644
--- /dev/null
+++ b/src/Database/Postgis/Trivial/Traversable/PointND.hs
@@ -0,0 +1,87 @@
+{-# LANGUAGE TypeFamilies #-}
+
+module Database.Postgis.Trivial.Traversable.PointND where
+
+import GHC.Base
+import GHC.Show ( Show )
+import Control.Exception ( throw )
+
+import Database.Postgis.Trivial.Types
+import Database.Postgis.Trivial.Cast
+
+
+-- | Default 2D point
+data P2D = Point2D
+        { xP2D :: {-# UNPACK #-} !Double
+        , yP2D :: {-# UNPACK #-} !Double
+        } deriving (Show, Eq)
+
+instance PointND P2D where
+    dimProps = (False, False)
+    components (Point2D x y) = (x, y, Nothing, Nothing)
+    fromComponents (x, y, Nothing, Nothing) = Point2D x y
+    fromComponents _ = throw $
+        GeometryError "invalid transition from user data type to Point2D"
+
+-- | Default 3D point with Z component
+data P3DZ = Point3DZ
+        { xP3DZ :: {-# UNPACK #-} !Double
+        , yP3DZ :: {-# UNPACK #-} !Double
+        , zP3DZ :: {-# UNPACK #-} !Double
+        } deriving (Show, Eq)
+
+instance PointND P3DZ where
+    dimProps = (False, True)
+    components (Point3DZ x y z) = (x, y, Just z, Nothing)
+    fromComponents (x, y, Just z, Nothing) = Point3DZ x y z
+    fromComponents _ = throw $
+        GeometryError "invalid transition from user data type to Point3DZ"
+
+-- | Default 3D point with M component
+data P3DM = Point3DM
+        { xP3DM :: {-# UNPACK #-} !Double
+        , yP3DM :: {-# UNPACK #-} !Double
+        , mP3DM :: {-# UNPACK #-} !Double
+        } deriving (Show, Eq)
+
+instance PointND P3DM where
+    dimProps = (True, False)
+    components (Point3DM x y m) = (x, y, Just m, Nothing)
+    fromComponents (x, y, Just m, Nothing) = Point3DM x y m
+    fromComponents _ = throw $
+        GeometryError "invalid transition from user data type to Point2DM"
+
+-- | Default point with Z and M component
+data P4D = Point4D
+        { xP4D :: {-# UNPACK #-} !Double
+        , yP4D :: {-# UNPACK #-} !Double
+        , zP4D :: {-# UNPACK #-} !Double
+        , mP4D :: {-# UNPACK #-} !Double
+        } deriving (Show, Eq)
+
+instance PointND P4D where
+    dimProps = (True, True)
+    components (Point4D x y z m) = (x, y, Just z, Just m)
+    fromComponents (x, y, Just z, Just m) = Point4D x y z m
+    fromComponents _ = throw $
+        GeometryError "invalid transition from user data type to Point4D"
+
+-- Cast ========================================================================
+
+type instance Cast P2D = P2D
+type instance Cast P3DZ = P3DZ
+type instance Cast P3DM = P3DM
+type instance Cast P4D = P4D
+
+instance Castable P2D where
+    toPointND = coerce
+    fromPointND = coerce
+instance Castable P3DZ where
+    toPointND = coerce
+    fromPointND = coerce
+instance Castable P3DM where
+    toPointND = coerce
+    fromPointND = coerce
+instance Castable P4D where
+    toPointND = coerce
+    fromPointND = coerce
diff --git a/src/Database/Postgis/Trivial/Types.hs b/src/Database/Postgis/Trivial/Types.hs
new file mode 100644
--- /dev/null
+++ b/src/Database/Postgis/Trivial/Types.hs
@@ -0,0 +1,103 @@
+
+
+{-# LANGUAGE AllowAmbiguousTypes #-}
+{-# LANGUAGE FlexibleInstances #-}
+{-# LANGUAGE UndecidableInstances #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE ExistentialQuantification                            #-}
+{-# LANGUAGE InstanceSigs #-}
+
+module Database.Postgis.Trivial.Types
+    ( SRID
+    , PointND (..)
+    , Putter
+    , Getter
+    , Geo (..)
+    , Geometry (..)
+    , GeometryError (..)
+    ) where
+
+import GHC.Base
+import GHC.Show ( Show(..), ShowS )
+import Control.Monad.Reader ( ReaderT )
+import Control.Exception ( SomeException, Exception(..) )
+import Data.Typeable ( Typeable, cast )
+import Data.Binary ( Get, Put )
+import Data.Binary.Get ( runGet )
+import Data.Binary.Put ( runPut )
+import qualified Data.ByteString.Lazy as BL
+import qualified Data.ByteString as BS
+import Database.PostgreSQL.Simple.ToField
+import Database.PostgreSQL.Simple.FromField
+
+
+-- | Spatial reference identifier
+type SRID = Maybe Int
+
+-- | Inner point type class
+class Typeable a => PointND a where
+    dimProps :: (Bool, Bool)
+    components :: a -> (Double, Double, Maybe Double, Maybe Double)
+    fromComponents :: (Double, Double, Maybe Double, Maybe Double) -> a
+
+-- | Binary putter
+type Putter a = a -> Put
+
+writeEWKB :: Putter a -> a -> BS.ByteString
+writeEWKB putter = BL.toStrict . runPut . putter
+
+-- | Binary getter
+type Getter h = ReaderT h Get
+
+readEWKB :: Get a -> BS.ByteString -> a
+readEWKB getter bs = runGet getter (BL.fromStrict bs)
+
+-- | Type class which defines geometry to/from binary form convertions
+class Typeable a => Geometry a where
+    putGeometry :: Putter a
+    getGeometry :: Get a
+
+-- | Wrapper for geomety types (prevents collisions with default instances)
+newtype Geo g = Geo g
+
+instance Geometry g => ToField (Geo g) where
+    toField (Geo g) = Escape . writeEWKB putGeometry $ g
+
+instance Geometry g => FromField (Geo g) where
+    fromField f m = do
+        typ <- typename f
+        if typ /= "geometry"
+            then returnError Incompatible f (show typ)
+            else case m of
+                Nothing  -> returnError UnexpectedNull f ""
+                Just bs -> return $ Geo (readEWKB getGeometry bs)
+
+-- | Geometry exceptions
+data SomeGeometryException = forall e. Exception e => SomeGeometryException e
+    deriving Typeable
+
+geometryExceptionToException :: Exception e => e -> SomeException
+geometryExceptionToException = toException . SomeGeometryException
+
+geometryExceptionFromException :: Exception e => SomeException -> Maybe e
+geometryExceptionFromException x = do
+    SomeGeometryException a <- fromException x
+    cast a
+
+instance Show SomeGeometryException where
+    showsPrec :: Int -> SomeGeometryException -> ShowS
+    showsPrec p (SomeGeometryException e) = showsPrec p e
+
+instance Exception SomeGeometryException where
+    displayException (SomeGeometryException e) = displayException e
+
+-- | Exception for operations with geometries
+newtype GeometryError = GeometryError {
+    geoMessage :: String
+    } deriving (Eq, Show, Typeable)
+
+instance Exception GeometryError where
+  toException = geometryExceptionToException
+  fromException = geometryExceptionFromException
+
+
diff --git a/src/Database/Postgis/Trivial/Unboxed.hs b/src/Database/Postgis/Trivial/Unboxed.hs
new file mode 100644
--- /dev/null
+++ b/src/Database/Postgis/Trivial/Unboxed.hs
@@ -0,0 +1,29 @@
+-- | Allows PostGIS to work with geospatial data enclosed in Traversable
+-- data structures with Unboxed Vectors as the most inner structures.
+--
+-- Example of suitable data types for LineString and Polygon.
+--
+-- > {-# LANGUAGE TypeFamilies #-}
+-- >
+-- > import qualified Data.Vector.Unboxed as VU
+-- >
+-- > type instance Cast (Double, Double, Double) = P3DZ
+-- >
+-- > instance Castable (Double, Double, Double) where
+-- >     toPointND (x, y, z) = Point3DZ x y z
+-- >     fromPointND (Point3DZ x y z) = (x, y, z)
+-- >
+-- > type LineStringData = VU.Vector (Double, Double, Double)
+-- > type PolygonData = [VU.Vector (Double, Double, Double)]
+
+module Database.Postgis.Trivial.Unboxed (
+    module Database.Postgis.Trivial.Types
+  , module Database.Postgis.Trivial.Cast
+  , module Database.Postgis.Trivial.Unboxed.PointND
+  , module Database.Postgis.Trivial.Unboxed.Geometry
+) where
+
+import Database.Postgis.Trivial.Types
+import Database.Postgis.Trivial.Cast
+import Database.Postgis.Trivial.Unboxed.PointND
+import Database.Postgis.Trivial.Unboxed.Geometry
diff --git a/src/Database/Postgis/Trivial/Unboxed/Geometry.hs b/src/Database/Postgis/Trivial/Unboxed/Geometry.hs
new file mode 100644
--- /dev/null
+++ b/src/Database/Postgis/Trivial/Unboxed/Geometry.hs
@@ -0,0 +1,204 @@
+{-# LANGUAGE ScopedTypeVariables #-}
+{-# LANGUAGE TypeApplications #-}
+{-# LANGUAGE UndecidableInstances #-}
+{-# LANGUAGE FlexibleContexts #-}
+
+
+module Database.Postgis.Trivial.Unboxed.Geometry where
+
+import GHC.Base hiding ( foldr )
+import Control.Monad ( mapM_ )
+import Control.Exception ( throw )
+import Control.Applicative ( (<$>) )
+import qualified Data.Vector.Unboxed as VU
+
+import Database.Postgis.Trivial.PGISConst
+import Database.Postgis.Trivial.Types
+import Database.Postgis.Trivial.Internal
+import Database.Postgis.Trivial.Cast
+
+
+-- | Translator of Unboxed vectors (direct)
+transToU :: (Castable p, VU.Unbox p, VU.Unbox (Cast p)) =>
+        VU.Vector p -> VU.Vector (Cast p)
+transToU = VU.map toPointND
+
+-- | Translator of Unboxed vectors (reverse)
+transFromU :: (Castable p, VU.Unbox p, VU.Unbox (Cast p)) =>
+        VU.Vector (Cast p) -> VU.Vector p
+transFromU = VU.map fromPointND
+
+-- | Chain putter
+putChainU :: (PointND a, VU.Unbox a) => Putter (VU.Vector a)
+putChainU vs = do
+        putChainLen $ VU.length vs
+        VU.mapM_ putPointND vs
+
+-- | Chain getter
+getChainU :: (PointND a, VU.Unbox a) => HeaderGetter (VU.Vector a)
+getChainU = getChainLen >>= (`VU.replicateM` getPointND)
+
+-- | Point geometry
+data Point p = Point SRID p
+
+instance (Castable p, VU.Unbox 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 p = LineString SRID (VU.Vector p)
+
+instance (Castable p, VU.Unbox p, VU.Unbox (Cast p)) =>
+        Geometry (LineString p) where
+    putGeometry (LineString srid vs) = do
+        putHeader srid pgisLinestring (dimProps @(Cast p))
+        putChainU (transToU (vs::VU.Vector p)::VU.Vector (Cast p))
+    getGeometry = do
+        h <- getHeaderPre
+        (vs::VU.Vector (Cast p), srid) <- if lookupType h==pgisLinestring
+            then makeResult h (skipHeader >> getChainU)
+            else throw $
+                GeometryError "invalid data for linestring geometry"
+        return (LineString srid (transFromU vs::VU.Vector p))
+
+-- | Polygon geometry
+data Polygon t2 p = Polygon SRID (t2 (VU.Vector p))
+
+instance (Castable p, VU.Unbox p, VU.Unbox (Cast p), GeoChain t2,
+        Repl t2 (VU.Vector (Cast p))) => Geometry (Polygon t2 p) where
+    putGeometry (Polygon srid vss) = do
+        putHeader srid pgisPolygon (dimProps @(Cast p))
+        putChainLen $ count vss
+        mapM_ (\vs -> putChainU (transToU vs::VU.Vector (Cast p))) vss
+    getGeometry = do
+        h <- getHeaderPre
+        (vss::t2 (VU.Vector (Cast p)), srid) <- if lookupType h==pgisPolygon
+            then makeResult h (skipHeader >> getChainLen >>= (`repl` getChainU))
+            else throw $
+                GeometryError "invalid data for polygon geometry"
+        return (Polygon srid (transFromU <$> vss::t2 (VU.Vector p)))
+
+-- | MultiPoint geometry
+data MultiPoint p = MultiPoint SRID (VU.Vector p)
+
+instance (Castable p, VU.Unbox p, VU.Unbox (Cast p)) =>
+        Geometry (MultiPoint p) where
+    putGeometry (MultiPoint srid vs) = do
+        putHeader srid pgisMultiPoint (dimProps @(Cast p))
+        putChainLen $ VU.length vs
+        VU.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 >>=
+                    (`VU.replicateM` (skipHeader >> getPointND))
+                )
+            else throw $
+                GeometryError "invalid data for multipoint geometry"
+        return (MultiPoint srid (transFromU vs::VU.Vector p))
+
+-- | MultiLineString geometry
+data MultiLineString t2 p = MultiLineString SRID (t2 (VU.Vector p))
+
+instance (Castable p, VU.Unbox p, VU.Unbox (Cast p), GeoChain t2,
+        Repl t2 (VU.Vector (Cast p))) => Geometry (MultiLineString t2 p) where
+    putGeometry (MultiLineString srid vss) = do
+        putHeader srid pgisMultiLinestring (dimProps @(Cast p))
+        putChainLen $ count vss
+        mapM_ (\vs -> do
+            putGeometry (LineString srid vs :: LineString p)
+            ) vss
+    getGeometry = do
+        h <- getHeaderPre
+        (vss::t2 (t1 (Cast p)), srid) <- if lookupType h==pgisMultiLinestring
+            then makeResult h (
+                skipHeader >> getChainLen >>= (`repl` (skipHeader >> getChainU))
+                )
+            else throw $
+                GeometryError "invalid data for multilinestring geometry"
+        return (MultiLineString srid (transFromU <$> vss::t2 (VU.Vector p)))
+
+-- | MultiPolygon geometry
+data MultiPolygon t3 t2 p = MultiPolygon SRID (t3 (t2 (VU.Vector p)))
+
+instance (Castable p, VU.Unbox p, VU.Unbox (Cast p),
+        Repl t3 (t2 (VU.Vector (Cast p))), Repl t2 (VU.Vector (Cast p)),
+        GeoChain t2, GeoChain t3) => Geometry (MultiPolygon t3 t2 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 p)
+            ) vsss
+    getGeometry = do
+        h <- getHeaderPre
+        (vsss::t3 (t2 (VU.Vector (Cast p))), srid) <-
+            if lookupType h==pgisMultiPolygon
+            then makeResult h (do
+                skipHeader >> getChainLen >>= (`repl` (skipHeader >> getChainLen
+                    >>= (`repl` getChainU)))
+                )
+            else throw $
+                GeometryError "invalid data for multipolygon geometry"
+        return (MultiPolygon srid ((transFromU <$>) <$>
+            vsss::t3 (t2 (VU.Vector p))))
+
+-- | Point putter
+putPoint :: Castable p => SRID -> p -> Geo (Point p)
+putPoint srid p = Geo (Point srid p)
+
+-- | Point getter
+getPoint :: Castable p => Geo (Point p) -> (SRID, p)
+getPoint (Geo (Point srid v)) = (srid, v)
+
+-- | Linestring putter
+putLS :: SRID -> VU.Vector p -> Geo (LineString p)
+putLS srid ps = Geo (LineString srid ps)
+
+-- | LineString getter
+getLS :: Geo (LineString p) -> (SRID, VU.Vector p)
+getLS (Geo (LineString srid vs)) = (srid, vs)
+
+-- | Polygon putter
+putPoly :: SRID -> t2 (VU.Vector p) -> Geo (Polygon t2 p)
+putPoly srid pss = Geo (Polygon srid pss)
+
+-- | Polygon getter
+getPoly :: Geo (Polygon t2 p) -> (SRID, t2 (VU.Vector p))
+getPoly (Geo (Polygon srid vss)) = (srid, vss)
+
+-- | MultiPoint putter
+putMPoint :: SRID -> VU.Vector p -> Geo (MultiPoint p)
+putMPoint srid ps = Geo (MultiPoint srid ps)
+
+-- | MultiPoint getter
+getMPoint :: Geo (MultiPoint p) -> (SRID, VU.Vector p)
+getMPoint (Geo (MultiPoint srid vs)) = (srid, vs)
+
+-- | MultiLineString putter
+putMLS :: SRID -> t2 (VU.Vector p) -> Geo (MultiLineString t2 p)
+putMLS srid pss = Geo (MultiLineString srid pss)
+
+-- | MultiLineString getter
+getMLS :: Geo (MultiLineString t2 p) -> (SRID, t2 (VU.Vector p))
+getMLS (Geo (MultiLineString srid vs)) = (srid, vs)
+
+-- | MultiPolygon putter
+putMPoly :: SRID -> t3 (t2 (VU.Vector p)) -> Geo (MultiPolygon t3 t2 p)
+putMPoly srid psss = Geo (MultiPolygon srid psss)
+
+-- | MultiPolygon getter
+getMPoly :: Geo (MultiPolygon t3 t2 p) -> (SRID, t3 (t2 (VU.Vector p)))
+getMPoly (Geo (MultiPolygon srid vs)) = (srid, vs)
+
diff --git a/src/Database/Postgis/Trivial/Unboxed/PointND.hs b/src/Database/Postgis/Trivial/Unboxed/PointND.hs
new file mode 100644
--- /dev/null
+++ b/src/Database/Postgis/Trivial/Unboxed/PointND.hs
@@ -0,0 +1,315 @@
+{-# LANGUAGE TypeFamilies #-}
+{-# LANGUAGE MultiParamTypeClasses #-}
+
+module Database.Postgis.Trivial.Unboxed.PointND
+( P2DU (..)
+, P3DZU (..)
+, P3DMU (..)
+, P4DU (..)
+) where
+
+import GHC.Base
+import GHC.Show ( Show )
+import Control.Applicative ( (<$>) )
+import Data.Tuple ( uncurry )
+import Control.Exception ( throw )
+import qualified Data.Vector.Generic as VG
+import qualified Data.Vector.Generic.Mutable as VGM
+import qualified Data.Vector.Unboxed as VU
+
+import Database.Postgis.Trivial.Types
+import Database.Postgis.Trivial.Cast
+
+
+uncurry3 :: (a -> b -> c -> d) -> (a, b, c) -> d
+uncurry3 f (a, b, c) = f a b c
+
+uncurry4 :: (a -> b -> c -> d -> e) -> (a, b, c, d) -> e
+uncurry4 f (a, b, c, d) = f a b c d
+
+-- P2D =========================================================================
+
+-- | Default Unbox 2D point
+data P2DU = Point2DU
+        {-# UNPACK #-} !Double
+        {-# UNPACK #-} !Double
+        deriving (Show, Eq)
+
+fromP2D :: P2DU -> (Double, Double)
+fromP2D (Point2DU x y) = (x, y)
+
+newtype instance VU.MVector s P2DU = MV_P2D (VU.MVector s (Double, Double))
+newtype instance VU.Vector    P2DU = V_P2D  (VU.Vector    (Double, Double))
+
+instance VGM.MVector VU.MVector P2DU where
+    {-# INLINE basicLength #-}
+    {-# INLINE basicUnsafeSlice #-}
+    {-# INLINE basicOverlaps #-}
+    {-# INLINE basicUnsafeNew #-}
+    {-# INLINE basicInitialize #-}
+    {-# INLINE basicUnsafeReplicate #-}
+    {-# INLINE basicUnsafeRead #-}
+    {-# INLINE basicUnsafeWrite #-}
+    {-# INLINE basicClear #-}
+    {-# INLINE basicSet #-}
+    {-# INLINE basicUnsafeCopy #-}
+    {-# INLINE basicUnsafeGrow #-}
+    basicLength (MV_P2D v) = VGM.basicLength v
+    basicUnsafeNew n = MV_P2D <$> VGM.basicUnsafeNew n
+    basicUnsafeSlice i n (MV_P2D v) = MV_P2D $ VGM.basicUnsafeSlice i n v
+    basicOverlaps (MV_P2D v1) (MV_P2D v2) = VGM.basicOverlaps v1 v2
+    basicInitialize (MV_P2D v) = VGM.basicInitialize v
+    basicUnsafeReplicate n p = MV_P2D <$> VGM.basicUnsafeReplicate n (fromP2D p)
+    basicUnsafeRead (MV_P2D v) i = uncurry Point2DU <$> VGM.basicUnsafeRead v i
+    basicUnsafeWrite (MV_P2D v) i p = VGM.basicUnsafeWrite v i (fromP2D p)
+    basicClear (MV_P2D v) = VGM.basicClear v
+    basicSet (MV_P2D v) p = VGM.basicSet v (fromP2D p)
+    basicUnsafeCopy (MV_P2D v1) (MV_P2D v2) = VGM.basicUnsafeCopy v1 v2
+    basicUnsafeMove (MV_P2D v1) (MV_P2D v2) = VGM.basicUnsafeMove v1 v2
+    basicUnsafeGrow (MV_P2D v) n = MV_P2D <$> VGM.basicUnsafeGrow v n
+
+instance VG.Vector VU.Vector P2DU where
+    {-# INLINE basicLength #-}
+    {-# INLINE basicUnsafeFreeze #-}
+    {-# INLINE basicUnsafeThaw #-}
+    {-# INLINE basicUnsafeSlice #-}
+    {-# INLINE basicUnsafeIndexM #-}
+    basicLength (V_P2D v) = VG.basicLength v
+    basicUnsafeFreeze (MV_P2D v) = V_P2D <$> VG.basicUnsafeFreeze v
+    basicUnsafeThaw (V_P2D v) = MV_P2D <$> VG.basicUnsafeThaw v
+    basicUnsafeSlice i n (V_P2D v) = V_P2D $ VG.basicUnsafeSlice i n v
+    basicUnsafeIndexM (V_P2D v) i = uncurry Point2DU <$> VG.basicUnsafeIndexM v i
+    basicUnsafeCopy (MV_P2D mv) (V_P2D v) = VG.basicUnsafeCopy mv v
+
+instance VU.Unbox P2DU
+
+instance PointND P2DU where
+    dimProps = (False, False)
+    components (Point2DU x y) = (x, y, Nothing, Nothing)
+    fromComponents (x, y, Nothing, Nothing) = Point2DU x y
+    fromComponents _ = throw $
+        GeometryError "invalid transition from user data type to P2D"
+
+-- P3DZ ========================================================================
+
+-- | Default Unbox 3D point with Z component
+data P3DZU = Point3DZU
+        {-# UNPACK #-} !Double
+        {-# UNPACK #-} !Double
+        {-# UNPACK #-} !Double
+        deriving (Show, Eq)
+
+fromP3DZ :: P3DZU -> (Double, Double, Double)
+fromP3DZ (Point3DZU x y z) = (x, y, z)
+
+newtype instance VU.MVector s P3DZU =
+    MV_P3DZ (VU.MVector s (Double, Double, Double))
+newtype instance VU.Vector    P3DZU =
+    V_P3DZ  (VU.Vector    (Double, Double, Double))
+
+instance VGM.MVector VU.MVector P3DZU where
+    {-# INLINE basicLength #-}
+    {-# INLINE basicUnsafeSlice #-}
+    {-# INLINE basicOverlaps #-}
+    {-# INLINE basicUnsafeNew #-}
+    {-# INLINE basicInitialize #-}
+    {-# INLINE basicUnsafeReplicate #-}
+    {-# INLINE basicUnsafeRead #-}
+    {-# INLINE basicUnsafeWrite #-}
+    {-# INLINE basicClear #-}
+    {-# INLINE basicSet #-}
+    {-# INLINE basicUnsafeCopy #-}
+    {-# INLINE basicUnsafeGrow #-}
+    basicLength (MV_P3DZ v) = VGM.basicLength v
+    basicUnsafeNew n = MV_P3DZ <$> VGM.basicUnsafeNew n
+    basicUnsafeSlice i n (MV_P3DZ v) = MV_P3DZ $ VGM.basicUnsafeSlice i n v
+    basicOverlaps (MV_P3DZ v1) (MV_P3DZ v2) = VGM.basicOverlaps v1 v2
+    basicInitialize (MV_P3DZ v) = VGM.basicInitialize v
+    basicUnsafeReplicate n p = MV_P3DZ <$> VGM.basicUnsafeReplicate n (fromP3DZ p)
+    basicUnsafeRead (MV_P3DZ v) i = uncurry3 Point3DZU <$> VGM.basicUnsafeRead v i
+    basicUnsafeWrite (MV_P3DZ v) i p = VGM.basicUnsafeWrite v i (fromP3DZ p)
+    basicClear (MV_P3DZ v) = VGM.basicClear v
+    basicSet (MV_P3DZ v) p = VGM.basicSet v (fromP3DZ p)
+    basicUnsafeCopy (MV_P3DZ v1) (MV_P3DZ v2) = VGM.basicUnsafeCopy v1 v2
+    basicUnsafeMove (MV_P3DZ v1) (MV_P3DZ v2) = VGM.basicUnsafeMove v1 v2
+    basicUnsafeGrow (MV_P3DZ v) n = MV_P3DZ <$> VGM.basicUnsafeGrow v n
+
+instance VG.Vector VU.Vector P3DZU where
+    {-# INLINE basicLength #-}
+    {-# INLINE basicUnsafeFreeze #-}
+    {-# INLINE basicUnsafeThaw #-}
+    {-# INLINE basicUnsafeSlice #-}
+    {-# INLINE basicUnsafeIndexM #-}
+    basicLength (V_P3DZ v) = VG.basicLength v
+    basicUnsafeFreeze (MV_P3DZ v) = V_P3DZ <$> VG.basicUnsafeFreeze v
+    basicUnsafeThaw (V_P3DZ v) = MV_P3DZ <$> VG.basicUnsafeThaw v
+    basicUnsafeSlice i n (V_P3DZ v) = V_P3DZ $ VG.basicUnsafeSlice i n v
+    basicUnsafeIndexM (V_P3DZ v) i = uncurry3 Point3DZU <$> VG.basicUnsafeIndexM v i
+    basicUnsafeCopy (MV_P3DZ mv) (V_P3DZ v) = VG.basicUnsafeCopy mv v
+
+instance VU.Unbox P3DZU
+
+instance PointND P3DZU where
+    dimProps = (False, True)
+    components (Point3DZU x y z) = (x, y, Just z, Nothing)
+    fromComponents (x, y, Just z, Nothing) = Point3DZU x y z
+    fromComponents _ = throw $
+        GeometryError "invalid transition from user data type to P3DZ"
+
+-- P3DM ========================================================================
+
+-- | Default Unbox 3D point with M component
+data P3DMU = Point3DMU
+        {-# UNPACK #-} !Double
+        {-# UNPACK #-} !Double
+        {-# UNPACK #-} !Double
+        deriving (Show, Eq)
+
+fromP3DM :: P3DMU -> (Double, Double, Double)
+fromP3DM (Point3DMU x y z) = (x, y, z)
+
+newtype instance VU.MVector s P3DMU =
+    MV_P3DM (VU.MVector s (Double, Double, Double))
+newtype instance VU.Vector    P3DMU =
+    V_P3DM  (VU.Vector    (Double, Double, Double))
+
+instance VGM.MVector VU.MVector P3DMU where
+    {-# INLINE basicLength #-}
+    {-# INLINE basicUnsafeSlice #-}
+    {-# INLINE basicOverlaps #-}
+    {-# INLINE basicUnsafeNew #-}
+    {-# INLINE basicInitialize #-}
+    {-# INLINE basicUnsafeReplicate #-}
+    {-# INLINE basicUnsafeRead #-}
+    {-# INLINE basicUnsafeWrite #-}
+    {-# INLINE basicClear #-}
+    {-# INLINE basicSet #-}
+    {-# INLINE basicUnsafeCopy #-}
+    {-# INLINE basicUnsafeGrow #-}
+    basicLength (MV_P3DM v) = VGM.basicLength v
+    basicUnsafeNew n = MV_P3DM <$> VGM.basicUnsafeNew n
+    basicUnsafeSlice i n (MV_P3DM v) = MV_P3DM $ VGM.basicUnsafeSlice i n v
+    basicOverlaps (MV_P3DM v1) (MV_P3DM v2) = VGM.basicOverlaps v1 v2
+    basicInitialize (MV_P3DM v) = VGM.basicInitialize v
+    basicUnsafeReplicate n p = MV_P3DM <$> VGM.basicUnsafeReplicate n (fromP3DM p)
+    basicUnsafeRead (MV_P3DM v) i = uncurry3 Point3DMU <$> VGM.basicUnsafeRead v i
+    basicUnsafeWrite (MV_P3DM v) i p = VGM.basicUnsafeWrite v i (fromP3DM p)
+    basicClear (MV_P3DM v) = VGM.basicClear v
+    basicSet (MV_P3DM v) p = VGM.basicSet v (fromP3DM p)
+    basicUnsafeCopy (MV_P3DM v1) (MV_P3DM v2) = VGM.basicUnsafeCopy v1 v2
+    basicUnsafeMove (MV_P3DM v1) (MV_P3DM v2) = VGM.basicUnsafeMove v1 v2
+    basicUnsafeGrow (MV_P3DM v) n = MV_P3DM <$> VGM.basicUnsafeGrow v n
+
+instance VG.Vector VU.Vector P3DMU where
+    {-# INLINE basicLength #-}
+    {-# INLINE basicUnsafeFreeze #-}
+    {-# INLINE basicUnsafeThaw #-}
+    {-# INLINE basicUnsafeSlice #-}
+    {-# INLINE basicUnsafeIndexM #-}
+    basicLength (V_P3DM v) = VG.basicLength v
+    basicUnsafeFreeze (MV_P3DM v) = V_P3DM <$> VG.basicUnsafeFreeze v
+    basicUnsafeThaw (V_P3DM v) = MV_P3DM <$> VG.basicUnsafeThaw v
+    basicUnsafeSlice i n (V_P3DM v) = V_P3DM $ VG.basicUnsafeSlice i n v
+    basicUnsafeIndexM (V_P3DM v) i = uncurry3 Point3DMU <$> VG.basicUnsafeIndexM v i
+    basicUnsafeCopy (MV_P3DM mv) (V_P3DM v) = VG.basicUnsafeCopy mv v
+
+instance VU.Unbox P3DMU
+
+instance PointND P3DMU where
+    dimProps = (True, False)
+    components (Point3DMU x y m) = (x, y, Just m, Nothing)
+    fromComponents (x, y, Just m, Nothing) = Point3DMU x y m
+    fromComponents _ = throw $
+        GeometryError "invalid transition from user data type to P3DM"
+
+-- P4D =========================================================================
+
+-- | Default Unbox point with Z and M component
+data P4DU = Point4DU
+        {-# UNPACK #-} !Double
+        {-# UNPACK #-} !Double
+        {-# UNPACK #-} !Double
+        {-# UNPACK #-} !Double
+        deriving (Show, Eq)
+
+fromP4D :: P4DU -> (Double, Double, Double, Double)
+fromP4D (Point4DU x y z m) = (x, y, z, m)
+
+newtype instance VU.MVector s P4DU =
+    MV_P4D (VU.MVector s (Double, Double, Double, Double))
+newtype instance VU.Vector    P4DU =
+    V_P4D  (VU.Vector    (Double, Double, Double, Double))
+
+instance VGM.MVector VU.MVector P4DU where
+    {-# INLINE basicLength #-}
+    {-# INLINE basicUnsafeSlice #-}
+    {-# INLINE basicOverlaps #-}
+    {-# INLINE basicUnsafeNew #-}
+    {-# INLINE basicInitialize #-}
+    {-# INLINE basicUnsafeReplicate #-}
+    {-# INLINE basicUnsafeRead #-}
+    {-# INLINE basicUnsafeWrite #-}
+    {-# INLINE basicClear #-}
+    {-# INLINE basicSet #-}
+    {-# INLINE basicUnsafeCopy #-}
+    {-# INLINE basicUnsafeGrow #-}
+    basicLength (MV_P4D v) = VGM.basicLength v
+    basicUnsafeNew n = MV_P4D <$> VGM.basicUnsafeNew n
+    basicUnsafeSlice i n (MV_P4D v) = MV_P4D $ VGM.basicUnsafeSlice i n v
+    basicOverlaps (MV_P4D v1) (MV_P4D v2) = VGM.basicOverlaps v1 v2
+    basicInitialize (MV_P4D v) = VGM.basicInitialize v
+    basicUnsafeReplicate n p = MV_P4D <$> VGM.basicUnsafeReplicate n (fromP4D p)
+    basicUnsafeRead (MV_P4D v) i = uncurry4 Point4DU <$> VGM.basicUnsafeRead v i
+    basicUnsafeWrite (MV_P4D v) i p = VGM.basicUnsafeWrite v i (fromP4D p)
+    basicClear (MV_P4D v) = VGM.basicClear v
+    basicSet (MV_P4D v) p = VGM.basicSet v (fromP4D p)
+    basicUnsafeCopy (MV_P4D v1) (MV_P4D v2) = VGM.basicUnsafeCopy v1 v2
+    basicUnsafeMove (MV_P4D v1) (MV_P4D v2) = VGM.basicUnsafeMove v1 v2
+    basicUnsafeGrow (MV_P4D v) n = MV_P4D <$> VGM.basicUnsafeGrow v n
+
+instance VG.Vector VU.Vector P4DU where
+    {-# INLINE basicLength #-}
+    {-# INLINE basicUnsafeFreeze #-}
+    {-# INLINE basicUnsafeThaw #-}
+    {-# INLINE basicUnsafeSlice #-}
+    {-# INLINE basicUnsafeIndexM #-}
+    basicLength (V_P4D v) = VG.basicLength v
+    basicUnsafeFreeze (MV_P4D v) = V_P4D <$> VG.basicUnsafeFreeze v
+    basicUnsafeThaw (V_P4D v) = MV_P4D <$> VG.basicUnsafeThaw v
+    basicUnsafeSlice i n (V_P4D v) = V_P4D $ VG.basicUnsafeSlice i n v
+    basicUnsafeIndexM (V_P4D v) i = uncurry4 Point4DU <$> VG.basicUnsafeIndexM v i
+    basicUnsafeCopy (MV_P4D mv) (V_P4D v) = VG.basicUnsafeCopy mv v
+
+instance VU.Unbox P4DU
+
+instance PointND P4DU where
+    dimProps = (True, True)
+    components (Point4DU x y z m) = (x, y, Just z, Just m)
+    fromComponents (x, y, Just z, Just m) = Point4DU x y z m
+    fromComponents _ = throw $
+        GeometryError "invalid transition from user data type to P4D"
+
+-- Cast ========================================================================
+
+type instance Cast P2DU = P2DU
+type instance Cast P3DZU = P3DZU
+type instance Cast P3DMU = P3DMU
+type instance Cast P4DU = P4DU
+
+instance Castable P2DU where
+    toPointND = coerce
+    fromPointND = coerce
+instance Castable P3DZU where
+    toPointND = coerce
+    fromPointND = coerce
+instance Castable P3DMU where
+    toPointND = coerce
+    fromPointND = coerce
+instance Castable P4DU where
+    toPointND = coerce
+    fromPointND = coerce
+
+
+
+
+
diff --git a/test/Spec.hs b/test/Spec.hs
new file mode 100644
--- /dev/null
+++ b/test/Spec.hs
@@ -0,0 +1,73 @@
+-----------------------------------------------------------------------------
+--
+-- Testing database can be created as following:
+--      createdb --owner=... --template=template0 test "a postgis testing db"
+-- After login as database owner execute next SQLs:
+--      CREATE EXTENSION IF NOT EXISTS postgis;
+--      CREATE TABLE IF NOT EXISTS points           (geom     geometry(POINT, 3785));
+--      CREATE TABLE IF NOT EXISTS linestrings      (geom     geometry(LINESTRING, 3785));
+--      CREATE TABLE IF NOT EXISTS linestringZs     (geom     geometry(LINESTRINGZ, 3785));
+--      CREATE TABLE IF NOT EXISTS polygons         (geom     geometry(POLYGON, 3785));
+--      CREATE TABLE IF NOT EXISTS multipoints      (geom     geometry(MULTIPOINT, 3785));
+--      CREATE TABLE IF NOT EXISTS multilinestrings (geom     geometry(MULTILINESTRING, 3785));
+--      CREATE TABLE IF NOT EXISTS multipolygons    (geom     geometry(MULTIPOLYGON, 3785));
+-- In some cases, you may also need to change `dbconn` function below and/or
+-- to set necessary privileges for user and tables
+--
+-----------------------------------------------------------------------------
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+
+import GHC.Base
+import GHC.Show ( Show(show) )
+import System.IO ( putStrLn )
+import System.IO.Error ( userError )
+import System.Exit ( exitFailure, exitSuccess )
+import Control.Exception
+import Data.ByteString ( ByteString )
+import Database.PostgreSQL.Simple
+import Test.HUnit
+import qualified Tests.Traversable as T
+import qualified Tests.Unboxed as U
+import qualified Tests.Storable as S
+
+
+dbconn :: ByteString
+dbconn = "dbname=test"
+
+checkdb :: IO ()
+checkdb = bracket
+    (connectPostgreSQL dbconn) close
+    (\conn -> do
+        [Only res] <- catch (query_ conn "SELECT PostGIS_version()")
+            (\(e::SomeException) -> ioError (userError (show e)))
+        if res/=(""::ByteString)
+            then return ()
+            else ioError (userError "No PostGIS detected")
+    )
+
+tests :: Test
+tests = TestList
+    [ TestLabel "Traversable-InsSel" $ T.tInsSel dbconn
+    , TestLabel "Unboxed-Mutability" U.tMutability
+    , TestLabel "Unboxed-InsSel" $ U.tInsSel dbconn
+    , TestLabel "Storable-Mutability" S.tMutability
+    , TestLabel "Storable-InsSel" $ S.tInsSel dbconn
+    ]
+
+main :: IO ()
+main = do
+    catch checkdb
+        (\(e::IOException) -> do
+            putStrLn
+                ("\nSkipping tests:"++
+                "\n  "++show e++
+                "\n  (see Spec.hs header for test instructions)"
+                )
+            exitSuccess
+        )
+    cs <- runTestTT tests
+    when (errors cs>0 || failures cs>0)
+        exitFailure
+
+
diff --git a/test/Tests/Storable.hs b/test/Tests/Storable.hs
new file mode 100644
--- /dev/null
+++ b/test/Tests/Storable.hs
@@ -0,0 +1,151 @@
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE MultiParamTypeClasses #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+{-# LANGUAGE TypeFamilies #-}
+{-# OPTIONS_GHC -Wno-orphans #-}
+
+module Tests.Storable where
+
+import GHC.Base
+import GHC.Show ( Show(..) )
+import Foreign.Storable.Record as Store
+import Foreign.Storable ( Storable (..) )
+import Control.Exception ( bracket )
+import Data.List ( length )
+import qualified Data.Vector.Storable as VS
+import Test.HUnit
+import Database.PostgreSQL.Simple
+
+import Database.Postgis.Trivial.Storable
+import Data.ByteString (ByteString)
+
+
+-- type InnerData = VS.Vector P2D
+
+data GeoDuo = GeoDuo
+        { dx :: !Double
+        , dy :: !Double
+        } deriving (Show, Eq)
+
+storeGeoDuo :: Store.Dictionary GeoDuo
+storeGeoDuo = Store.run $
+    liftA2 GeoDuo
+        (Store.element dx)
+        (Store.element dy)
+
+instance Storable GeoDuo where
+   sizeOf = Store.sizeOf storeGeoDuo
+   alignment = Store.alignment storeGeoDuo
+   peek = Store.peek storeGeoDuo
+   poke = Store.poke storeGeoDuo
+
+type instance Cast GeoDuo = P2DS
+
+instance Castable GeoDuo where
+    toPointND (GeoDuo y x) = Point2DS x y
+    fromPointND (Point2DS x y) = GeoDuo y x
+
+type SomeData = VS.Vector GeoDuo
+
+
+data GeoTrio = GeoTrio
+        { tx :: !Double
+        , ty :: !Double
+        , tz :: !Double
+        } deriving (Show, Eq)
+
+storeGeoTrio :: Store.Dictionary GeoTrio
+storeGeoTrio = Store.run $
+    liftA3 GeoTrio
+        (Store.element tx)
+        (Store.element ty)
+        (Store.element tz)
+
+instance Storable GeoTrio where
+   sizeOf = Store.sizeOf storeGeoTrio
+   alignment = Store.alignment storeGeoTrio
+   peek = Store.peek storeGeoTrio
+   poke = Store.poke storeGeoTrio
+
+type instance Cast GeoTrio = P3DZS
+
+instance Castable GeoTrio where
+    toPointND (GeoTrio x y z) = Point3DZS x y z
+    fromPointND (Point3DZS x y z) = GeoTrio x y z
+
+type SomeDataZ = VS.Vector GeoTrio
+
+tMutability ::Test
+tMutability = TestList
+    [ "safe update" ~:
+        VS.fromList [Point2DS 1 2, Point2DS 1.5 2.5, Point2DS 2.5 3, Point2DS 1 2] VS.//
+            [(2, Point2DS 2 13), (0, Point2DS 10 2)]
+        ~?= VS.fromList [Point2DS 10 2, Point2DS 1.5 2.5, Point2DS 2 13, Point2DS 1 2]
+    , "unsafe update" ~:
+        VS.unsafeUpd
+            (VS.fromList [Point2DS 1 2, Point2DS 1.5 2.5, Point2DS 2.5 3, Point2DS 1 2])
+            [(2, Point2DS 2 13), (0, Point2DS 10 2)]
+        ~?= VS.fromList [Point2DS 10 2, Point2DS 1.5 2.5, Point2DS 2 13, Point2DS 1 2]
+    ]
+
+tInsSel :: ByteString -> Test
+tInsSel dbconn = TestList
+    [ "linestring (2D, Unboxed Vector)" ~: bracket
+        (connectPostgreSQL dbconn) close
+        (\conn -> do
+            let srid = Just 3785 :: SRID
+                ls0 = VS.fromList [Point2DS 1 2, Point2DS 1.5 2.5, Point2DS 2.5 3, Point2DS 1 2]
+            _ <- execute_ conn "TRUNCATE linestrings"
+            _ <- execute conn "INSERT INTO linestrings (geom) VALUES (?)"
+                (Only (putLS srid ls0))
+            [Only res] <- query_ conn "SELECT * FROM linestrings"
+            let (srid', ls0') = getLS res
+            _ <- execute_ conn "TRUNCATE linestrings"
+            VS.length ls0'==VS.length ls0 && srid'==srid && ls0'==ls0 @?= True
+        )
+    , "linestring (2D, Unboxed Vector)" ~: bracket
+        (connectPostgreSQL dbconn) close
+        (\conn -> do
+            let srid = Just 3785 :: SRID
+                ls0 = VS.fromList [GeoDuo 1 2, GeoDuo 1.5 2.5, GeoDuo 2.5 3, GeoDuo 1 2]
+            _ <- execute_ conn "TRUNCATE linestrings"
+            _ <- execute conn "INSERT INTO linestrings (geom) VALUES (?)"
+                (Only (putLS srid ls0))
+            [Only res] <- query_ conn "SELECT * FROM linestrings"
+            let (srid', ls0') = getLS res
+            _ <- execute_ conn "TRUNCATE linestrings"
+            VS.length ls0'==VS.length ls0 && srid'==srid && ls0'==ls0 @?= True
+        )
+    , "linestring (3D, Unboxed Vector)" ~: bracket
+        (connectPostgreSQL dbconn) close
+        (\conn -> do
+            let srid = Just 3785 :: SRID
+                ls0 = VS.fromList [GeoTrio 1 2 0, GeoTrio 1.5 2.5 10, GeoTrio 2.5 3 20, GeoTrio 1 2 30]
+            _ <- execute_ conn "TRUNCATE linestringZs"
+            _ <- execute conn "INSERT INTO linestringZs (geom) VALUES (?)"
+                (Only (putLS srid ls0))
+            [Only res] <- query_ conn "SELECT * FROM linestringZs"
+            let (srid', ls0') = getLS res
+            _ <- execute_ conn "TRUNCATE linestringZs"
+            VS.length ls0'==VS.length ls0 && srid'==srid && ls0'==ls0 @?= True
+        )
+    , "multilinestring" ~: bracket
+        (connectPostgreSQL dbconn) close
+        (\conn -> do
+            let srid = Just 3785 :: SRID
+                mls0 =
+                    [ VS.fromList [GeoDuo 1 2, GeoDuo 1.5 2.5, GeoDuo 2.5 3, GeoDuo 1 2]
+                    , VS.fromList []
+                    , VS.fromList [GeoDuo 1 2] ]
+            _ <- execute_ conn "TRUNCATE multilinestrings"
+            _ <- execute conn "INSERT INTO multilinestrings (geom) VALUES (?)"
+                (Only (putMLS srid mls0))
+            [Only res] <- query_ conn "SELECT * FROM multilinestrings"
+            let (srid', mls0'::[SomeData]) = getMLS res
+            _ <- execute_ conn "TRUNCATE multilinestrings"
+            length mls0'==length mls0 && srid'==srid && mls0'==mls0 @?= True
+        )
+    ]
+
+
+
diff --git a/test/Tests/Traversable.hs b/test/Tests/Traversable.hs
new file mode 100644
--- /dev/null
+++ b/test/Tests/Traversable.hs
@@ -0,0 +1,186 @@
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE MultiParamTypeClasses #-}
+{-# LANGUAGE TypeFamilies #-}
+{-# LANGUAGE FlexibleInstances #-}
+{-# OPTIONS_GHC -Wno-orphans #-}
+
+module Tests.Traversable where
+
+import GHC.Base
+import GHC.Show ( Show(..) )
+import Control.Applicative ( (<$>) )
+import Control.Exception ( bracket )
+import Data.List
+import qualified Data.Vector as V
+import Test.HUnit
+import Database.PostgreSQL.Simple
+
+import Database.Postgis.Trivial
+import Data.ByteString (ByteString)
+
+
+data LatLon =
+        LatLon {-# UNPACK #-} !Double {-# UNPACK #-} !Double
+        deriving (Show, Eq)
+
+type instance Cast LatLon = P2D
+
+instance Castable LatLon where
+    toPointND (LatLon y x) = Point2D x y
+    fromPointND (Point2D x y) = LatLon y x
+
+data LatLonZ =
+        LatLonZ {-# UNPACK #-} !Double {-# UNPACK #-} !Double {-# UNPACK #-} !Double
+        deriving (Show, Eq)
+
+type instance Cast LatLonZ = P3DZ
+
+instance Castable LatLonZ where
+    toPointND (LatLonZ y x z) = Point3DZ x y z
+    fromPointND (Point3DZ x y z) = LatLonZ y x z
+
+type instance Cast (Double, Double) = P2D
+
+instance Castable (Double, Double) where
+    toPointND (x, y) = Point2D x y
+    fromPointND (Point2D x y) = (x, y)
+
+tInsSel :: ByteString -> Test
+tInsSel dbconn = TestList
+    [ "geometry point (PointND)" ~: bracket
+        (connectPostgreSQL dbconn) close
+        (\conn -> do
+            let srid = Just 3785 :: SRID
+                point0 = Point2D 1 2
+            _ <- execute_ conn "TRUNCATE points"
+            _ <- execute conn "INSERT INTO points (geom) VALUES (?)"
+                (Only (putPoint srid point0))
+            [Only res] <- query_ conn "SELECT * FROM points"
+            let (srid', point0') = getPoint res
+            (point0', srid') @?= (point0, srid)
+        )
+    , "geometry point (Pair)" ~: bracket
+        (connectPostgreSQL dbconn) close
+        (\conn -> do
+            let srid = Just 3785 :: SRID
+                point0 = (1, 2) :: (Double, Double)
+            _ <- execute_ conn "TRUNCATE points"
+            _ <- execute conn "INSERT INTO points (geom) VALUES (?)"
+                (Only (putPoint srid point0))
+            [Only res] <- query_ conn "SELECT * FROM points"
+            let (srid', point0') = getPoint res
+            (point0', srid') @?= (point0, srid)
+        )
+    , "geometry points ([LatLon, LatLon])" ~: bracket
+        (connectPostgreSQL dbconn) close
+        (\conn -> do
+            let srid = Just 3785 :: SRID
+                point0 = LatLon 1 2 :: LatLon
+                point1 = LatLon 11 2 :: LatLon
+            _ <- execute_ conn "TRUNCATE points"
+            _ <- executeMany conn "INSERT INTO points (geom) VALUES (?)"
+                (Only <$> [putPoint srid point0, putPoint srid point1])
+            res <- map (getPoint . fromOnly) <$> query_ conn "SELECT * FROM points"
+            _ <- execute_ conn "TRUNCATE points"
+            res @?= [(srid, point0), (srid, point1)]
+        )
+    , "linestring (3D, Vector)" ~: bracket
+        (connectPostgreSQL dbconn) close
+        (\conn -> do
+            let srid = Just 3785 :: SRID
+                ls0 = V.fromList [Point3DZ 1 2 0, Point3DZ 1.5 2.5 10, Point3DZ 2.5 3 20, Point3DZ 1 2 30]
+            _ <- execute_ conn "TRUNCATE linestringZs"
+            _ <- execute conn "INSERT INTO linestringZs (geom) VALUES (?)"
+                (Only (putLS srid ls0))
+            [Only res] <- query_ conn "SELECT * FROM linestringZs"
+            let (srid', ls0') = getLS res
+            _ <- execute_ conn "TRUNCATE linestringZs"
+            length ls0'==length ls0 && srid'==srid && all (==True) (V.zipWith (==) ls0' ls0) @?= True
+        )
+    , "linestring (3D, Vector), without helpers" ~: bracket
+        (connectPostgreSQL dbconn) close
+        (\conn -> do
+            let srid = Just 3785 :: SRID
+                ls0 = V.fromList [Point3DZ 1 2 0, Point3DZ 1.5 2.5 10, Point3DZ 2.5 3 20, Point3DZ 1 2 30]
+            _ <- execute_ conn "TRUNCATE linestringZs"
+            _ <- execute conn "INSERT INTO linestringZs (geom) VALUES (?)"
+                (Only (Geo (LineString srid ls0)))
+            [Only res] <- query_ conn "SELECT * FROM linestringZs"
+            let Geo (LineString srid' ls0') = res
+            _ <- execute_ conn "TRUNCATE linestringZs"
+            length ls0'==length ls0 && srid'==srid && all (==True) (V.zipWith (==) ls0' ls0) @?= True
+        )
+    , "linestring (empty)" ~: bracket
+        (connectPostgreSQL dbconn) close
+        (\conn -> do
+            let srid = Just 3785 :: SRID
+                ls0 = [] :: [LatLonZ]
+            _ <- execute_ conn "TRUNCATE linestringZs"
+            _ <- execute conn "INSERT INTO linestringZs (geom) VALUES (?)"
+                (Only (putLS srid ls0))
+            [Only res] <- query_ conn "SELECT * FROM linestringZs LIMIT 1"
+            let (srid', ls0') = getLS res
+            _ <- execute_ conn "TRUNCATE linestringZs"
+            length ls0'==length ls0 && srid'==srid && all (==True) (zipWith (==) ls0' ls0) @?= True
+        )
+    , "polygon" ~: bracket
+        (connectPostgreSQL dbconn) close
+        (\conn -> do
+            let srid = Just 3785 :: SRID
+                polygon0 = [[Point2D 1 2, Point2D 1.5 2.5, Point2D 2.5 3, Point2D 1 2]]
+            _ <- execute_ conn "TRUNCATE polygons"
+            _ <- execute conn "INSERT INTO polygons (geom) VALUES (?)"
+                (Only (putPoly srid polygon0))
+            [Only res] <- query_ conn "SELECT * FROM polygons"
+            let (srid', polygon0') = getPoly res
+            _ <- execute_ conn "TRUNCATE polygons"
+            not (null polygon0')
+                && length (head polygon0')==length (head polygon0)
+                && srid'==srid
+                && all (==True) (zipWith (==) (head polygon0') (head polygon0)) @?= True
+        )
+    , "multipoint" ~: bracket
+        (connectPostgreSQL dbconn) close
+        (\conn -> do
+            let srid = Just 3785 :: SRID
+                mp0 = [Point2D 1 2, Point2D 1.5 2.5, Point2D 2.5 3, Point2D 1 2]
+            _ <- execute_ conn "TRUNCATE multipoints"
+            _ <- execute conn "INSERT INTO multipoints (geom) VALUES (?)"
+                (Only (putMPoint srid mp0))
+            [Only res] <- query_ conn "SELECT * FROM multipoints"
+            let (srid', mp0') = getMPoint res
+            _ <- execute_ conn "TRUNCATE multipoints"
+            length mp0'==length mp0 && srid'==srid && all (==True) (zipWith (==) mp0' mp0) @?= True
+        )
+    , "multilinestring" ~: bracket
+        (connectPostgreSQL dbconn) close
+        (\conn -> do
+            let srid = Just 3785 :: SRID
+                mls0 = [[Point2D 1 2, Point2D 1.5 2.5, Point2D 2.5 3, Point2D 1 2], [], [Point2D 1 2]]
+            _ <- execute_ conn "TRUNCATE multilinestrings"
+            _ <- execute conn "INSERT INTO multilinestrings (geom) VALUES (?)"
+                (Only (putMLS srid mls0))
+            [Only res] <- query_ conn "SELECT * FROM multilinestrings"
+            let (srid', mls0') = getMLS res
+            _ <- execute_ conn "TRUNCATE multilinestrings"
+            length mls0'==length mls0 && srid'==srid && all (==True) (zipWith (==) mls0' mls0) @?= True
+        )
+    , "multipolygon" ~: bracket
+        (connectPostgreSQL dbconn) close
+        (\conn -> do
+            let srid = Just 3785 :: SRID
+                mps0 = [[[Point2D 1 2, Point2D 1.5 2.5, Point2D 2.5 3, Point2D 1 2], []], [[Point2D 1 2]]]
+            _ <- execute_ conn "TRUNCATE multipolygons"
+            _ <- execute conn "INSERT INTO multipolygons (geom) VALUES (?)"
+                (Only (putMPoly srid mps0))
+            [Only res] <- query_ conn "SELECT * FROM multipolygons"
+            let (srid', mps0') = getMPoly res
+            _ <- execute_ conn "TRUNCATE multipolygons"
+            length mps0'==length mps0 && srid'==srid && all (==True) (zipWith (==) mps0' mps0) @?= True
+        )
+    ]
+    -- ] where
+        -- dbconn = "dbname=test"
+
+
+
diff --git a/test/Tests/Unboxed.hs b/test/Tests/Unboxed.hs
new file mode 100644
--- /dev/null
+++ b/test/Tests/Unboxed.hs
@@ -0,0 +1,103 @@
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE MultiParamTypeClasses #-}
+{-# LANGUAGE FlexibleInstances #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+{-# LANGUAGE TypeFamilies #-}
+{-# OPTIONS_GHC -Wno-orphans #-}
+
+module Tests.Unboxed where
+
+import GHC.Base
+import Control.Exception ( bracket )
+import Data.List
+import qualified Data.Vector.Unboxed as VU
+import Test.HUnit
+import Database.PostgreSQL.Simple
+
+import Database.Postgis.Trivial.Unboxed
+import Data.ByteString (ByteString)
+
+-- type InnerData = VU.Vector P2D
+
+type instance Cast (Double, Double, Double) = P3DZU
+instance Castable (Double, Double, Double) where
+    toPointND (x, y, z) = Point3DZU x y z
+    fromPointND (Point3DZU x y z) = (x, y, z)
+type SomeDataZ = VU.Vector (Double, Double, Double)
+
+tMutability :: Test
+tMutability = TestList
+    [ "safe update" ~:
+        VU.fromList [Point2DU 1 2, Point2DU 1.5 2.5, Point2DU 2.5 3, Point2DU 1 2] VU.//
+            [(2, Point2DU 2 13), (0, Point2DU 10 2)]
+        ~?= VU.fromList [Point2DU 10 2, Point2DU 1.5 2.5, Point2DU 2 13, Point2DU 1 2]
+    , "unsafe update" ~:
+        VU.unsafeUpd
+            (VU.fromList [Point2DU 1 2, Point2DU 1.5 2.5, Point2DU 2.5 3, Point2DU 1 2])
+            [(2, Point2DU 2 13), (0, Point2DU 10 2)]
+        ~?= VU.fromList [Point2DU 10 2, Point2DU 1.5 2.5, Point2DU 2 13, Point2DU 1 2]
+    ]
+
+tInsSel :: ByteString -> Test
+tInsSel dbconn = TestList
+    [ "linestring (2D, Unboxed Vector)" ~: bracket
+        (connectPostgreSQL dbconn) close
+        (\conn -> do
+            let srid = Just 3785 :: SRID
+                ls0 = VU.fromList [Point2DU 1 2, Point2DU 1.5 2.5, Point2DU 2.5 3, Point2DU 1 2]
+            _ <- execute_ conn "TRUNCATE linestrings"
+            _ <- execute conn "INSERT INTO linestrings (geom) VALUES (?)"
+                (Only (putLS srid ls0))
+            -- return True
+            [Only res] <- query_ conn "SELECT * FROM linestrings"
+            let (srid', ls0') = getLS res
+            _ <- execute_ conn "TRUNCATE linestrings"
+            VU.length ls0'==VU.length ls0 && srid'==srid && ls0'==ls0 @?= True
+        )
+    , "linestring (2D, Unboxed Vector)" ~: bracket
+        (connectPostgreSQL dbconn) close
+        (\conn -> do
+            let srid = Just 3785 :: SRID
+                ls0 = VU.fromList [Point2DU 1 2, Point2DU 1.5 2.5, Point2DU 2.5 3, Point2DU 1 2]
+            _ <- execute_ conn "TRUNCATE linestrings"
+            _ <- execute conn "INSERT INTO linestrings (geom) VALUES (?)"
+                (Only (putLS srid ls0))
+            -- return True
+            [Only res] <- query_ conn "SELECT * FROM linestrings"
+            let (srid', ls0') = getLS res
+            _ <- execute_ conn "TRUNCATE linestrings"
+            VU.length ls0'==VU.length ls0 && srid'==srid && ls0'==ls0 @?= True
+        )
+    , "linestring (3D, Unboxed Vector)" ~: bracket
+        (connectPostgreSQL dbconn) close
+        (\conn -> do
+            let srid = Just 3785 :: SRID
+                ls0 = VU.fromList [(1, 2, 0), (1.5, 2.5, 10), (2.5, 3, 20), (1, 2, 30)] :: SomeDataZ
+            _ <- execute_ conn "TRUNCATE linestringZs"
+            _ <- execute conn "INSERT INTO linestringZs (geom) VALUES (?)"
+                (Only (putLS srid ls0))
+            [Only res] <- query_ conn "SELECT * FROM linestringZs"
+            let (srid', ls0') = getLS res
+            _ <- execute_ conn "TRUNCATE linestringZs"
+            VU.length ls0'==VU.length ls0 && srid'==srid && ls0'==ls0 @?= True
+        )
+    , "multilinestring" ~: bracket
+        (connectPostgreSQL dbconn) close
+        (\conn -> do
+            let srid = Just 3785 :: SRID
+                mls0 =
+                    [ VU.fromList [Point2DU 1 2, Point2DU 1.5 2.5, Point2DU 2.5 3, Point2DU 1 2]
+                    , VU.fromList []
+                    , VU.fromList [Point2DU 1 2] ]
+            _ <- execute_ conn "TRUNCATE multilinestrings"
+            _ <- execute conn "INSERT INTO multilinestrings (geom) VALUES (?)"
+                (Only (putMLS srid mls0))
+            [Only res] <- query_ conn "SELECT * FROM multilinestrings"
+            let (srid', mls0') = getMLS res
+            _ <- execute_ conn "TRUNCATE multilinestrings"
+            length mls0'==length mls0 && srid'==srid && mls0'==mls0 @?= True
+        )
+    ]
+
+
+
