packages feed

readshp (empty) → 0.1.0.0

raw patch · 9 files changed

+676/−0 lines, 9 filesdep +basedep +binarydep +bytestringsetup-changed

Dependencies added: base, binary, bytestring, data-binary-ieee754, filepath, monad-loops

Files

+ Geometry/Shapefile.hs view
@@ -0,0 +1,16 @@+{-|+Module      : Geometry.Shapefile+Description : Code for reading ESRI shapefiles.+              Developed for intelligence2integrity.+Author      : Sam van Herwaarden <samvherwaarden@gmail.com>+-}+module Geometry.Shapefile ( module Geometry.Shapefile.MergeShpDbf,+                            module Geometry.Shapefile.ReadDbf,+                            module Geometry.Shapefile.ReadShp,+                            module Geometry.Shapefile.Types+                          ) where++import Geometry.Shapefile.MergeShpDbf+import Geometry.Shapefile.ReadDbf+import Geometry.Shapefile.ReadShp+import Geometry.Shapefile.Types
+ Geometry/Shapefile/Internal.hs view
@@ -0,0 +1,58 @@+{-|+Module      : Geometry.Shapefile.Internal+Description : Some functions for reuse inside the library+Author      : Sam van Herwaarden <samvherwaarden@gmail.com>+-}++module Geometry.Shapefile.Internal where++import Control.Monad ( replicateM )+import Data.Binary.Get+import Data.Binary.IEEE754 ( getFloat64le )+import qualified Data.ByteString.Char8 as C++-- | 8-bit Int value+getInt8 :: Get Int+getInt8 = fromIntegral <$> getWord8++-- | 32-bit big-endian Int+getIntBE :: Get Int+getIntBE = fromIntegral <$> getWord32be++-- | 16-bit big-endian Int+getInt16BE :: Get Int+getInt16BE = fromIntegral <$> getWord16be++-- | 32-bit little-endian Int+getIntLE :: Get Int+getIntLE = fromIntegral <$> getWord32le++-- | 16-bit little-endian Int+getInt16LE :: Get Int+getInt16LE = fromIntegral <$> getWord16le++-- | String of length `l`+getString :: Int -> Get String+getString l = filter (/= '\NUL') . C.unpack <$> getByteString l++-- | Char (presumably 8-bit)+getCharVal :: Get Char+getCharVal = C.head <$> getByteString 1++type Point = (Double, Double)++-- | Two doubles making up a point with two coordinates+getPoint :: Get Point+getPoint = do x <- getFloat64le+              y <- getFloat64le+              return (x, y)++-- | Get the increments between list values+steps :: Num a => [a] -> [a]+steps         [] = []+steps     (_:[]) = []+steps (x1:x2:xs) = x2 - x1 : steps (x2:xs)++-- | List of points of length `numPoints`+getPointList :: Int -> Get [Point]+getPointList numPoints = replicateM numPoints getPoint
+ Geometry/Shapefile/MergeShpDbf.hs view
@@ -0,0 +1,30 @@+{-|+Module      : Geometry.Shapefile.MergeShpDbf+Description : Code for reading ESRI shapefiles with DBF data+Author      : Sam van Herwaarden <samvherwaarden@gmail.com>+-}++module Geometry.Shapefile.MergeShpDbf ( readShpWithDbf ) where++import System.FilePath++import qualified Data.ByteString.Lazy as BL++import Geometry.Shapefile.ReadDbf+import Geometry.Shapefile.ReadShp+import Geometry.Shapefile.Types++-- | Read a shp-file from `fp`, and a dbf file from the same path but with+--   modified extension.+readShpWithDbf :: String -> IO ShpData+readShpWithDbf fp = do+  shpData <- readShpData <$> BL.readFile fp+  let dbfPath = dropExtension fp ++ ".dbf"+  dbfData <- readDbfData <$> BL.readFile dbfPath+  return shpData {+      dbfFieldDescs = Just $ dbfFields dbfData,+      shpRecs = zipWith addRecLabel (shpRecs shpData) (dbfRecords dbfData)+    }++addRecLabel :: ShpRec -> [DbfRecord] -> ShpRec+addRecLabel sr labels = sr { shpRecLabel = Just labels }
+ Geometry/Shapefile/ReadDbf.hs view
@@ -0,0 +1,63 @@+{-|+Module      : Geometry.Shapefile.ReadDbf+Description : Code for reading DBF files.+              Not robust, handle with care. YMMV+              Based on http://www.dbf2002.com/dbf-file-format.html+Author      : Sam van Herwaarden <samvherwaarden@gmail.com>+-}++module Geometry.Shapefile.ReadDbf ( readDbfFile,+                                    readDbfData+                                  ) where++import Control.Monad       ( replicateM )+import Control.Monad.Loops ( whileM )+import Data.Binary.Get++import qualified Data.ByteString.Lazy as BL++import Geometry.Shapefile.Internal+import Geometry.Shapefile.Types++-- | Read dbf file at `fp` into resident data format `DbfData`+readDbfFile :: String -> IO DbfData+readDbfFile fp = readDbfData <$> BL.readFile fp++-- | Parse a ByteString containing DBF data+readDbfData :: BL.ByteString -> DbfData+readDbfData = runGet $ do+  _           <- getByteString 4   -- DBF File type+  numRecs     <- getIntLE          -- Number of records+  firstRecPos <- getInt16LE        -- Position of first record+  _           <- getByteString 22  -- Rec len, table flags, code page mark+  -- Field descriptors+  let fieldsRemain = (< firstRecPos - 1) . fromIntegral <$> bytesRead+  fields      <- whileM fieldsRemain getDbfFieldDesc+  _           <- getWord8          -- Skip spacer+  -- Records+  recs        <- replicateM numRecs (getWord8 >> mapM getDbfRecord fields)+  -- Everything together+  return DbfData { dbfNumRecs = numRecs,+              dbfFields  = fields,+              dbfRecords = recs }++-- | Read header field descriptor+getDbfFieldDesc :: Get DbfFieldDesc+getDbfFieldDesc = do+  name     <- getString 11     -- Field name+  fieldT   <- getCharVal       -- Field type+  _        <- getIntLE         -- Field displacement (?)+  len      <- getInt8          -- Field length+  _        <- getByteString 15 -- Num decimal/field flags/autoincrement+  return DbfFieldDesc { fieldName = name,+                   fieldType = fieldT,+                   fieldLen  = len }++-- | Read a record. Not all record types are (meaningfully) implemented.+getDbfRecord :: DbfFieldDesc -> Get DbfRecord+getDbfRecord field = let n = fieldLen field in+  case fieldType field of+    'C' -> DbfString <$> getString     n+    'N' -> DbfNum    <$> getString     n+    _   -> DbfBS     <$> getByteString n+
+ Geometry/Shapefile/ReadShp.hs view
@@ -0,0 +1,247 @@+{-|+Module      : Geometry.Shapefile.ReadShp+Description : Code for reading the binary ESRI Shapefile format.+              https://www.esri.com/library/whitepapers/pdfs/shapefile.pdf+Author      : Sam van Herwaarden <samvherwaarden@gmail.com>+-}++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++-- | Read shapefile at `fp` to resident data structure `ShpData`+readShpFile :: String -> IO ShpData+readShpFile fp = readShpData <$> BL.readFile fp++-- | Parse a shapefile ByteString+readShpData :: BL.ByteString -> ShpData+readShpData = runGet $ do+  shpH  <- getShpHeader+  shpRs <- whileM (not <$> isEmpty) getShpRec+  return ShpData { shpHeader     = shpH,+              dbfFieldDescs = Nothing, -- fill using readDbfData+              shpRecs       = shpRs }++-- | Header of the shapefile+getShpHeader :: Get ShpHeader+getShpHeader = do+  fc      <- getIntBE                 -- File code+  when (fc /= 9994) (error "getShpHeader: Input file is not a SHP file")+  _       <- replicateM 5 getWord32be -- Unused data+  shpLen  <- getIntBE                 -- File length+  shpV    <- getIntLE                 -- Version (little-endian!)+  shpT    <- getShpType               -- Shape type+  shpbb   <- getShpBBox shpT          -- Bounding box+  return ShpHeader { shpFileLength = shpLen,+                shpVersion    = shpV,+                shpType       = shpT,+                shpBB         = shpbb }++-- | Get type of the record(s)+getShpType :: Get ShpType+getShpType = shpTypeFromId <$> getIntLE++-- | Read a bounding box for relevant record type.+--   Not all bounding boxes have Z/M coordinates.+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 }++-- | Read a single record+getShpRec :: Get ShpRec+getShpRec = do+  recNum      <- getIntBE             -- Record number+  recLen      <- getIntBE             -- Record length+  recType     <- getShpType           -- Shape type+  recContents <- if recType == ShpNull -- Record contents+                    then return Nothing+                    else Just <$> getRecContents recType+  return ShpRec { shpRecNum      = recNum,+             shpRecLen      = recLen,+             shpRecContents = recContents,+             shpRecLabel    = Nothing,+             shpRecType     = recType }++-- | Get record contents for record of given type+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!"++-- | Recurring pattern of bounding box with a number of points+getPointsData :: Get (RecBBox, Int, [Point])+getPointsData = do+  bb      <- getRecBBox+  nPoints <- getIntLE+  points  <- replicateM nPoints getPoint+  return (bb, nPoints, points)++-- | Recurring pattern of a bounding box with polygon data+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)++-- | Recurring pattern of M-valued data+getMData :: Int -> Get (Double, Double, [Double])+getMData nPoints = do+  [mMin, mMax] <- replicateM 2 getFloat64le+  ms           <- replicateM nPoints getFloat64le+  return (mMin, mMax, ms)++-- | Recurring pattern of Z-valued data (same structure as M-valued data)+getZData :: Int -> Get (Double, Double, [Double])+getZData = getMData++-- | Record bounding boxes contain no information on M/Z+getRecBBox :: Get RecBBox+getRecBBox = do+  [xMin, yMin, xMax, yMax] <- replicateM 4 getFloat64le+  return RecBBox { recXMin = xMin,+              recXMax = xMax,+              recYMin = yMin,+              recYMax = yMax }+
+ Geometry/Shapefile/Types.hs view
@@ -0,0 +1,207 @@+{-|+Module      : Geometry.Shapefile.Types+Description : Type definitions used by the library. Currently (24 Nov 2015)+              MultiPatch record types are not implemented. Only Point and+              Polygon are (somewhat) tested. YMMV+              https://www.esri.com/library/whitepapers/pdfs/shapefile.pdf+Author      : Sam van Herwaarden <samvherwaarden@gmail.com>+-}++module Geometry.Shapefile.Types where++import qualified Data.ByteString as BS++import Geometry.Shapefile.Internal++-- * SHP Types++data ShpData =+  ShpData {+    shpHeader     :: ShpHeader,+    dbfFieldDescs :: Maybe [DbfFieldDesc],+    shpRecs       :: [ShpRec]+  } deriving (Eq, Show)++data ShpHeader =+  ShpHeader {+    shpFileLength :: Int,+    shpVersion    :: Int,+    shpType       :: ShpType,+    shpBB         :: ShpBBox+  } deriving (Eq, Show)++data ShpType = ShpNull+             | ShpPoint+             | ShpPolyLine+             | ShpPolygon+             | ShpMultiPoint+             | ShpPointZ+             | ShpPolyLineZ+             | ShpPolygonZ+             | ShpMultiPointZ+             | ShpPointM+             | ShpPolyLineM+             | ShpPolygonM+             | ShpMultiPointM+             | ShpMultiPatch+               deriving (Eq, Show)++-- | Types with Z values+zTypes :: [ShpType]+zTypes = [ShpPointZ, ShpPolyLineZ, ShpPolygonZ, ShpMultiPointZ]++-- | Types with M values+mTypes :: [ShpType]+mTypes = [ShpPointM, ShpPolyLineM, ShpPolygonM, ShpMultiPointM]++-- | Numeric codes used for record types+shpTypeFromId :: Int -> ShpType+shpTypeFromId i =+  case i of 0  -> ShpNull+            1  -> ShpPoint+            3  -> ShpPolyLine+            5  -> ShpPolygon+            8  -> ShpMultiPoint+            11 -> ShpPointZ+            13 -> ShpPolyLineZ+            15 -> ShpPolygonZ+            18 -> ShpMultiPointZ+            21 -> ShpPointM+            23 -> ShpPolyLineM+            25 -> ShpPolygonM+            28 -> ShpMultiPointM+            31 -> ShpMultiPatch+            _  -> error "shpTypeFromId: Unknown Shape Type"++data ShpBBox =+  ShpBBox {+    shpXMin :: Double,+    shpXMax :: Double,+    shpYMin :: Double,+    shpYMax :: Double,+    shpZMin :: Maybe Double,+    shpZMax :: Maybe Double,+    shpMMin :: Maybe Double,+    shpMMax :: Maybe Double+  } deriving (Eq, Show)+++data ShpRec =+  ShpRec {+    shpRecNum      :: Int,+    shpRecLen      :: Int,+    shpRecContents :: Maybe RecContents, -- Can be null type+    shpRecLabel    :: Maybe [DbfRecord], -- Should be usable without dbf+    shpRecType     :: ShpType+  } deriving (Eq, Show)++data RecContents =+    RecNull+  | RecPoint Point+  | RecPointM {+      pmPoint             :: Point,+      pmM                 :: Double }+  | RecPointZ {+      pzPoint             :: Point,+      pzZ                 :: Double,+      pzM                 :: Double }+  | RecMultiPoint {+      recMPBBox           :: RecBBox,+      recMPNumPoints      :: Int,+      recMPPoints         :: [Point] }+  | RecMultiPointM {+      recMPMBBox          :: RecBBox,+      recMPMNumPoints     :: Int,+      recMPMPoints        :: [Point],+      recMPMMRange        :: (Double, Double),+      recMPMMs            :: [Double] }+  | RecMultiPointZ {+      recMPZBBox          :: RecBBox,+      recMPZNumPoints     :: Int,+      recMPZPoints        :: [Point],+      recMPZZRange        :: (Double, Double),+      recMPZZs            :: [Double],+      recMPZMRange        :: (Double, Double),+      recMPZMs            :: [Double] }+  | RecPolyLine {+      recPolLBBox         :: RecBBox,+      recPolLNumParts     :: Int,+      recPolLNumPoints    :: Int,+      recPolLPartLengths  :: [Int],+      recPolLPoints       :: [[Point]] }+  | RecPolyLineM {+      recPolLMBBox        :: RecBBox,+      recPolLMNumParts    :: Int,+      recPolLMNumPoints   :: Int,+      recPolLMPartLengths :: [Int],+      recPolLMPoints      :: [[Point]],+      recPolLMMRange      :: (Double, Double),+      recPolLMMs          :: [Double] }+  | RecPolyLineZ {+      recPolLZBBox        :: RecBBox,+      recPolLZNumParts    :: Int,+      recPolLZNumPoints   :: Int,+      recPolLZPartLengths :: [Int],+      recPolLZPoints      :: [[Point]],+      recPolLZZRange      :: (Double, Double),+      recPolLZZs          :: [Double],+      recPolLZMRange      :: (Double, Double),+      recPolLZMs          :: [Double] }+  | RecPolygon {+      recPolBBox          :: RecBBox,+      recPolNumParts      :: Int,+      recPolNumPoints     :: Int,+      recPolPartLengths   :: [Int],+      recPolPoints        :: [[Point]] }+  | RecPolygonM {+      recPolMBBox         :: RecBBox,+      recPolMNumParts     :: Int,+      recPolMNumPoints    :: Int,+      recPolMPartLengths  :: [Int],+      recPolMPoints       :: [[Point]],+      recPolMMRange       :: (Double, Double),+      recPolMMs           :: [Double] }+  | RecPolygonZ {+      recPolZBBox         :: RecBBox,+      recPolZNumParts     :: Int,+      recPolZNumPoints    :: Int,+      recPolZPartLengths  :: [Int],+      recPolZPoints       :: [[Point]],+      recPolZZRange       :: (Double, Double),+      recPolZZs           :: [Double],+      recPolZMRange       :: (Double, Double),+      recPolZMs           :: [Double] }+  -- | RecMultiPatch+    deriving (Eq, Show)++data RecBBox =+  RecBBox {+    recXMin :: Double,+    recXMax :: Double,+    recYMin :: Double,+    recYMax :: Double+  } deriving (Eq, Show)++-- * DBF Types++data DbfData =+  DbfData {+    dbfNumRecs :: Int,+    dbfFields :: [DbfFieldDesc],+    dbfRecords :: [[DbfRecord]]+  } deriving (Eq, Show)++data DbfFieldDesc =+  DbfFieldDesc {+    fieldName :: String,+    fieldType :: Char,+    fieldLen :: Int+  } deriving (Eq, Show)++-- | Not all record types are implemented. Use your favorite method to+--   convert any bytestring (and feel free to add functionality).+data DbfRecord = DbfString String+               | DbfNum String+               | DbfBS BS.ByteString+                 deriving (Eq, Show)+
+ LICENSE view
@@ -0,0 +1,20 @@+Copyright (c) 2015 Sam van Herwaarden++Permission is hereby granted, free of charge, to any person obtaining+a copy of this software and associated documentation files (the+"Software"), to deal in the Software without restriction, including+without limitation the rights to use, copy, modify, merge, publish,+distribute, sublicense, and/or sell copies of the Software, and to+permit persons to whom the Software is furnished to do so, subject to+the following conditions:++The above copyright notice and this permission notice shall be included+in all copies or substantial portions of the Software.++THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ readshp.cabal view
@@ -0,0 +1,33 @@+name:                readshp+version:             0.1.0.0+synopsis:            Code for reading ESRI Shapefiles.+description:         Provides code for binary parsing of ESRI shapefiles,+                     where the .shp and the .dbf file are read. The code has+                     not been tested on a large variety of shapefiles so+                     your mileage may vary, but so far it has worked with+                     no problems. At the moment only text and number fields+                     in DBF files are read (the others are stored as+                     ByteString).+license:             MIT+license-file:        LICENSE+author:              Sam van Herwaarden+maintainer:          samvherwaarden@gmail.com+category:            GIS+build-type:          Simple+cabal-version:       >=1.10++library+  exposed-modules:     Geometry.Shapefile,+                       Geometry.Shapefile.MergeShpDbf,+                       Geometry.Shapefile.ReadDbf,+                       Geometry.Shapefile.ReadShp,+                       Geometry.Shapefile.Types+  other-modules:       Geometry.Shapefile.Internal+  build-depends:       base >=4.8 && <4.9,+                       binary,+                       bytestring,+                       data-binary-ieee754,+                       filepath,+                       monad-loops+  default-language:    Haskell2010+  ghc-options:         -Wall -fno-warn-unused-do-bind