SWMMoutGetMB-0.1.0.1: src/Water/SWMM.hs
-- |
-- Module : Water.SWMM
-- Copyright : (C) 2014 Siddhanathan Shanmugam
-- License : LGPL (see LICENSE)
-- Maintainer : siddhanathan@gmail.com
-- Portability : very
--
-- Parser for SWMM 5 Binary .OUT files
--
{-# LANGUAGE Trustworthy #-}
module Water.SWMM ( SWMMObject(..)
, Header(..)
, ObjectIds(..)
, Properties(..)
, ObjectProperties(..)
, Variables(..)
, ReportingVariables(..)
, ReportingInterval(..)
, ValuesForOneDateTime(..)
, ComputedResult(..)
, ClosingRecord(..)
, parseSWMMBinary
) where
import safe Data.Binary.Get (getWord32le, runGet, Get, getLazyByteString)
import safe Control.Applicative ((<$>), (<*>))
import safe qualified Data.ByteString.Lazy as BL (ByteString, pack, unpack, drop, length)
import safe qualified Data.ByteString.Lazy.Char8 as BLC (ByteString, pack, unpack)
import safe Data.List.Split (chunksOf)
import safe Control.Monad (replicateM)
-- There is an implicit assumption that IEEE-754 is the in-memory representation.
-- If we assume this is the case, then this is safe.
import Data.Binary.IEEE754 (getFloat32le, getFloat64le)
data SWMMObject = SWMMObject { header :: Header
, ids :: ObjectIds
, properties :: ObjectProperties
, variables :: ReportingVariables
, intervals :: ReportingInterval
, result :: ComputedResult
, closingRecord :: ClosingRecord
} deriving (Show, Eq)
data Header = Header { headerIdNumber :: Integer
, versionNumber :: Integer
, codeNumber :: Integer
, numberOfSubcatchments :: Integer
, numberOfNodes :: Integer
, numberOfLinks :: Integer
, numberOfPollutants :: Integer
} deriving (Show, Eq)
data ObjectIds = ObjectIds { subcatchmentIds :: [BLC.ByteString]
, nodeIds :: [BLC.ByteString]
, linkIds :: [BLC.ByteString]
, pollutantIds :: [BLC.ByteString]
, concentrationIds :: [Integer]
} deriving (Show, Eq)
data ObjectProperties = ObjectProperties { subcatchmentProperties :: Properties
, nodeProperties :: Properties
, linkProperties :: Properties
} deriving (Show, Eq)
data ReportingVariables = ReportingVariables { subcatchmentVariables :: Variables
, nodeVariables :: Variables
, linkVariables :: Variables
, systemVariables :: Variables
} deriving (Show, Eq)
data ReportingInterval = ReportingInterval { startDateTime :: Double
, timeIntervals :: Integer
} deriving (Show, Eq)
data ComputedResult = ComputedResult { allDateTimes :: [ValuesForOneDateTime] }
deriving (Show, Eq)
data ClosingRecord = ClosingRecord { idBytePosition :: Integer
, propertiesBytePosition :: Integer
, resultBytePosition :: Integer
, numberOfPeriods :: Integer
, errorCode :: Integer
, closingIdNumber :: Integer
} deriving (Show, Eq)
type Ids = [BL.ByteString]
data Properties = Properties { numberOfProperties :: Integer
, codeNumberProperties :: [Integer]
, valueProperties :: [Float]
} deriving (Show, Eq)
data Variables = Variables { numberOfVariables :: Integer
, codeNumberVariables :: [Integer]
} deriving (Show, Eq)
data ValuesForOneDateTime = ValuesForOneDateTime { dateTimeValue :: Double
, subcatchmentValue :: [[Float]]
, nodeValue :: [[Float]]
, linkValue :: [[Float]]
, systemValue :: [Float]
} deriving (Show)
instance Ord ValuesForOneDateTime where
a <= b = dateTimeValue a <= dateTimeValue b
instance Eq ValuesForOneDateTime where
a == b = dateTimeValue a == dateTimeValue b
closingRecordSize :: Int
closingRecordSize = 6 * 4
getHeader :: Get Header
getHeader = Header <$> a
<*> a
<*> a
<*> a
<*> a
<*> a
<*> a
where a = getIntegerWord32le
getSWMMObject :: ClosingRecord -> Get SWMMObject
getSWMMObject closing = do
header <- getHeader
objectIds <- getObjectIds header
objectProperties <- getObjectProperties header
reportingVariables <- getReportingVariables
reportingIntervals <- getReportingIntervals
computedResult <- getComputedResults (numberOfPeriods closing) header reportingVariables
closingRecord <- getClosingRecords
return $ SWMMObject header
objectIds
objectProperties
reportingVariables
reportingIntervals
computedResult
closingRecord
parseSWMMBinary :: BL.ByteString -> SWMMObject
parseSWMMBinary input = do
let closingRecord = runGet getClosingRecords (getClosingByteString input)
swmmObject = runGet (getSWMMObject closingRecord) input
swmmObject
getIntegerWord32le :: Get Integer
getIntegerWord32le = fromIntegral <$> getWord32le
getClosingByteString :: BL.ByteString -> BL.ByteString
getClosingByteString input = BL.drop (BL.length input - fromIntegral closingRecordSize) input
getObjectIds :: Header -> Get ObjectIds
getObjectIds header =
ObjectIds <$> getIds (numberOfSubcatchments header)
<*> getIds (numberOfNodes header)
<*> getIds (numberOfLinks header)
<*> getIds (numberOfPollutants header)
<*> replicateM (fromInteger . numberOfPollutants $ header) getIntegerWord32le
getIds :: Integer -> Get [BL.ByteString]
getIds n = replicateM (fromInteger n)
(getIntegerWord32le >>= getLazyByteString . fromInteger)
getVariables :: Get Variables
getVariables = do
number <- getIntegerWord32le
codeNumbers <- replicateM (fromInteger number) getIntegerWord32le
return $ Variables number codeNumbers
getProperties :: Integer -> Get Properties
getProperties n = do
number <- getIntegerWord32le
codeNumbers <- replicateM (fromInteger number) getIntegerWord32le
values <- replicateM (fromInteger (number * n)) getFloat32le
return $ Properties number codeNumbers values
getObjectProperties :: Header -> Get ObjectProperties
getObjectProperties header = ObjectProperties <$> getProperties (numberOfSubcatchments header)
<*> getProperties (numberOfNodes header)
<*> getProperties (numberOfLinks header)
getReportingVariables :: Get ReportingVariables
getReportingVariables = ReportingVariables <$> a
<*> a
<*> a
<*> a
where a = getVariables
getReportingIntervals :: Get ReportingInterval
getReportingIntervals = ReportingInterval <$> getFloat64le
<*> getIntegerWord32le
getResults :: Header -> ReportingVariables -> Get ValuesForOneDateTime
getResults header report = do
ValuesForOneDateTime <$> getFloat64le
<*> getSplitValues (numberOfSubcatchments header * ns) ns
<*> getSplitValues (numberOfNodes header * nn) nn
<*> getSplitValues (numberOfLinks header * nl) nl
<*> getValues (numberOfVariables . systemVariables $ report)
where ns = (numberOfVariables . subcatchmentVariables) report
nn = (numberOfVariables . nodeVariables) report
nl = (numberOfVariables . linkVariables) report
getComputedResults :: Integer -> Header -> ReportingVariables -> Get ComputedResult
getComputedResults n header report = ComputedResult <$> replicateM (fromInteger n) (getResults header report)
getValues :: Integer -> Get [Float]
getValues n = replicateM (fromInteger n) getFloat32le
getSplitValues :: Integer -> Integer -> Get [[Float]]
getSplitValues n nv = chunksOf (fromInteger nv) <$> replicateM (fromInteger n) getFloat32le
getClosingRecords :: Get ClosingRecord
getClosingRecords = ClosingRecord <$> a
<*> a
<*> a
<*> a
<*> a
<*> a
where a = getIntegerWord32le