hgis 1.0.0.1 → 1.0.0.2
raw patch · 21 files changed
+698/−692 lines, 21 filesdep ~base
Dependency ranges changed: base
Files
- cabal.project.local +2/−0
- dbf/Geometry/Shapefile/Internal.hs +67/−0
- dbf/Geometry/Shapefile/MergeShpDbf.hs +27/−0
- dbf/Geometry/Shapefile/ReadDbf.hs +63/−0
- dbf/Geometry/Shapefile/ReadShp.hs +247/−0
- dbf/Geometry/Shapefile/Types.hs +217/−0
- dbf/LICENSE +20/−0
- docs/manual.tex +2/−2
- hgis.cabal +24/−17
- src/GIS/Graphics/Plot.hs +2/−1
- src/GIS/Graphics/Types.hs +11/−3
- src/GIS/Hylo.hs +7/−6
- src/GIS/Math/Spherical.hs +3/−3
- src/GIS/Types.hs +6/−3
- src/depends/readshp/Geometry/Shapefile.hs +0/−16
- src/depends/readshp/Geometry/Shapefile/Internal.hs +0/−67
- src/depends/readshp/Geometry/Shapefile/MergeShpDbf.hs +0/−28
- src/depends/readshp/Geometry/Shapefile/ReadDbf.hs +0/−63
- src/depends/readshp/Geometry/Shapefile/ReadShp.hs +0/−243
- src/depends/readshp/Geometry/Shapefile/Types.hs +0/−220
- src/depends/readshp/LICENSE +0/−20
cabal.project.local view
@@ -1,2 +1,4 @@ constraints: hgis +development +cairo +diagrams optimization: 2++max-backjumps: 120000
+ dbf/Geometry/Shapefile/Internal.hs view
@@ -0,0 +1,67 @@+{-|+Module : Geometry.Shapefile.Internal+Description : Some functions for reuse inside the library+Author : Sam van Herwaarden <samvherwaarden@gmail.com>+-}++module Geometry.Shapefile.Internal ( getInt8+ , getIntBE+ , getInt16BE+ , getIntLE+ , getInt16LE+ , getString+ , getPoint+ , getCharVal+ , getPointList+ , steps+ , Point+ ) where++import Control.Monad (replicateM)+import Data.Binary.Get hiding (getInt8)+import Data.Binary.IEEE754 (getFloat64le)+import qualified Data.ByteString.Char8 as ASCII++-- | 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 = fmap (filter (/= '\NUL') . ASCII.unpack) . getByteString++-- | Char (presumably 8-bit)+getCharVal :: Get Char+getCharVal = ASCII.head <$> getByteString 1++type Point = (Double, Double)++-- | Two doubles making up a point with two coordinates+getPoint :: Get Point+getPoint = (,) <$> getFloat64le <*> getFloat64le++-- | 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 = flip replicateM getPoint
+ dbf/Geometry/Shapefile/MergeShpDbf.hs view
@@ -0,0 +1,27 @@+{-|+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 qualified Data.ByteString.Lazy as BL+import Geometry.Shapefile.ReadDbf+import Geometry.Shapefile.ReadShp+import Geometry.Shapefile.Types+import System.FilePath++-- | Read a shp-file from `fp`, and a dbf file from the same path but with+-- modified extension.+readShpWithDbf :: FilePath -> IO ShpData+readShpWithDbf fp = do+ shpData <- readShpData <$> BL.readFile fp+ let dbfPath = dropExtension fp <> ".dbf"+ dbfData <- readDbfData <$> BL.readFile dbfPath+ pure shpData { dbfFieldDescs = Just $ dbfFields dbfData+ , shpRecs = zipWith addRecLabel (shpRecs shpData) (dbfRecords dbfData)+ }++addRecLabel :: ShpRec -> [DbfRecord] -> ShpRec+addRecLabel sr labels = sr { shpRecLabel = Just labels } -- FIXME add field name as string
+ dbf/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 hiding (getInt8)+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 :: FilePath -> 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+ pure 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+ pure 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+
+ dbf/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, 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+ pure 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+ pure 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 fmap Just <$> ds+ else ds >> pure [Nothing, Nothing]+ [mMin, mMax] <- let ds = replicateM 2 getFloat64le+ in if t `elem` mTypes+ then fmap Just <$> ds+ else ds >> pure [Nothing, Nothing]+ pure 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 pure Nothing+ else Just <$> getRecContents recType+ pure 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 -> RecPoint <$> getPoint++ ShpPointM -> do+ p <- getPoint+ m <- getFloat64le+ pure RecPointM { pmPoint = p, pmM = m }++ ShpPointZ -> do+ p <- getPoint+ z <- getFloat64le+ m <- getFloat64le+ pure RecPointZ { pzPoint = p, pzZ = z, pzM = m }++ ShpMultiPoint -> do+ (bb, nPoints, points) <- getPointsData+ pure RecMultiPoint { recMPBBox = bb,+ recMPNumPoints = nPoints,+ recMPPoints = points }++ ShpMultiPointM -> do+ (bb, nPoints, points) <- getPointsData+ (mMin, mMax, ms) <- getMData nPoints+ pure 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+ pure 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+ pure RecPolyLine { recPolLBBox = bb,+ recPolLNumParts = nParts,+ recPolLNumPoints = nPoints,+ recPolLPartLengths = partLengths,+ recPolLPoints = pntLists }++ ShpPolyLineM -> do+ (bb, nParts, nPoints, partLengths, pntLists) <- getPolyData+ (mMin, mMax, ms) <- getMData nPoints+ pure 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+ pure 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+ pure RecPolygon { recPolBBox = bb,+ recPolNumParts = nParts,+ recPolNumPoints = nPoints,+ recPolPartLengths = partLengths,+ recPolPoints = pntLists }++ ShpPolygonM -> do+ (bb, nParts, nPoints, partLengths, pntLists) <- getPolyData+ (mMin, mMax, ms) <- getMData nPoints+ pure 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+ pure RecPolygonZ { recPolZBBox = bb,+ recPolZNumParts = nParts,+ recPolZNumPoints = nPoints,+ recPolZPartLengths = partLengths,+ recPolZPoints = pntLists,+ recPolZZRange = (zMin, zMax),+ recPolZZs = zs,+ recPolZMRange = (mMin, mMax),+ recPolZMs = ms }++ ShpNull ->+ pure 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+ pure (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 <- traverse getPointList partLengths+ pure (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+ pure (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+ pure RecBBox { recXMin = xMin,+ recXMax = xMax,+ recYMin = yMin,+ recYMax = yMax }+
+ dbf/Geometry/Shapefile/Types.hs view
@@ -0,0 +1,217 @@+{-|+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 ( ShpData (..)+ , ShpHeader (..)+ , ShpType (..)+ , zTypes+ , mTypes+ , shpTypeFromId+ , RecBBox (..)+ , RecContents (..)+ , ShpRec (..)+ , ShpBBox (..)+ , DbfFieldDesc (..)+ , DbfData (..)+ , DbfRecord (..)+ ) where++import qualified Data.ByteString as BS+import Geometry.Shapefile.Internal++-- * SHP Types++data ShpData =+ ShpData {+ shpHeader :: ShpHeader,+ dbfFieldDescs :: Maybe [DbfFieldDesc],+ shpRecs :: [ShpRec]+ }++data ShpHeader =+ ShpHeader {+ shpFileLength :: Int,+ shpVersion :: Int,+ shpType :: ShpType,+ shpBB :: ShpBBox+ }++data ShpType = ShpNull+ | ShpPoint+ | ShpPolyLine+ | ShpPolygon+ | ShpMultiPoint+ | ShpPointZ+ | ShpPolyLineZ+ | ShpPolygonZ+ | ShpMultiPointZ+ | ShpPointM+ | ShpPolyLineM+ | ShpPolygonM+ | ShpMultiPointM+ | ShpMultiPatch+ deriving (Eq)++-- | 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+ }+++data ShpRec =+ ShpRec {+ shpRecNum :: Int,+ shpRecLen :: Int,+ shpRecContents :: Maybe RecContents, -- Can be null type+ shpRecLabel :: Maybe [DbfRecord], -- Should be usable without dbf+ shpRecType :: ShpType+ }++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++data RecBBox =+ RecBBox {+ recXMin :: Double,+ recXMax :: Double,+ recYMin :: Double,+ recYMax :: Double+ }++-- * DBF Types++data DbfData =+ DbfData {+ dbfNumRecs :: Int,+ dbfFields :: [DbfFieldDesc],+ dbfRecords :: [[DbfRecord]]+ }++data DbfFieldDesc =+ DbfFieldDesc {+ fieldName :: String,+ fieldType :: Char,+ fieldLen :: Int+ }++-- | 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 (Show)
+ dbf/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.
docs/manual.tex view
@@ -32,8 +32,8 @@ \begin{appendices} \section{Installation}- HGIS is supported and tested with nix and stack, available from- \url{http://nixos.org/nix/} and \url{https://haskellstack.org}, respectively.+ HGIS is supported and tested with cabal, available from+ \url{https://www.haskell.org/cabal/}. % opinionated: nix is best when you just want the binaries! \section{Using the libaries}
hgis.cabal view
@@ -1,6 +1,6 @@-cabal-version: 1.18+cabal-version: 2.0 name: hgis-version: 1.0.0.1+version: 1.0.0.2 license: BSD3 license-file: LICENSE copyright: Copyright: (c) 2016-2018 Vanessa McHale@@ -17,7 +17,7 @@ cabal.project.local extra-doc-files: README.md docs/manual.tex- src/depends/readshp/LICENSE+ dbf/LICENSE source-repository head type: darcs@@ -42,23 +42,16 @@ GIS.Math.Projections GIS.Math.Spherical GIS.Hylo- hs-source-dirs: src src/depends/readshp+ hs-source-dirs: src other-modules: GIS.Graphics.Types GIS.Math.Utils GIS.Utils GIS.Graphics.Plot GIS.Types- Geometry.Shapefile- Geometry.Shapefile.MergeShpDbf- Geometry.Shapefile.ReadDbf- Geometry.Shapefile.ReadShp- Geometry.Shapefile.Types- Geometry.Shapefile.Internal default-language: Haskell2010 default-extensions: DeriveGeneric OverloadedStrings- other-extensions: RankNTypes TemplateHaskell FlexibleInstances- RecordWildCards+ other-extensions: RankNTypes FlexibleInstances RecordWildCards ghc-options: -Wall -Wincomplete-uni-patterns -Wincomplete-record-updates -Wmissing-export-lists build-depends:@@ -70,11 +63,7 @@ directory -any, colour -any, data-default -any,- binary -any,- bytestring -any,- data-binary-ieee754 -any,- filepath -any,- monad-loops -any+ hgis-readshp -any if flag(cairo) exposed-modules:@@ -94,6 +83,24 @@ if flag(development) ghc-options: -Werror++library hgis-readshp+ exposed-modules:+ Geometry.Shapefile.Types+ Geometry.Shapefile.MergeShpDbf+ Geometry.Shapefile.ReadShp+ hs-source-dirs: dbf+ other-modules:+ Geometry.Shapefile.ReadDbf+ Geometry.Shapefile.Internal+ default-language: Haskell2010+ build-depends:+ base -any,+ binary -any,+ bytestring -any,+ data-binary-ieee754 -any,+ monad-loops -any,+ filepath -any test-suite hgis-test type: exitcode-stdio-1.0
src/GIS/Graphics/Plot.hs view
@@ -13,6 +13,7 @@ import Control.Arrow import Data.Colour.Names+import Data.Foldable (fold) import GIS.Graphics.Types hiding (title) import GIS.Math.Utils import GIS.Types@@ -52,6 +53,6 @@ -- concave. plotLabels :: [([Polygon], String)] -> Plot Double Double plotLabels points = toPlot texts- where pairs = fmap (flatten . first (shittyCentroid . concat)) points+ where pairs = fmap (flatten . first (shittyCentroid . fold)) points fontStyle = font_size .~ 15 $ font_weight .~ FontWeightBold $ def texts = plot_annotation_values .~ pairs $ plot_annotation_style .~ fontStyle $ def
src/GIS/Graphics/Types.hs view
@@ -1,5 +1,3 @@-{-# LANGUAGE TemplateHaskell #-}- -- | Module for types associated with generating maps. Includes lenses. module GIS.Graphics.Types ( Map (..) , projection@@ -19,7 +17,17 @@ , _labelledDistricts :: [([Polygon], String)] -- the data we actually want to map } -makeLenses ''Map+projection :: Lens' Map Projection+projection f s = fmap (\x -> s { _projection = x }) (f (_projection s))++title :: Lens' Map String+title f s = fmap (\x -> s { _title = x }) (f (_title s))++labelEntities :: Lens' Map Bool+labelEntities f s = fmap (\x -> s { _labelEntities = x }) (f (_labelEntities s))++labelledDistricts :: Lens' Map [([Polygon], String)]+labelledDistricts f s = fmap (\x -> s { _labelledDistricts = x }) (f (_labelledDistricts s)) instance Default Map where def = Map { _projection = id , _title = mempty , _labelEntities = False , _labelledDistricts = mempty }
src/GIS/Hylo.hs view
@@ -16,6 +16,7 @@ import Control.Lens hiding (lens) import Data.Default+import Data.Foldable (fold) import Data.List import Data.Maybe import Geometry.Shapefile.MergeShpDbf@@ -30,17 +31,17 @@ -- | Get the areas of various objects and return a string suitable for printing districtArea :: [District] -> String-districtArea districts = concat . intercalate (pure "\n") $ fmap (pure . show . distA) districts+districtArea districts = fold . intercalate (pure "\n") $ fmap (pure . show . distA) districts where distA (District _ label _ area _) = (label, sum area) -- TODO figure out which one is the correct one -- | Get the perimeters of various objects and return a string suitable for printing districtPerimeter :: [District] -> String-districtPerimeter districts = concat . intercalate (pure "\n") $ fmap (pure . show . distP) districts+districtPerimeter districts = fold . intercalate (pure "\n") $ fmap (pure . show . distP) districts where distP (District _ label perimeter _ _) = (label, perimeter) -- | Label with relative compactness districtCompactness :: [District] -> String-districtCompactness districts = concat . intercalate (pure "\n") $ fmap (pure . show . distC) districts+districtCompactness districts = fold . intercalate (pure "\n") $ fmap (pure . show . distC) districts where distC (District _ label _ _ compacticity) = (label, compacticity) -- | Given a projection and lists of districts, draw a map.@@ -80,12 +81,12 @@ shapes = extract id perimeters = extract totalPerimeter areas = extract (fmap areaPolygon)- compacticity = extract (relativeCompactness . concat)+ compacticity = extract (relativeCompactness . fold) pure $ zipWith5 District shapes districtLabels perimeters areas compacticity -- | Helper function for extracting from shapefiles. getPolygon :: RecContents -> [Polygon]-getPolygon RecPolygon {..} = recPolPoints+getPolygon RecPolygon {..} = recPolPoints getPolygon RecPolygonM {..} = recPolMPoints getPolygon RecPolygonZ {..} = recPolZPoints-getPolygon _ = error "shapefile contents not supported. please ensure the shapefile contains a list of polygons."+getPolygon _ = error "shapefile contents not supported. please ensure the shapefile contains a list of polygons."
src/GIS/Math/Spherical.hs view
@@ -57,7 +57,7 @@ perimeterPolygon :: Polygon -> Double perimeterPolygon [x1, x2] = distance x1 x2 perimeterPolygon (x1:x2:points) = perimeterPolygon (x2:points) + distance x1 x2-perimeterPolygon _ = error "Attempted to take area of polygon with no points"+perimeterPolygon _ = error "Attempted to take area of polygon with no points" -- | Find the area of a polygon with rectangular coördinates given. This -- function will error if given a polygon with no points.@@ -67,10 +67,10 @@ areaPolyRectangular _ = error "Attempted to take area of polygon with no points" -- | Distance in kilometers between two points given in degrees.-distance :: (Double, Double) -> (Double, Double) -> Double+distance :: Point -> Point -> Double distance = (*6371) .* on centralAngle toRadians -- | Compute central angle from points given in radians-centralAngle :: (Double, Double) -> (Double, Double) -> Double+centralAngle :: Point -> Point -> Double centralAngle (long1, lat1) (long2, lat2) = acos $ sin lat1 * sin lat2 + cos lat1 * cos lat2 * cos (long1 - long2)
src/GIS/Types.hs view
@@ -1,5 +1,4 @@ {-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE TemplateHaskell #-} -- | Module containing types for various geometrical objects. Lenses included. module GIS.Types ( -- * Types@@ -19,7 +18,7 @@ type Point = (Double, Double) type Polygon = [Point]-type Projection = (Double, Double) -> (Double, Double)+type Projection = Point -> Point -- | Data type for one record in a shape file, also capable of storing basic -- information about the district.@@ -30,7 +29,11 @@ , _compactness :: Double } deriving (Generic, Show) -makeLensesFor [("_districtLabel", "districtLabel"), ("_area", "area")] ''District+districtLabel :: Lens' District String+districtLabel f s = fmap (\x -> s { _districtLabel = x }) (f (_districtLabel s))++area :: Lens' District [Double]+area f s = fmap (\x -> s { _area = x }) (f (_area s)) data DbfReadError = NotAPolygon | ShpNull
− src/depends/readshp/Geometry/Shapefile.hs
@@ -1,16 +0,0 @@-{-|-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
− src/depends/readshp/Geometry/Shapefile/Internal.hs
@@ -1,67 +0,0 @@-{-|-Module : Geometry.Shapefile.Internal-Description : Some functions for reuse inside the library-Author : Sam van Herwaarden <samvherwaarden@gmail.com>--}--module Geometry.Shapefile.Internal ( getInt8- , getIntBE- , getInt16BE- , getIntLE- , getInt16LE- , getString- , getPoint- , getCharVal- , getPointList- , steps- , Point- ) where--import Control.Monad (replicateM)-import Data.Binary.Get hiding (getInt8)-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 = (,) <$> getFloat64le <*> getFloat64le---- | 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
− src/depends/readshp/Geometry/Shapefile/MergeShpDbf.hs
@@ -1,28 +0,0 @@-{-|-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 qualified Data.ByteString.Lazy as BL-import Geometry.Shapefile.ReadDbf-import Geometry.Shapefile.ReadShp-import Geometry.Shapefile.Types-import System.FilePath---- | 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- pure shpData {- dbfFieldDescs = Just $ dbfFields dbfData,- shpRecs = zipWith addRecLabel (shpRecs shpData) (dbfRecords dbfData)- }--addRecLabel :: ShpRec -> [DbfRecord] -> ShpRec-addRecLabel sr labels = sr { shpRecLabel = Just labels } -- FIXME add field name as string
− src/depends/readshp/Geometry/Shapefile/ReadDbf.hs
@@ -1,63 +0,0 @@-{-|-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 hiding (getInt8)--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- pure 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- pure 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-
− src/depends/readshp/Geometry/Shapefile/ReadShp.hs
@@ -1,243 +0,0 @@-{-|-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, 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- pure 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- pure 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 fmap Just <$> ds- else ds >> pure [Nothing, Nothing]- [mMin, mMax] <- let ds = replicateM 2 getFloat64le- in if t `elem` mTypes- then fmap Just <$> ds- else ds >> pure [Nothing, Nothing]- pure 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 pure Nothing- else Just <$> getRecContents recType- pure 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 -> RecPoint <$> getPoint-- ShpPointM -> do- p <- getPoint- m <- getFloat64le- pure RecPointM { pmPoint = p, pmM = m }-- ShpPointZ -> do- p <- getPoint- z <- getFloat64le- m <- getFloat64le- pure RecPointZ { pzPoint = p, pzZ = z, pzM = m }-- ShpMultiPoint -> do- (bb, nPoints, points) <- getPointsData- pure RecMultiPoint { recMPBBox = bb,- recMPNumPoints = nPoints,- recMPPoints = points }-- ShpMultiPointM -> do- (bb, nPoints, points) <- getPointsData- (mMin, mMax, ms) <- getMData nPoints- pure 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- pure 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- pure RecPolyLine { recPolLBBox = bb,- recPolLNumParts = nParts,- recPolLNumPoints = nPoints,- recPolLPartLengths = partLengths,- recPolLPoints = pntLists }-- ShpPolyLineM -> do- (bb, nParts, nPoints, partLengths, pntLists) <- getPolyData- (mMin, mMax, ms) <- getMData nPoints- pure 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- pure 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- pure RecPolygon { recPolBBox = bb,- recPolNumParts = nParts,- recPolNumPoints = nPoints,- recPolPartLengths = partLengths,- recPolPoints = pntLists }-- ShpPolygonM -> do- (bb, nParts, nPoints, partLengths, pntLists) <- getPolyData- (mMin, mMax, ms) <- getMData nPoints- pure 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- pure RecPolygonZ { recPolZBBox = bb,- recPolZNumParts = nParts,- recPolZNumPoints = nPoints,- recPolZPartLengths = partLengths,- recPolZPoints = pntLists,- recPolZZRange = (zMin, zMax),- recPolZZs = zs,- recPolZMRange = (mMin, mMax),- recPolZMs = ms }-- ShpNull ->- pure 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- pure (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- pure (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- pure (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- pure RecBBox { recXMin = xMin,- recXMax = xMax,- recYMin = yMin,- recYMax = yMax }-
− src/depends/readshp/Geometry/Shapefile/Types.hs
@@ -1,220 +0,0 @@-{-|-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 ( ShpData (..)- , ShpHeader (..)- , ShpType (..)- , zTypes- , mTypes- , shpTypeFromId- , RecBBox (..)- , RecContents (..)- , ShpRec (..)- , ShpBBox (..)- , DbfFieldDesc (..)- , DbfData (..)- , DbfRecord (..)- ) 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)-
− src/depends/readshp/LICENSE
@@ -1,20 +0,0 @@-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.