module Geometry.Shapefile.ReadShp ( readShpFile,
readShpData,
) where
import Control.Monad ( replicateM, when )
import Control.Monad.Loops ( whileM )
import Data.Binary.Get
import Data.Binary.IEEE754 ( getFloat64le )
import qualified Data.ByteString.Lazy as BL
import Geometry.Shapefile.Internal
import Geometry.Shapefile.Types
readShpFile :: String -> IO ShpData
readShpFile fp = readShpData <$> BL.readFile fp
readShpData :: BL.ByteString -> ShpData
readShpData = runGet $ do
shpH <- getShpHeader
shpRs <- whileM (not <$> isEmpty) getShpRec
return ShpData { shpHeader = shpH,
dbfFieldDescs = Nothing,
shpRecs = shpRs }
getShpHeader :: Get ShpHeader
getShpHeader = do
fc <- getIntBE
when (fc /= 9994) (error "getShpHeader: Input file is not a SHP file")
_ <- replicateM 5 getWord32be
shpLen <- getIntBE
shpV <- getIntLE
shpT <- getShpType
shpbb <- getShpBBox shpT
return ShpHeader { shpFileLength = shpLen,
shpVersion = shpV,
shpType = shpT,
shpBB = shpbb }
getShpType :: Get ShpType
getShpType = shpTypeFromId <$> getIntLE
getShpBBox :: ShpType -> Get ShpBBox
getShpBBox t = do
[xMin, yMin, xMax, yMax] <- replicateM 4 getFloat64le
[zMin, zMax] <- let ds = replicateM 2 getFloat64le
in if t `elem` zTypes
then map Just <$> ds
else ds >> return [Nothing, Nothing]
[mMin, mMax] <- let ds = replicateM 2 getFloat64le
in if t `elem` mTypes
then map Just <$> ds
else ds >> return [Nothing, Nothing]
return ShpBBox { shpXMin = xMin,
shpXMax = xMax,
shpYMin = yMin,
shpYMax = yMax,
shpZMin = zMin,
shpZMax = zMax,
shpMMin = mMin,
shpMMax = mMax }
getShpRec :: Get ShpRec
getShpRec = do
recNum <- getIntBE
recLen <- getIntBE
recType <- getShpType
recContents <- if recType == ShpNull
then return Nothing
else Just <$> getRecContents recType
return ShpRec { shpRecNum = recNum,
shpRecLen = recLen,
shpRecContents = recContents,
shpRecLabel = Nothing,
shpRecType = recType }
getRecContents :: ShpType -> Get RecContents
getRecContents t = case t of
ShpPoint -> do
p <- getPoint
return $ RecPoint p
ShpPointM -> do
p <- getPoint
m <- getFloat64le
return $ RecPointM { pmPoint = p, pmM = m }
ShpPointZ -> do
p <- getPoint
z <- getFloat64le
m <- getFloat64le
return $ RecPointZ { pzPoint = p, pzZ = z, pzM = m }
ShpMultiPoint -> do
(bb, nPoints, points) <- getPointsData
return RecMultiPoint { recMPBBox = bb,
recMPNumPoints = nPoints,
recMPPoints = points }
ShpMultiPointM -> do
(bb, nPoints, points) <- getPointsData
(mMin, mMax, ms) <- getMData nPoints
return RecMultiPointM { recMPMBBox = bb,
recMPMNumPoints = nPoints,
recMPMPoints = points,
recMPMMRange = (mMin, mMax),
recMPMMs = ms}
ShpMultiPointZ -> do
(bb, nPoints, points) <- getPointsData
(zMin, zMax, zs) <- getZData nPoints
(mMin, mMax, ms) <- getMData nPoints
return RecMultiPointZ { recMPZBBox = bb,
recMPZNumPoints = nPoints,
recMPZPoints = points,
recMPZZRange = (zMin, zMax),
recMPZZs = zs,
recMPZMRange = (mMin, mMax),
recMPZMs = ms }
ShpPolyLine -> do
(bb, nParts, nPoints, partLengths, pntLists) <- getPolyData
return RecPolyLine { recPolLBBox = bb,
recPolLNumParts = nParts,
recPolLNumPoints = nPoints,
recPolLPartLengths = partLengths,
recPolLPoints = pntLists }
ShpPolyLineM -> do
(bb, nParts, nPoints, partLengths, pntLists) <- getPolyData
(mMin, mMax, ms) <- getMData nPoints
return RecPolyLineM { recPolLMBBox = bb,
recPolLMNumParts = nParts,
recPolLMNumPoints = nPoints,
recPolLMPartLengths = partLengths,
recPolLMPoints = pntLists,
recPolLMMRange = (mMin, mMax),
recPolLMMs = ms }
ShpPolyLineZ -> do
(bb, nParts, nPoints, partLengths, pntLists) <- getPolyData
(zMin, zMax, zs) <- getZData nPoints
(mMin, mMax, ms) <- getMData nPoints
return RecPolyLineZ { recPolLZBBox = bb,
recPolLZNumParts = nParts,
recPolLZNumPoints = nPoints,
recPolLZPartLengths = partLengths,
recPolLZPoints = pntLists,
recPolLZZRange = (zMin, zMax),
recPolLZZs = zs,
recPolLZMRange = (mMin, mMax),
recPolLZMs = ms }
ShpPolygon -> do
(bb, nParts, nPoints, partLengths, pntLists) <- getPolyData
return RecPolygon { recPolBBox = bb,
recPolNumParts = nParts,
recPolNumPoints = nPoints,
recPolPartLengths = partLengths,
recPolPoints = pntLists }
ShpPolygonM -> do
(bb, nParts, nPoints, partLengths, pntLists) <- getPolyData
(mMin, mMax, ms) <- getMData nPoints
return RecPolygonM { recPolMBBox = bb,
recPolMNumParts = nParts,
recPolMNumPoints = nPoints,
recPolMPartLengths = partLengths,
recPolMPoints = pntLists,
recPolMMRange = (mMin, mMax),
recPolMMs = ms }
ShpPolygonZ -> do
(bb, nParts, nPoints, partLengths, pntLists) <- getPolyData
(zMin, zMax, zs) <- getZData nPoints
(mMin, mMax, ms) <- getMData nPoints
return RecPolygonZ { recPolZBBox = bb,
recPolZNumParts = nParts,
recPolZNumPoints = nPoints,
recPolZPartLengths = partLengths,
recPolZPoints = pntLists,
recPolZZRange = (zMin, zMax),
recPolZZs = zs,
recPolZMRange = (mMin, mMax),
recPolZMs = ms }
ShpNull ->
return RecNull
ShpMultiPatch ->
error "getShpRec: MultiPatch type is not supported, sorry!"
getPointsData :: Get (RecBBox, Int, [Point])
getPointsData = do
bb <- getRecBBox
nPoints <- getIntLE
points <- replicateM nPoints getPoint
return (bb, nPoints, points)
getPolyData :: Get (RecBBox, Int, Int, [Int], [[Point]])
getPolyData = do
bb <- getRecBBox
nParts <- getIntLE
nPoints <- getIntLE
partIndices <- replicateM nParts getIntLE
let partLengths = steps $ partIndices ++ [nPoints]
pntLists <- mapM getPointList partLengths
return (bb, nParts, nPoints, partLengths, pntLists)
getMData :: Int -> Get (Double, Double, [Double])
getMData nPoints = do
[mMin, mMax] <- replicateM 2 getFloat64le
ms <- replicateM nPoints getFloat64le
return (mMin, mMax, ms)
getZData :: Int -> Get (Double, Double, [Double])
getZData = getMData
getRecBBox :: Get RecBBox
getRecBBox = do
[xMin, yMin, xMax, yMax] <- replicateM 4 getFloat64le
return RecBBox { recXMin = xMin,
recXMax = xMax,
recYMin = yMin,
recYMax = yMax }