2022-03-16 03:39:21 +03:00
|
|
|
{-# LANGUAGE TemplateHaskell #-}
|
|
|
|
|
2018-06-27 16:11:32 +03:00
|
|
|
module Hasura.SQL.GeoJSON
|
2021-03-26 19:59:16 +03:00
|
|
|
( Position (..),
|
|
|
|
Point (..),
|
2018-06-27 16:11:32 +03:00
|
|
|
MultiPoint (..),
|
|
|
|
LineString (..),
|
2021-03-26 19:59:16 +03:00
|
|
|
LinearRing (..),
|
2018-06-27 16:11:32 +03:00
|
|
|
MultiLineString (..),
|
|
|
|
Polygon (..),
|
|
|
|
MultiPolygon (..),
|
2021-03-26 19:59:16 +03:00
|
|
|
Geometry (..),
|
2018-06-27 16:11:32 +03:00
|
|
|
GeometryCollection (..),
|
2018-09-27 15:23:17 +03:00
|
|
|
GeometryWithCRS (..),
|
2018-06-27 16:11:32 +03:00
|
|
|
)
|
|
|
|
where
|
2021-09-24 01:56:37 +03:00
|
|
|
|
2018-06-27 16:11:32 +03:00
|
|
|
import Control.Monad
|
2018-09-27 15:23:17 +03:00
|
|
|
import Data.Aeson qualified as J
|
|
|
|
import Data.Aeson.Casing qualified as J
|
|
|
|
import Data.Aeson.TH qualified as J
|
|
|
|
import Data.Aeson.Types qualified as J
|
|
|
|
import Data.Vector qualified as V
|
2018-06-27 16:11:32 +03:00
|
|
|
import Hasura.Prelude
|
|
|
|
|
2022-06-26 01:08:01 +03:00
|
|
|
-- Positions
|
|
|
|
|
2018-06-27 16:11:32 +03:00
|
|
|
data Position
|
|
|
|
= Position !Double !Double !(Maybe Double)
|
|
|
|
deriving (Show, Eq)
|
|
|
|
|
|
|
|
withParsedArray ::
|
|
|
|
(J.FromJSON a) =>
|
|
|
|
String ->
|
|
|
|
(V.Vector a -> J.Parser b) ->
|
|
|
|
J.Value ->
|
|
|
|
J.Parser b
|
|
|
|
withParsedArray s fn =
|
|
|
|
J.withArray s (mapM J.parseJSON >=> fn)
|
|
|
|
|
|
|
|
instance J.FromJSON Position where
|
|
|
|
parseJSON = withParsedArray "Position" $ \arr ->
|
|
|
|
if V.length arr < 2
|
|
|
|
then fail "A Position needs at least 2 elements"
|
|
|
|
else -- here we are ignoring anything past 3 elements
|
2021-09-24 01:56:37 +03:00
|
|
|
|
2023-05-24 16:51:56 +03:00
|
|
|
return
|
|
|
|
$ Position
|
2018-06-27 16:11:32 +03:00
|
|
|
(arr `V.unsafeIndex` 0)
|
|
|
|
(arr `V.unsafeIndex` 1)
|
|
|
|
(arr V.!? 2)
|
|
|
|
|
|
|
|
instance J.ToJSON Position where
|
|
|
|
toJSON (Position a b c) =
|
|
|
|
J.toJSON $ a : b : maybeToList c
|
|
|
|
|
2022-06-26 01:08:01 +03:00
|
|
|
-- Point, Multipoint
|
|
|
|
|
2018-06-27 16:11:32 +03:00
|
|
|
newtype Point = Point {unPoint :: Position}
|
|
|
|
deriving (Show, Eq, J.ToJSON, J.FromJSON)
|
|
|
|
|
|
|
|
newtype MultiPoint = MultiPoint {unMultiPoint :: [Position]}
|
|
|
|
deriving (Show, Eq, J.ToJSON, J.FromJSON)
|
|
|
|
|
2022-06-26 01:08:01 +03:00
|
|
|
-- LineString, MultiLineString
|
|
|
|
|
2018-06-27 16:11:32 +03:00
|
|
|
data LineString = LineString
|
|
|
|
{ _lsFirst :: !Position,
|
|
|
|
_lsSecond :: !Position,
|
|
|
|
_lsRest :: ![Position]
|
|
|
|
}
|
|
|
|
deriving (Show, Eq)
|
|
|
|
|
|
|
|
instance J.ToJSON LineString where
|
|
|
|
toJSON (LineString a b rest) =
|
|
|
|
J.toJSON $ a : b : rest
|
|
|
|
|
|
|
|
instance J.FromJSON LineString where
|
|
|
|
parseJSON = withParsedArray "LineString" $ \arr ->
|
|
|
|
if V.length arr < 2
|
|
|
|
then fail "A LineString needs at least 2 Positions"
|
|
|
|
else -- here we are ignoring anything past 3 elements
|
2021-09-24 01:56:37 +03:00
|
|
|
|
2018-06-27 16:11:32 +03:00
|
|
|
let fstPos = arr `V.unsafeIndex` 0
|
|
|
|
sndPos = arr `V.unsafeIndex` 1
|
|
|
|
rest = V.toList $ V.drop 2 arr
|
|
|
|
in return $ LineString fstPos sndPos rest
|
2021-09-24 01:56:37 +03:00
|
|
|
|
2018-06-27 16:11:32 +03:00
|
|
|
newtype MultiLineString = MultiLineString {unMultiLineString :: [LineString]}
|
|
|
|
deriving (Show, Eq, J.ToJSON, J.FromJSON)
|
|
|
|
|
2022-06-26 01:08:01 +03:00
|
|
|
-- Polygon, MultiPolygon
|
2018-06-27 16:11:32 +03:00
|
|
|
|
|
|
|
data LinearRing = LinearRing
|
|
|
|
{ _pFirst :: !Position,
|
|
|
|
_pSecond :: !Position,
|
|
|
|
_pThird :: !Position,
|
|
|
|
_pRest :: ![Position]
|
|
|
|
}
|
|
|
|
deriving (Show, Eq)
|
|
|
|
|
|
|
|
instance J.FromJSON LinearRing where
|
|
|
|
parseJSON = withParsedArray "LinearRing" $ \arr ->
|
|
|
|
if V.length arr < 4
|
|
|
|
then fail "A LinearRing needs at least 4 Positions"
|
|
|
|
else -- here we are ignoring anything past 3 elements
|
|
|
|
do
|
|
|
|
let fstPos = arr `V.unsafeIndex` 0
|
|
|
|
sndPos = arr `V.unsafeIndex` 1
|
|
|
|
thrPos = arr `V.unsafeIndex` 2
|
|
|
|
rest = V.drop 3 arr
|
|
|
|
let lastPos = V.last rest
|
2023-05-24 16:51:56 +03:00
|
|
|
unless (fstPos == lastPos)
|
|
|
|
$ fail "the first and last locations have to be equal for a LinearRing"
|
2018-06-27 16:11:32 +03:00
|
|
|
return $ LinearRing fstPos sndPos thrPos $ V.toList $ V.init rest
|
|
|
|
|
|
|
|
instance J.ToJSON LinearRing where
|
|
|
|
toJSON (LinearRing a b c rest) =
|
|
|
|
J.toJSON $ (V.fromList [a, b, c] <> V.fromList rest) `V.snoc` a
|
|
|
|
|
|
|
|
newtype Polygon = Polygon {unPolygon :: [LinearRing]}
|
|
|
|
deriving (Show, Eq, J.ToJSON, J.FromJSON)
|
|
|
|
|
|
|
|
newtype MultiPolygon = MultiPolygon {unMultiPolygon :: [Polygon]}
|
|
|
|
deriving (Show, Eq, J.ToJSON, J.FromJSON)
|
|
|
|
|
2022-06-26 01:08:01 +03:00
|
|
|
-- GeometryCollection
|
|
|
|
|
|
|
|
data CRSNameProps = CRSNameProps
|
|
|
|
{ _cnpName :: !Text
|
|
|
|
}
|
2018-06-27 16:11:32 +03:00
|
|
|
deriving (Show, Eq)
|
|
|
|
|
2022-06-26 01:08:01 +03:00
|
|
|
data CRSLinkProps = CRSLinkProps
|
|
|
|
{ _clpHref :: !Text,
|
|
|
|
_clpType :: !(Maybe Text)
|
|
|
|
}
|
|
|
|
deriving (Show, Eq)
|
|
|
|
|
|
|
|
data CRS
|
|
|
|
= CRSName !CRSNameProps
|
|
|
|
| CRSLink !CRSLinkProps
|
|
|
|
deriving (Show, Eq)
|
|
|
|
|
|
|
|
$(J.deriveJSON (J.aesonPrefix J.camelCase) ''CRSNameProps)
|
|
|
|
$(J.deriveJSON (J.aesonPrefix J.camelCase) ''CRSLinkProps)
|
|
|
|
$( J.deriveJSON
|
|
|
|
J.defaultOptions
|
|
|
|
{ J.constructorTagModifier = J.camelCase . drop 3,
|
|
|
|
J.sumEncoding = J.TaggedObject "type" "properties"
|
|
|
|
}
|
|
|
|
''CRS
|
|
|
|
)
|
|
|
|
|
2018-09-27 15:23:17 +03:00
|
|
|
data GeometryWithCRS = GeometryWithCRS
|
|
|
|
{ _gwcGeom :: !Geometry,
|
|
|
|
_gwcCrs :: !(Maybe CRS)
|
|
|
|
}
|
|
|
|
deriving (Show, Eq)
|
2018-06-27 16:11:32 +03:00
|
|
|
|
2020-10-27 16:53:49 +03:00
|
|
|
encToCoords :: (J.ToJSON a) => Text -> a -> Maybe CRS -> J.Value
|
2018-09-27 15:23:17 +03:00
|
|
|
encToCoords ty a Nothing =
|
|
|
|
J.object ["type" J..= ty, "coordinates" J..= a]
|
|
|
|
encToCoords ty a (Just crs) =
|
|
|
|
J.object ["type" J..= ty, "coordinates" J..= a, "crs" J..= crs]
|
|
|
|
|
|
|
|
instance J.ToJSON GeometryWithCRS where
|
|
|
|
toJSON (GeometryWithCRS geom crsM) = case geom of
|
|
|
|
GPoint o -> encToCoords "Point" o crsM
|
|
|
|
GMultiPoint o -> encToCoords "MultiPoint" o crsM
|
|
|
|
GLineString o -> encToCoords "LineString" o crsM
|
|
|
|
GMultiLineString o -> encToCoords "MultiLineString" o crsM
|
|
|
|
GPolygon o -> encToCoords "Polygon" o crsM
|
2018-10-24 11:21:37 +03:00
|
|
|
GMultiPolygon o -> encToCoords "MultiPolygon" o crsM
|
2018-06-27 16:11:32 +03:00
|
|
|
GGeometryCollection o ->
|
2020-10-27 16:53:49 +03:00
|
|
|
J.object
|
|
|
|
[ "type" J..= ("GeometryCollection" :: Text),
|
2018-06-27 16:11:32 +03:00
|
|
|
"geometries" J..= o
|
|
|
|
]
|
|
|
|
|
2018-09-27 15:23:17 +03:00
|
|
|
instance J.FromJSON GeometryWithCRS where
|
2018-06-27 16:11:32 +03:00
|
|
|
parseJSON = J.withObject "Geometry" $ \o -> do
|
|
|
|
ty <- o J..: "type"
|
2018-09-27 15:23:17 +03:00
|
|
|
geom <- case ty of
|
2018-06-27 16:11:32 +03:00
|
|
|
"Point" -> GPoint <$> o J..: "coordinates"
|
|
|
|
"MultiPoint" -> GMultiPoint <$> o J..: "coordinates"
|
|
|
|
"LineString" -> GLineString <$> o J..: "coordinates"
|
|
|
|
"MultiLineString" -> GMultiLineString <$> o J..: "coordinates"
|
|
|
|
"Polygon" -> GPolygon <$> o J..: "coordinates"
|
2018-10-24 11:21:37 +03:00
|
|
|
"MultiPolygon" -> GMultiPolygon <$> o J..: "coordinates"
|
2018-06-27 16:11:32 +03:00
|
|
|
"GeometryCollection" -> GGeometryCollection <$> o J..: "geometries"
|
|
|
|
_ -> fail $ "unexpected geometry type: " <> ty
|
2018-09-27 15:23:17 +03:00
|
|
|
crsM <- o J..:? "crs"
|
|
|
|
return $ GeometryWithCRS geom crsM
|
|
|
|
|
2022-06-26 01:08:01 +03:00
|
|
|
newtype GeometryCollection = GeometryCollection {unGeometryCollection :: [GeometryWithCRS]}
|
|
|
|
deriving (Show, Eq, J.ToJSON, J.FromJSON)
|
2018-09-27 15:23:17 +03:00
|
|
|
|
2022-06-26 01:08:01 +03:00
|
|
|
-- Geometry
|
2018-09-27 15:23:17 +03:00
|
|
|
|
2022-06-26 01:08:01 +03:00
|
|
|
data Geometry
|
|
|
|
= GPoint !Point
|
|
|
|
| GMultiPoint !MultiPoint
|
|
|
|
| GLineString !LineString
|
|
|
|
| GMultiLineString !MultiLineString
|
|
|
|
| GPolygon !Polygon
|
|
|
|
| GMultiPolygon !MultiPolygon
|
|
|
|
| GGeometryCollection !GeometryCollection
|
2018-09-27 15:23:17 +03:00
|
|
|
deriving (Show, Eq)
|