hgrib-0.3.0.0: src/Data/Grib.hs
{- |
Module : Data.Grib
Description : High-level GRIB library.
Copyright : (c) Mattias Jakobsson 2015
License : GPL-3
Maintainer : mjakob422@gmail.com
Stability : unstable
Portability : portable
-}
module Data.Grib ( -- *The GRIB Monad
GribIO
, runGribIO
, runGribIO_
, GribEnv
-- **Get values
--
-- |These operations may fail with:
--
-- * 'isGribException' 'GribNotFound' if the key
-- is missing.
, getDouble
, getLong
, getString
, getValues
-- **Set values
--
-- |These operations may fail with:
--
-- * 'isGribException' 'GribNotFound' if the key
-- is missing; or
--
-- * 'isGribException' 'GribReadOnly' if the key
-- is read-only.
, setDouble
, setLong
, setString
, setValues
-- **Utilities
, getFilename
, getIndex
, getHandle
, liftIO
) where
import Control.Monad ( void )
import Control.Monad.IO.Class ( liftIO )
import Control.Monad.Trans.Reader ( ReaderT, ask, runReaderT )
import Foreign ( allocaArray, allocaBytes )
-- Hack to have Applicative in base < 4.8 but avoid warning in base >= 4.8:
import Control.Applicative
import Prelude
import Data.Grib.Raw
-- Helper that extracts a list of grib handles from a file.
handles :: FilePath -> IO [GribHandle]
handles path = withBinaryCFile path ReadMode go
where go file = gribHandleNewFromFile defaultGribContext file >>=
maybe (return []) (\h' -> (h' :) <$> go file)
-- |The reader environment of 'GribIO' containing the current
-- filename, a 'GribHandle', and its index in the file.
data GribEnv = GribEnv
{ filename :: FilePath
, index :: Int
, handle :: GribHandle
}
-- Helper that generates a list of environments for GribIO.
envs :: FilePath -> IO [GribEnv]
envs path = fmap (zipWith3 GribEnv (repeat path) [0..]) (handles path)
-- |The 'GribIO' monad is a 'ReaderT' monad transformer over the
-- 'IO' monad with a 'GribEnv' environment.
type GribIO = ReaderT GribEnv IO
-- |Run an action on each GRIB message in a file and collect the
-- results.
--
-- This operation may fail with:
--
-- * any 'IOError' raised by 'openBinaryCFile';
--
-- * any 'Data.Grib.Exception.GribError' raised by
-- 'gribHandleNewFromFile'; or
--
-- * any other exception raised by the given 'GribIO' action.
runGribIO :: FilePath -- ^a path to a GRIB file
-> GribIO a -- ^an action to take on each GRIB message in the file
-> IO [a] -- ^the results of the actions
runGribIO path m = envs path >>= mapM (runReaderT m)
-- |Like 'runGribIO', but discard the results.
runGribIO_ :: FilePath -> GribIO a -> IO ()
runGribIO_ path m = envs path >>= mapM_ (runReaderT m)
-- |Return the name of the file being read.
getFilename :: GribIO FilePath
getFilename = fmap filename ask
-- |Return the zero-based index of the current message in the file.
getIndex :: GribIO Int
getIndex = fmap index ask
-- |Return the current 'GribHandle' for use with the 'Data.Grib.Raw'
-- GRIB API bindings.
getHandle :: GribIO GribHandle
getHandle = fmap handle ask
-- |Get the value for a key as a float.
getDouble :: Key -> GribIO Double
getDouble key = getHandle >>= liftIO . flip gribGetDouble key
-- |Get the value for a key as an integer.
getLong :: Key -> GribIO Int
getLong key = getHandle >>= liftIO . flip gribGetLong key
-- |Get the value for a key as a string.
getString :: Key -> GribIO String
getString key = getHandle >>= \h -> do
n <- liftIO . gribGetLength h $ key
liftIO . allocaBytes n $ \bufr -> gribGetString h key bufr n
-- |Get the data values of the GRIB message as floats.
getValues :: GribIO [Double]
getValues = getHandle >>= \h -> do
n <- liftIO . gribGetSize h $ key
liftIO . allocaArray n $ \array -> gribGetDoubleArray h key array n
where key = "values"
-- Helper for the value setters below.
setGeneric :: (GribHandle -> Key -> a -> IO b) -> Key -> a -> GribIO b
setGeneric setter key value = getHandle >>= \h -> liftIO $ setter h key value
-- |Set the value of a key from a float.
setDouble :: Key -> Double -> GribIO ()
setDouble = setGeneric gribSetDouble
-- |Set the value of a key from an integer.
setLong :: Key -> Int -> GribIO ()
setLong = setGeneric gribSetLong
-- |Set the value of a key from a string.
setString :: Key -> String -> GribIO ()
setString key value = void $ setGeneric gribSetString key value
-- |Set the values of the GRIB message from floats.
setValues :: [Double] -> GribIO ()
setValues = setGeneric gribSetDoubleArray "values"