packages feed

haskell-postgis 0.1.0.2 → 0.2.0

raw patch · 6 files changed

+87/−63 lines, 6 filesdep +eitherPVP ok

version bump matches the API change (PVP)

Dependencies added: either

API changes (from Hackage documentation)

- Database.Postgis: data LineString
- Database.Postgis: data MultiLineString
- Database.Postgis: data MultiPoint
- Database.Postgis: data MultiPolygon
- Database.Postgis: data Point
- Database.Postgis: data Polygon
- Database.Postgis.Geometry: data LineString
- Database.Postgis.Geometry: data MultiLineString
- Database.Postgis.Geometry: data MultiPoint
- Database.Postgis.Geometry: data MultiPolygon
- Database.Postgis.Geometry: data Point
- Database.Postgis.Geometry: data Polygon
+ Database.Postgis: Position :: Double -> Double -> Maybe Double -> Maybe Double -> Position
+ Database.Postgis: data Position
+ Database.Postgis: newtype LineString
+ Database.Postgis: newtype MultiLineString
+ Database.Postgis: newtype MultiPoint
+ Database.Postgis: newtype MultiPolygon
+ Database.Postgis: newtype Point
+ Database.Postgis: newtype Polygon
+ Database.Postgis.Geometry: Position :: Double -> Double -> Maybe Double -> Maybe Double -> Position
+ Database.Postgis.Geometry: data Position
+ Database.Postgis.Geometry: instance Data.Data.Data Database.Postgis.Geometry.Position
+ Database.Postgis.Geometry: instance Database.Postgis.Geometry.EWKBGeometry Database.Postgis.Geometry.Position
+ Database.Postgis.Geometry: instance GHC.Classes.Eq Database.Postgis.Geometry.Position
+ Database.Postgis.Geometry: instance GHC.Show.Show Database.Postgis.Geometry.Position
+ Database.Postgis.Geometry: newtype LineString
+ Database.Postgis.Geometry: newtype MultiLineString
+ Database.Postgis.Geometry: newtype MultiPoint
+ Database.Postgis.Geometry: newtype MultiPolygon
+ Database.Postgis.Geometry: newtype Point
+ Database.Postgis.Geometry: newtype Polygon
+ Database.Postgis.JSON: instance Data.Aeson.Types.FromJSON.FromJSON Database.Postgis.Geometry.Position
+ Database.Postgis.JSON: instance Data.Aeson.Types.ToJSON.ToJSON Database.Postgis.Geometry.Position
+ Database.Postgis.Serialize: getPosition :: Getter Position
+ Database.Postgis.Serialize: putPosition :: Putter Position
- Database.Postgis: LineString :: Vector Point -> LineString
+ Database.Postgis: LineString :: Vector Position -> LineString
- Database.Postgis: MultiPoint :: Vector Point -> MultiPoint
+ Database.Postgis: MultiPoint :: Vector Position -> MultiPoint
- Database.Postgis: Point :: Double -> Double -> Maybe Double -> Maybe Double -> Point
+ Database.Postgis: Point :: Position -> Point
- Database.Postgis: [_m] :: Point -> Maybe Double
+ Database.Postgis: [_m] :: Position -> Maybe Double
- Database.Postgis: [_x] :: Point -> Double
+ Database.Postgis: [_x] :: Position -> Double
- Database.Postgis: [_y] :: Point -> Double
+ Database.Postgis: [_y] :: Position -> Double
- Database.Postgis: [_z] :: Point -> Maybe Double
+ Database.Postgis: [_z] :: Position -> Maybe Double
- Database.Postgis: type LinearRing = Vector Point
+ Database.Postgis: type LinearRing = Vector Position
- Database.Postgis.Geometry: LineString :: Vector Point -> LineString
+ Database.Postgis.Geometry: LineString :: Vector Position -> LineString
- Database.Postgis.Geometry: MultiPoint :: Vector Point -> MultiPoint
+ Database.Postgis.Geometry: MultiPoint :: Vector Position -> MultiPoint
- Database.Postgis.Geometry: Point :: Double -> Double -> Maybe Double -> Maybe Double -> Point
+ Database.Postgis.Geometry: Point :: Position -> Point
- Database.Postgis.Geometry: [_m] :: Point -> Maybe Double
+ Database.Postgis.Geometry: [_m] :: Position -> Maybe Double
- Database.Postgis.Geometry: [_x] :: Point -> Double
+ Database.Postgis.Geometry: [_x] :: Position -> Double
- Database.Postgis.Geometry: [_y] :: Point -> Double
+ Database.Postgis.Geometry: [_y] :: Position -> Double
- Database.Postgis.Geometry: [_z] :: Point -> Maybe Double
+ Database.Postgis.Geometry: [_z] :: Position -> Maybe Double
- Database.Postgis.Geometry: isClosed :: Vector Point -> Bool
+ Database.Postgis.Geometry: isClosed :: Vector Position -> Bool
- Database.Postgis.Geometry: type LinearRing = Vector Point
+ Database.Postgis.Geometry: type LinearRing = Vector Position
- Database.Postgis.Serialize: getSegment :: Getter (Vector Point)
+ Database.Postgis.Serialize: getSegment :: Getter (Vector Position)

Files

haskell-postgis.cabal view
@@ -2,7 +2,7 @@ -- documentation, see http://haskell.org/cabal/users-guide/  name:                haskell-postgis-version:             0.1.0.2+version:             0.2.0 synopsis:            A haskell library for PostGIS geometry types. description:         A collection of types and parsers to use with the PostGIS extesion to PostgreSQL. license:             MIT@@ -32,6 +32,7 @@       , placeholders        , aeson         , unordered-containers+      , either   hs-source-dirs: src        default-language:    Haskell2010   exposed-modules: 
src/Database/Postgis.hs view
@@ -8,6 +8,7 @@   ) where import Database.Postgis.Geometry (           SRID,+          Position(..),           Point(..),           LineString(..),           LinearRing,
src/Database/Postgis/Geometry.hs view
@@ -1,5 +1,6 @@ {-# LANGUAGE GADTs, TypeFamilies #-} {-# LANGUAGE DeriveDataTypeable #-}+{-# LANGUAGE GeneralisedNewtypeDeriving #-}  module Database.Postgis.Geometry where @@ -7,6 +8,7 @@ import Data.Word import Data.Data import Data.Typeable+import Data.Maybe  {-Linear rings—Rings are simple and closed, which means that linear rings may not self intersect.-} @@ -22,26 +24,27 @@   hasZ :: a -> Bool   geoType :: a -> Word32  --data Point = Point  {+data Position = Position {     _x :: Double   , _y :: Double   , _z :: Maybe Double   , _m :: Maybe Double } deriving (Data, Typeable, Show, Eq) -instance EWKBGeometry Point where-  hasM (Point x y z m) = m /= Nothing -  hasZ (Point x y z m) = z /= Nothing +newtype Point = Point Position deriving (Data, Typeable, Show, Eq, EWKBGeometry)++instance EWKBGeometry Position where+  hasM (Position x y z m) = isJust m+  hasZ (Position x y z m) = isJust z   geoType _ = 1 -type LinearRing = V.Vector Point+type LinearRing = V.Vector Position -isClosed :: V.Vector Point -> Bool+isClosed :: V.Vector Position -> Bool isClosed v = V.head v == V.last v  -data LineString = LineString (V.Vector Point) deriving (Data, Typeable, Show, Eq)+newtype LineString = LineString (V.Vector Position) deriving (Data, Typeable, Show, Eq)   instance EWKBGeometry LineString where@@ -49,7 +52,7 @@   hasZ (LineString ps) = hasZ . V.head $ ps   geoType _ = 2 -data Polygon = Polygon (V.Vector LinearRing) deriving (Data, Typeable, Show, Eq)+newtype Polygon = Polygon (V.Vector LinearRing) deriving (Data, Typeable, Show, Eq)  hasMLinearRing :: LinearRing -> Bool hasMLinearRing = hasM . V.head @@ -62,21 +65,21 @@   hasZ (Polygon ps) = hasZLinearRing . V.head $ ps   geoType _ = 3 -data MultiPoint = MultiPoint (V.Vector Point) deriving (Data, Typeable, Show, Eq)+newtype MultiPoint = MultiPoint (V.Vector Position) deriving (Data, Typeable, Show, Eq)  instance EWKBGeometry MultiPoint where   hasM (MultiPoint ps) = hasM . V.head $ ps   hasZ (MultiPoint ps) = hasZ . V.head $ ps   geoType _ = 4 -data MultiLineString = MultiLineString (V.Vector LineString) deriving (Data, Typeable, Show, Eq)+newtype MultiLineString = MultiLineString (V.Vector LineString) deriving (Data, Typeable, Show, Eq)  instance EWKBGeometry MultiLineString where   hasM (MultiLineString ps) = hasM . V.head $ ps   hasZ (MultiLineString ps) = hasZ . V.head $ ps   geoType _ = 5 -data MultiPolygon = MultiPolygon (V.Vector Polygon) deriving (Data, Typeable, Show, Eq)+newtype MultiPolygon = MultiPolygon (V.Vector Polygon) deriving (Data, Typeable, Show, Eq)  instance EWKBGeometry MultiPolygon where   hasM (MultiPolygon ps) = hasM . V.head $ ps
src/Database/Postgis/JSON.hs view
@@ -1,4 +1,3 @@-{-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ScopedTypeVariables #-} @@ -19,23 +18,36 @@ import Data.Vector ((!), (!?)) import qualified Data.HashMap.Lazy as HM import Data.Text.Read (decimal)+import Data.Either.Combinators (rightToMaybe) +instance ToJSON Position where+  toJSON (Position x y m z) = toJSON $ catMaybes [Just x, Just y, m, z] +instance FromJSON Position where+  parseJSON = withArray "Position" $ \v' -> do+    v <- mapM parseJSON v'+    return $ Position (v ! 0) (v ! 1) (v !? 2) (v !? 3)+ instance ToJSON Point where-  toJSON (Point x y m z) = toJSON $ catMaybes [Just x, Just y, m, z]+  toJSON (Point position) = object+    [ "type" .= ("Point" :: T.Text)+    , "coordinates" .= toJSON position+    ]  instance FromJSON Point where-  parseJSON = withArray "Point" $ \v' -> do-    v <- sequence $ fmap parseJSON v'-    return $ Point (v ! 0) (v ! 1) (v !? 2) (v !? 3)+  parseJSON = withObject "Point" $ \o -> do+    ("Point" :: T.Text) <- o .: "type"+    cs <- o .: "coordinates"+    pos <- parseJSON cs+    return $ Point pos  instance FromJSON LineString where   parseJSON = withObject "LineString" $ \o -> do     ("LineString" :: T.Text) <- o .: "type"     cs <- o .: "coordinates"-    vs <- sequence $ fmap parseJSON cs-    return $ LineString vs -    +    vs <- mapM parseJSON cs+    return $ LineString vs+ instance ToJSON LineString where   toJSON (LineString points) = object ["type" .= ("LineString" :: T.Text), "coordinates" .=  V.map toJSON points] @@ -48,7 +60,7 @@   parseJSON = withObject "Polygon" $ \o -> do     ("Polygon" :: T.Text) <- o .: "type"     ls <- o .: "coordinates"-    cs <- sequence $ fmap parseJSON ls+    cs <- mapM parseJSON ls     return $ Polygon cs  ---@@ -59,7 +71,7 @@   parseJSON = withObject "MultiPoint" $ \o -> do     ("MultiPoint" :: T.Text) <- o .: "type"     ls <- o .: "coordinates"-    cs <- sequence $ fmap parseJSON ls+    cs <- mapM parseJSON ls     return $ MultiPoint cs  instance ToJSON MultiLineString where@@ -69,7 +81,7 @@   parseJSON = withObject "MultiLineString" $ \o -> do     ("MultiLineString" :: T.Text) <- o .: "type"     ls <- o .: "coordinates"-    cs <- sequence $ fmap parseJSON ls+    cs <- mapM parseJSON ls     return $ MultiLineString cs  instance ToJSON MultiPolygon where@@ -79,17 +91,17 @@   parseJSON = withObject "MultiPolygon" $ \o -> do     ("MultiPolygon" :: T.Text) <- o .: "type"     ls <- o .: "coordinates"-    cs <- sequence $ fmap parseJSON ls+    cs <- mapM parseJSON ls     return $ MultiPolygon cs  addKeyToValue :: Value -> T.Text -> Value -> Maybe Value addKeyToValue (Object hm) k v = Just . Object $ HM.insert k v hm addKeyToValue _ _ _ = Nothing-  + go :: ToJSON a => SRID -> a -> Value-go (Just s) x = -    let v = toJSON x -    in maybe v id $ addKeyToValue v "crs" $ sridToJson s+go (Just s) x =+    let v = toJSON x+    in fromMaybe v $ addKeyToValue v "crs" $ sridToJson s go Nothing x = toJSON x  instance ToJSON Geometry where@@ -100,24 +112,24 @@   toJSON (GeoMultiLineString s x) =  go s x   toJSON (GeoMultiPolygon s x) =  go s x -sridToJson srid = -  object ["type" .= ("name" :: T.Text), "properties" .= object ["name" .= ("ESPG:" <> (show srid)  :: String)] ]+sridToJson srid =+  object ["type" .= ("name" :: T.Text), "properties" .= object ["name" .= ("ESPG:" <> show srid  :: String)] ]   parseCRS :: Value -> Parser (Maybe Int)-parseCRS = withObject "crs" $ \o ->  do-  crs <- o .: "crs" -  ("name"::T.Text) <- crs .: "type"-  prop <- crs .: "properties"-  espg <-  prop .: "name"-  let (x:y:xs) = T.split ((==) ':') espg-  case decimal y of-    Left e ->  return Nothing-    Right (v,_) -> return $ Just v-    -  +parseCRS = withObject "crs" $ \o ->+  (o .:? "crs") >>= maybe (return Nothing) _parseCRS+  where+    _parseCRS crs = do+        ("name"::T.Text) <- crs .: "type"+        prop <- crs .: "properties"+        espg <-  prop .: "name"+        -- FIXME: any string before : allowed+        let (x:y:xs) = T.split (':' ==) espg+        return $ rightToMaybe (decimal y) >>= Just . fst+ instance FromJSON Geometry where-  parseJSON o  +  parseJSON o     =   GeoPoint <$> parseCRS o <*> parseJSON o     <|> GeoLineString <$> parseCRS o <*> parseJSON o     <|> GeoPolygon <$> parseCRS o <*> parseJSON o
src/Database/Postgis/Serialize.hs view
@@ -13,6 +13,7 @@ import Data.Binary.Put import System.Endian import qualified Data.Vector as V+import Data.Maybe  import Data.Binary.IEEE754 import Data.Int@@ -33,7 +34,7 @@ type Putter a = a -> Put   instance Binary Endianness where-  get = fromHex <$> (getLazyByteString 2) +  get = fromHex <$> getLazyByteString 2   put = putLazyByteString . toHex  instance Binary Header where@@ -89,13 +90,13 @@ makeHeader s geo =   let gt = geoType geo       wOr acc (p, h) = if p then h .|. acc else acc-      typ = foldl wOr gt [(hasM geo, wkbM), (hasZ geo, wkbZ), (s /= Nothing, wkbSRID)]   +      typ = foldl wOr gt [(hasM geo, wkbM), (hasZ geo, wkbZ), (isJust s, wkbSRID)]   in Header getSystemEndianness typ s   putRing :: Putter LinearRing putRing v = do   putInt . V.length $ v  -  V.mapM_ putPoint v+  V.mapM_ putPosition v  putGeometry :: Putter Geometry putGeometry (GeoPoint s p) = do@@ -114,7 +115,7 @@ putGeometry (GeoMultiPoint s mp@(MultiPoint ps)) = do   put $ makeHeader s mp   putInt . V.length $ ps -  V.mapM_ (putGeometry . GeoPoint s)  ps+  V.mapM_ putPosition ps  putGeometry (GeoMultiLineString s mls@(MultiLineString ls)) = do   put $ makeHeader s mls@@ -127,8 +128,11 @@   V.mapM_ (putGeometry . GeoPolygon s)  ps  ----+putPosition :: Putter Position+putPosition (Position x y m z) = putDouble x >> putDouble y >> putMaybe m putDouble >> putMaybe z putDouble+ putPoint :: Putter Point-putPoint (Point x y m z) = putDouble x >> putDouble y >> putMaybe m putDouble >> putMaybe z putDouble+putPoint (Point position) = putPosition position  putDouble :: Putter Double putDouble = putLazyByteString . toHex . (endFunc getSystemEndianness byteSwap64) . doubleToWord@@ -147,7 +151,7 @@ getGeometry :: Get Geometry  getGeometry = do   h <- lookAhead get-  let t = (_geoType h) .&. ewkbTypeOffset+  let t = _geoType h .&. ewkbTypeOffset       mkGeo :: (SRID -> a -> Geometry) -> Getter a -> Get Geometry       mkGeo cons p = cons (_srid h) <$> runReaderT p h   case t of@@ -178,7 +182,7 @@ getMultiPoint = do   lift getHeader   n <- getInt -  ps <- V.replicateM n getGeoPoint+  ps <- V.replicateM n getPosition   return $ MultiPoint ps   getPolygon :: Getter Polygon @@ -190,22 +194,25 @@ getRing :: Getter LinearRing getRing = getSegment  -getSegment :: Getter (V.Vector Point)-getSegment = getInt >>= (\n -> V.replicateM n getPoint) +getSegment :: Getter (V.Vector Position)+getSegment = getInt >>= (\n -> V.replicateM n getPosition)    getGeoPoint :: Getter Point getGeoPoint = lift getHeader >> getPoint +getPosition :: Getter Position+getPosition = do+  gt <- asks _geoType +  let hasM = (gt .&. wkbM) > 0+      hasZ = (gt .&. wkbZ) > 0+  x <- getDouble+  y <- getDouble+  z <- if hasZ then Just <$> getDouble else return Nothing+  m <- if hasM then Just <$> getDouble else return Nothing+  return $ Position x y z m+ getPoint :: Getter Point-getPoint = do-    gt <- asks _geoType -    let hasM = if (gt .&. wkbM) > 0 then True else False -        hasZ = if (gt .&. wkbZ) > 0 then True else False-    x <- getDouble-    y <- getDouble-    z <- if hasZ then Just <$> getDouble else return Nothing-    m <- if hasM then Just <$> getDouble else return Nothing-    return $ Point x y z m+getPoint = getPosition >>= return . Point  getHeader :: Get Header getHeader = do
tests/GeoSpec.hs view
@@ -14,10 +14,10 @@ import qualified Data.Vector as V import System.Endian -point1 = GeoPoint (Just 4326) (Point (-79.4217280000002) 42.289467099999925 Nothing Nothing)+point1 = GeoPoint (Just 4326) (Point (Position (-79.4217280000002) 42.289467099999925 Nothing Nothing)) point1BS ="0101000020E6100000BCF36F97FDDA53C042E207420D254540"  :: BL.ByteString linestring1BS = "0102000020E610000005000000805C4A99F98B5DC0BC5768BDDB0E4140805C9A58F98B5DC0C05798F5DC0E4140805C6C2DF88B5DC0C457C846E30E41407E5C3E5CF78B5DC0C857D0B2E70E41407E5CAA69F68B5DC0CC57B813EA0E4140" :: BL.ByteString-linestring1 = GeoLineString (Just 4326) (LineString (V.fromList [(Point (-118.18710930120324) 34.11608092875346 Nothing Nothing), (Point (-118.1870938785014) 34.11611814440357 Nothing Nothing), (Point (-118.18702254850541) 34.116310928176546 Nothing Nothing), (Point (-118.18697267618151) 34.116445876817636 Nothing Nothing), (Point (-118.18691484104963)  34.11651846409913 Nothing Nothing)]))+linestring1 = GeoLineString (Just 4326) (LineString (V.fromList [(Position (-118.18710930120324) 34.11608092875346 Nothing Nothing), (Position (-118.1870938785014) 34.11611814440357 Nothing Nothing), (Position (-118.18702254850541) 34.116310928176546 Nothing Nothing), (Position (-118.18697267618151) 34.116445876817636 Nothing Nothing), (Position (-118.18691484104963)  34.11651846409913 Nothing Nothing)])) linestringJSON = "{\"coordinates\":[[-118.18710930120324,34.11608092875346],[-118.1870938785014,34.11611814440357],[-118.18702254850541,34.116310928176546],[-118.18697267618151,34.116445876817636],[-118.18691484104963,34.11651846409913]],\"crs\":{\"type\":\"name\",\"properties\":{\"name\":\"ESPG:4326\"}},\"type\":\"LineString\"}"  toUpperBS :: BL.ByteString -> BL.ByteString