module Data.Internal.Wkb.Point ( getPoint , getMultiPoint , getGeoPoint , getCoordPoint , getCoordPoints , builderPoint , builderMultiPoint , builderCoordPoint , builderCoordPoints ) where import qualified Data.Binary.Get as BinaryGet import qualified Data.ByteString.Builder as ByteStringBuilder import qualified Data.Foldable as Foldable import qualified Data.Geospatial as Geospatial import Data.Monoid ((<>)) import qualified Data.Monoid as Monoid import qualified Data.Sequence as Sequence import qualified Data.Word as Word import qualified Data.Internal.Wkb.Endian as Endian import qualified Data.Internal.Wkb.Geometry as Geometry import qualified Data.Internal.Wkb.GeometryCollection as GeometryCollection -- Binary parsers getPoint :: Endian.EndianType -> Geometry.CoordinateType -> BinaryGet.Get Geospatial.GeospatialGeometry getPoint endianType coordType = do geoPoint <- getGeoPoint endianType coordType pure $ Geospatial.Point geoPoint getMultiPoint :: (Endian.EndianType -> BinaryGet.Get Geometry.WkbGeometryType) -> Endian.EndianType -> Geometry.CoordinateType -> BinaryGet.Get Geospatial.GeospatialGeometry getMultiPoint getWkbGeom endianType _ = do numberOfPoints <- Endian.getFourBytes endianType geoPoints <- Sequence.replicateM (fromIntegral numberOfPoints) (GeometryCollection.getEnclosedFeature getWkbGeom Geometry.Point getGeoPoint) pure $ Geospatial.MultiPoint $ Geospatial.mergeGeoPoints geoPoints getGeoPoint :: Endian.EndianType -> Geometry.CoordinateType -> BinaryGet.Get Geospatial.GeoPoint getGeoPoint endianType coordType = do p <- getCoordPoint endianType coordType pure $ Geospatial.GeoPoint p getCoordPoint :: Endian.EndianType -> Geometry.CoordinateType -> BinaryGet.Get Geospatial.GeoPositionWithoutCRS getCoordPoint endianType coordType = case coordType of Geometry.TwoD -> do point <- Geospatial.PointXY <$> getDouble <*> getDouble return $ Geospatial.GeoPointXY point Geometry.Z -> do point <- Geospatial.PointXYZ <$> getDouble <*> getDouble <*> getDouble return $ Geospatial.GeoPointXYZ point Geometry.M -> do point <- Geospatial.PointXYZ <$> getDouble <*> getDouble <*> getDouble return $ Geospatial.GeoPointXYZ point Geometry.ZM -> do point <- Geospatial.PointXYZM <$> getDouble <*> getDouble <*> getDouble <*> getDouble return $ Geospatial.GeoPointXYZM point where getDouble = Endian.getDouble endianType getCoordPoints :: Endian.EndianType -> Geometry.CoordinateType -> Word.Word32 -> BinaryGet.Get (Sequence.Seq Geospatial.GeoPositionWithoutCRS) getCoordPoints endianType coordType numberOfPoints = Sequence.replicateM (fromIntegral numberOfPoints) (getCoordPoint endianType coordType) -- Binary builders builderPoint :: Geometry.BuilderWkbGeometryType -> Endian.EndianType -> Geospatial.GeoPoint -> ByteStringBuilder.Builder builderPoint builderWkbGeom endianType (Geospatial.GeoPoint coordPoint) = case Geometry.geoPositionWithoutCRSToCoordinateType coordPoint of Just coordinateType -> Endian.builderEndianType endianType <> builderWkbGeom endianType (Geometry.WkbGeom Geometry.Point coordinateType) <> builderCoordPoint endianType coordPoint Nothing -> Monoid.mempty builderMultiPoint :: Geometry.BuilderWkbGeometryType -> Endian.EndianType -> Geospatial.GeoMultiPoint -> ByteStringBuilder.Builder builderMultiPoint builderWkbGeom endianType (Geospatial.GeoMultiPoint coordPoints) = Endian.builderEndianType endianType <> builderWkbGeom endianType (Geometry.WkbGeom Geometry.MultiPoint coordType) <> Endian.builderFourBytes endianType (fromIntegral $ length coordPoints) <> Foldable.foldMap (builderPoint builderWkbGeom endianType . Geospatial.GeoPoint) coordPoints where coordType = Geometry.coordTypeOfSequence coordPoints builderCoordPoint :: Endian.EndianType -> Geospatial.GeoPositionWithoutCRS -> ByteStringBuilder.Builder builderCoordPoint endianType coordPoint = case coordPoint of Geospatial.GeoEmpty -> Monoid.mempty Geospatial.GeoPointXY (Geospatial.PointXY x y) -> Foldable.foldMap builderDouble [x, y] Geospatial.GeoPointXYZ (Geospatial.PointXYZ x y z) -> Foldable.foldMap builderDouble [x, y, z] Geospatial.GeoPointXYZM (Geospatial.PointXYZM x y z m) -> Foldable.foldMap builderDouble [x, y, z, m] where builderDouble = Endian.builderDouble endianType builderCoordPoints :: Endian.EndianType -> Sequence.Seq Geospatial.GeoPositionWithoutCRS -> ByteStringBuilder.Builder builderCoordPoints endianType = Foldable.foldMap (builderCoordPoint endianType)