module Data.Internal.Wkb.Geometry ( GeometryType (..) , CoordinateType (..) , WkbGeometryType (..) , BuilderWkbGeometryType , getWkbGeom , builderWkbGeom , geoPositionWithoutCRSToCoordinateType , coordTypeOfSequence , coordTypeOfLinearRings ) where import qualified Control.Monad as Monad import qualified Data.Binary.Get as BinaryGet import qualified Data.ByteString.Builder as ByteStringBuilder import qualified Data.Geospatial as Geospatial import qualified Data.LinearRing as LinearRing import qualified Data.Maybe as Maybe import qualified Data.Sequence as Sequence import qualified Data.Word as Word import qualified Data.Internal.Wkb.Endian as Endian -- Types data GeometryType = Geometry | Point | LineString | Polygon | MultiPoint | MultiLineString | MultiPolygon | GeometryCollection deriving (Show, Eq) data CoordinateType = TwoD | Z | M | ZM deriving (Show, Eq) data WkbGeometryType = WkbGeom GeometryType CoordinateType deriving (Show, Eq) -- Binary parsers getWkbGeom :: Endian.EndianType -> BinaryGet.Get WkbGeometryType getWkbGeom endianType = do fullGeometryType <- Endian.getFourBytes endianType let geomType = intToGeometryType $ fullGeometryType `rem` 1000 coordType = intToCoordinateType $ fullGeometryType `div` 1000 case (geomType, coordType) of (Just g, Just c) -> pure $ WkbGeom g c _ -> Monad.fail $ "Invalid WkbGeometryType: " ++ show fullGeometryType -- Binary builders type BuilderWkbGeometryType = Endian.EndianType -> WkbGeometryType -> ByteStringBuilder.Builder builderWkbGeom :: Endian.EndianType -> WkbGeometryType -> ByteStringBuilder.Builder builderWkbGeom endianType (WkbGeom geometryType coordinateType) = do let int = coordinateTypeToInt coordinateType * 1000 + geometryTypeToInt geometryType Endian.builderFourBytes endianType int -- Helpers geoPositionWithoutCRSToCoordinateType :: Geospatial.GeoPositionWithoutCRS -> Maybe CoordinateType geoPositionWithoutCRSToCoordinateType geoPosition = case geoPosition of Geospatial.GeoEmpty -> Nothing Geospatial.GeoPointXY _ -> Just TwoD Geospatial.GeoPointXYZ _ -> Just Z Geospatial.GeoPointXYZM _ -> Just ZM coordTypeOfSequence :: Sequence.Seq Geospatial.GeoPositionWithoutCRS -> CoordinateType coordTypeOfSequence (first Sequence.:<| _) = Maybe.fromMaybe TwoD (geoPositionWithoutCRSToCoordinateType first) coordTypeOfSequence _ = TwoD coordTypeOfLinearRings :: Sequence.Seq (LinearRing.LinearRing Geospatial.GeoPositionWithoutCRS) -> CoordinateType coordTypeOfLinearRings (first Sequence.:<| _) = coordTypeOfSequence $ LinearRing.toSeq first coordTypeOfLinearRings _ = TwoD intToGeometryType :: Word.Word32 -> Maybe GeometryType intToGeometryType int = case int of 0 -> Just Geometry 1 -> Just Point 2 -> Just LineString 3 -> Just Polygon 4 -> Just MultiPoint 5 -> Just MultiLineString 6 -> Just MultiPolygon 7 -> Just GeometryCollection _ -> Nothing geometryTypeToInt :: GeometryType -> Word.Word32 geometryTypeToInt geometryType = case geometryType of Geometry -> 0 Point -> 1 LineString -> 2 Polygon -> 3 MultiPoint -> 4 MultiLineString -> 5 MultiPolygon -> 6 GeometryCollection -> 7 intToCoordinateType :: Word.Word32 -> Maybe CoordinateType intToCoordinateType int = case int of 0 -> Just TwoD 1 -> Just Z 2 -> Just M 3 -> Just ZM _ -> Nothing coordinateTypeToInt :: CoordinateType -> Word.Word32 coordinateTypeToInt coordinateType = case coordinateType of TwoD -> 0 Z -> 1 M -> 2 ZM -> 3