packages feed

wkt-geom-0.0.12: src/Data/Internal/Wkb/Point.hs

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 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)