packages feed

esqueleto-postgis 3.0.0 → 4.0.0

raw patch · 4 files changed

+130/−15 lines, 4 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

+ Database.Esqueleto.Postgis: Degree :: SridUnit
+ Database.Esqueleto.Postgis: Linear :: SridUnit
+ Database.Esqueleto.Postgis: britishNationalGrid :: SRID 'Linear
+ Database.Esqueleto.Postgis: class HasPgType (spatialType :: SpatialType)
+ Database.Esqueleto.Postgis: data SRID (unit :: SridUnit)
+ Database.Esqueleto.Postgis: data SridUnit
+ Database.Esqueleto.Postgis: instance Database.Persist.Class.PersistField.PersistField (Database.Esqueleto.Postgis.SRID unit)
+ Database.Esqueleto.Postgis: instance GHC.Num.Num (Database.Esqueleto.Postgis.SRID unit)
+ Database.Esqueleto.Postgis: mercator :: SRID 'Linear
+ Database.Esqueleto.Postgis: st_transform_geography :: SRID 'Linear -> SqlExpr (Value (Postgis 'Geography a)) -> SqlExpr (Value (Postgis 'Geometry a))
+ Database.Esqueleto.Postgis: st_transform_geometry :: SqlExpr (Value (Postgis 'Geometry a)) -> SqlExpr (Value (Postgis 'Geography a))
+ Database.Esqueleto.Postgis: wgs84 :: SRID 'Degree
- Database.Esqueleto.Postgis: st_contains :: forall (spatialType :: SpatialType) a. SqlExpr (Value (Postgis spatialType a)) -> SqlExpr (Value (Postgis spatialType a)) -> SqlExpr (Value Bool)
+ Database.Esqueleto.Postgis: st_contains :: SqlExpr (Value (Postgis 'Geometry a)) -> SqlExpr (Value (Postgis 'Geometry a)) -> SqlExpr (Value Bool)
- Database.Esqueleto.Postgis: st_intersects :: SqlExpr (Value (PostgisGeometry a)) -> SqlExpr (Value (PostgisGeometry a)) -> SqlExpr (Value Bool)
+ Database.Esqueleto.Postgis: st_intersects :: forall (spatialType :: SpatialType) a. SqlExpr (Value (Postgis spatialType a)) -> SqlExpr (Value (Postgis spatialType a)) -> SqlExpr (Value Bool)
- Database.Esqueleto.Postgis: st_union :: forall (spatialType :: SpatialType) a. SqlExpr (Value (Postgis spatialType a)) -> SqlExpr (Value (Postgis spatialType a))
+ Database.Esqueleto.Postgis: st_union :: SqlExpr (Value (Postgis 'Geometry a)) -> SqlExpr (Value (Postgis 'Geometry a))
- Database.Esqueleto.Postgis: st_unions :: forall (spatialType :: SpatialType) a. HasPgType spatialType => SqlExpr (Value (Postgis spatialType a)) -> SqlExpr (Value (Postgis spatialType a)) -> SqlExpr (Value (Postgis spatialType a))
+ Database.Esqueleto.Postgis: st_unions :: SqlExpr (Value (Postgis 'Geometry a)) -> SqlExpr (Value (Postgis 'Geometry a)) -> SqlExpr (Value (Postgis 'Geometry a))

Files

Changelog.md view
@@ -1,5 +1,14 @@ # Change log for esqueleto-postgis project +## Version 4.0.0 +Hotfix!+++ `st_union` can't work on geography, banned that.++ `st_intersects` was to strict and worked only on geography++ add `st_transform_geometry` and `st_transform_geography` to convert between the two.++ hopefully I've explained the srid system somewhat understandable, it's kinda confusing.++ restrict `st_contains` to geometry as well        .+ ## Version 3.0.0  * add spatial type, split up postgis from geoemetry.   this will allow us to deal with curveture of earth and other weird SRID's:
esqueleto-postgis.cabal view
@@ -5,7 +5,7 @@ -- see: https://github.com/sol/hpack  name:           esqueleto-postgis-version:        3.0.0+version:        4.0.0 homepage:       https://github.com/jappeace/esqueleto-postgis#readme bug-reports:    https://github.com/jappeace/esqueleto-postgis/issues author:         Jappie Klooster
src/Database/Esqueleto/Postgis.hs view
@@ -8,6 +8,13 @@  -- | Haskell bindings for postgres postgis --   for a good explenation see <https://postgis.net/>+--+--   Make sure to use the correct 'SpatialType'.+--   Earth spanning applications should use Geography,+--   local applications should use 'Geometry' because it's more convenient.+--+--   if you can't use a function for example when you're using 'Geography'.+--   there is the option to 'st_transform_geography'. module Database.Esqueleto.Postgis   (     Postgis(..),@@ -28,10 +35,22 @@     st_point,     st_point_xyz,     st_point_xyzm,-    -- * other +    -- * transform+    st_transform_geography,+    st_transform_geometry,+    -- ** srid+    SRID,+    wgs84,+    mercator ,+    britishNationalGrid,+    SridUnit    (..),+++    -- * other     makePolygon,     PostgisGeometry,+    HasPgType,      -- * re-exports     PointXY(..),@@ -88,10 +107,14 @@ tshow = pack . show  --- | Spatial Reference System.+-- | Guarantees we don't accidently mix curved space with flat space.+--   Postgis will catch this too, this just put's it in the type system. data SpatialType = Geometry -- ^ assume a flat space.                  | Geography -- ^ assume curvature of the earth. +-- | technical typeclass to bind a spatial type to a string value.+--   because we represent the constructors as a datakind, we need+--   this to go back to a value. class HasPgType (spatialType :: SpatialType) where   pgType :: Proxy spatialType -> Text @@ -267,9 +290,9 @@ --   https://postgis.net/docs/ST_Contains.html st_contains ::   -- | geom a-  SqlExpr (Value (Postgis spatialType a)) ->+  SqlExpr (Value (Postgis 'Geometry a)) ->   -- | geom b-  SqlExpr (Value (Postgis spatialType a)) ->+  SqlExpr (Value (Postgis 'Geometry a)) ->   SqlExpr (Value Bool) st_contains a b = unsafeSqlFunction "ST_CONTAINS" (a, b) @@ -285,6 +308,66 @@   SqlExpr (Value Bool) st_dwithin a b c = unsafeSqlFunction "ST_DWithin" (a, b, c) +-- | SRID+-- you can find your local like this: https://blog.rustprooflabs.com/2020/11/postgis-find-local-srid+-- geography appears to use 'wgs84'. So I hardcoded the use going from geom to geography as that.+--+-- you can use the num instance to put in whatever.+-- however, if you miss a srid please submit a PR.+newtype SRID (unit :: SridUnit) = SRID Int+  deriving newtype (Num, PersistField)++-- | default for geography type in postgis.+--   Geodetic CRS: WGS 84+--   https://epsg.io/4326+wgs84 :: SRID 'Degree+wgs84 = 4326++-- | most maps are in this, it has some large distortions further away from equator+--   https://epsg.io/3857+mercator :: SRID 'Linear+mercator = 3857++-- | if you're in england this is pretty good.+--   https://epsg.io/27700+britishNationalGrid :: SRID 'Linear+britishNationalGrid = 27700+++-- | Diferent 'SRID' come in different units,+--   important for converting from geograhy to geometry.+data SridUnit = Linear -- ^ meters or feet+          | Degree -- ^ spheroids++-- | Project a geography onto a geometry.+--   allows using of functions such as 'st_union' which only work in flat space (geometry).+--   https://postgis.net/docs/ST_Transform.html+st_transform_geography :: forall a. SRID 'Linear ->+                        SqlExpr (Value (Postgis 'Geography a)) -> -- ^ g1 (library handles the conversion)+                        SqlExpr (Value (Postgis 'Geometry a))+st_transform_geography srid geography =+  unsafeSqlFunction "ST_Transform" (casted, val srid)+  where+    casted :: SqlExpr (Value (Postgis 'Geometry a))+    casted = unsafe_cast_pg_type geography++-- | project a geometry as a geography, assumes 'wgs84'.+--   https://postgis.net/docs/ST_Transform.html+st_transform_geometry :: SqlExpr (Value (Postgis 'Geometry a)) -> -- ^ g1 (library handles the conversion)+                        SqlExpr (Value (Postgis 'Geography a))+st_transform_geometry input = unsafe_cast_pg_type transformed+  where+    transformed :: SqlExpr (Value (Postgis 'Geometry a))+    transformed =+      unsafeSqlFunction "ST_Transform" (input, val wgs84)++-- postgis doesn't appear to care about casting between spaces,+-- the user probably wants to use st_transform instead.+unsafe_cast_pg_type :: forall two one a . HasPgType two => SqlExpr (Value (Postgis one a)) -> SqlExpr (Value (Postgis two a))+unsafe_cast_pg_type = unsafeSqlCastAs castAs+  where+    castAs = pgType $ Proxy @two+ -- | allows union of geometries, eg group a bunch together, --   https://postgis.net/docs/ST_Union.html --   for example:@@ -302,21 +385,20 @@ --    pure unit -- @ st_union ::-  SqlExpr (Value (Postgis spatialType a)) ->-  SqlExpr (Value (Postgis spatialType a))+  SqlExpr (Value (Postgis 'Geometry a)) ->+  SqlExpr (Value (Postgis 'Geometry a)) st_union a = unsafeSqlFunction "ST_union" a  st_unions ::-  forall spatialType a . HasPgType spatialType =>-  SqlExpr (Value (Postgis spatialType a)) ->-  SqlExpr (Value (Postgis spatialType a)) ->-  SqlExpr (Value (Postgis spatialType a))+  SqlExpr (Value (Postgis 'Geometry a)) ->+  SqlExpr (Value (Postgis 'Geometry a)) ->+  SqlExpr (Value (Postgis 'Geometry a)) st_unions a b =   -- casts to prevent   -- function st_union(unknown, unknown) is not unique", sqlErrorDetail = "", sqlErrorHint = "Could not choose a best candidate function. You might need to add explicit type casts.   unsafeSqlFunction "ST_union" ((unsafeSqlCastAs casted a), (unsafeSqlCastAs casted b))   where-    casted = (pgType $ Proxy @spatialType)+    casted = (pgType $ Proxy @'Geometry)  -- | calculate the distance between two points --   https://postgis.net/docs/ST_Distance.html@@ -331,8 +413,8 @@ --   Geometries intersect if they have any point in common. --   https://postgis.net/docs/ST_Intersects.html st_intersects ::-  SqlExpr (Value (PostgisGeometry a)) ->-  SqlExpr (Value (PostgisGeometry a)) ->+  SqlExpr (Value (Postgis spatialType a)) -> -- ^ geomA or geogA+  SqlExpr (Value (Postgis spatialType a)) -> -- ^ geomB or geogB   SqlExpr (Value Bool) st_intersects a b = unsafeSqlFunction "ST_Intersects" (a, b) 
test/Test.hs view
@@ -71,6 +71,10 @@     geom (PostgisGeometry PointXY)     label Text +  GeoGrid+    geo (Postgis 'Geography PointXY)+    label Text+   Unit sql=unit     geom       (PostgisGeometry PointXY)     deriving Eq Show@@ -218,10 +222,30 @@                 grid <- from $ table @Grid                 pure $ st_union $ grid ^. GridGeom             unValue <$> result @?= (Just $ Polygon $ makeLinearRing (PointXY {_xyX = 0.0, _xyY = 2.0}) (PointXY {_xyX = 2.0, _xyY = 2.0}) (PointXY {_xyX = 4.0, _xyY = 2.0}) (Seq.fromList [PointXY {_xyX = 4.0, _xyY = 0.0}, PointXY {_xyX = 2.0, _xyY = 0.0}, PointXY {_xyX = 0.0, _xyY = 0.0}])),+          testCase ("union_in_PG_and then get out some Haskell") $ do+            result <- runDB $ do+              _ <-+                insert $+                  GeoGrid+                    { geoGridGeo = Polygon $ makePolygon (PointXY 0 0) (PointXY 0 2) (PointXY 2 2) $ Seq.fromList [(PointXY 2 0)],+                      geoGridLabel = "x"+                    }+              _ <-+                insert $+                  GeoGrid+                    { geoGridGeo = Polygon $ makePolygon (PointXY 2 0) (PointXY 2 2) (PointXY 4 2) $ Seq.fromList [(PointXY 4 0)],+                      geoGridLabel = "y"+                    }++              selectOne $ do+                grid <- from $ table @GeoGrid+                pure $ st_transform_geometry $ st_union $ st_transform_geography mercator $ grid ^. GeoGridGeo+            -- as degrees it doesn't appear to be merging them.+            unValue <$> result @?= (Just (Polygon (makeLinearRing (PointXY {_xyX = 1.9999999999999996, _xyY = 1.9999999999999996}) (PointXY {_xyX = 3.999999999999999, _xyY = 1.9999999999999996}) (PointXY {_xyX = 3.999999999999999, _xyY = 0.0}) (Seq.fromList [PointXY {_xyX = 1.9999999999999996, _xyY = 0.0},PointXY {_xyX = 0.0, _xyY = 0.0},PointXY {_xyX = 0.0, _xyY = 1.9999999999999996}])))),           testCase ("see if we can unions in PG and then get out some Haskell") $ do             result <- runDB $ do               selectOne $-                pure $ st_unions @'Geometry  (val (Polygon $ makePolygon (PointXY 0 0) (PointXY 0 2) (PointXY 2 2) $ Seq.fromList [(PointXY 2 0)])) $+                pure $ st_unions (val (Polygon $ makePolygon (PointXY 0 0) (PointXY 0 2) (PointXY 2 2) $ Seq.fromList [(PointXY 2 0)])) $                         val $ Polygon $ makePolygon (PointXY 2 0) (PointXY 2 2) (PointXY 4 2) $ Seq.fromList [(PointXY 4 0)]             unValue <$> result @?= (Just $ Polygon $ makeLinearRing (PointXY {_xyX = 0.0, _xyY = 2.0}) (PointXY {_xyX = 2.0, _xyY = 2.0}) (PointXY {_xyX = 4.0, _xyY = 2.0}) (Seq.fromList [PointXY {_xyX = 4.0, _xyY = 0.0}, PointXY {_xyX = 2.0, _xyY = 0.0}, PointXY {_xyX = 0.0, _xyY = 0.0}])),