diff --git a/docs/manual.tex b/docs/manual.tex
new file mode 100644
--- /dev/null
+++ b/docs/manual.tex
@@ -0,0 +1,41 @@
+\documentclass{article}
+
+\usepackage{hyperref}
+\usepackage{appendix}
+
+\begin{document}
+
+\title{HGIS - GIS in Haskell}
+\author{Vanessa McHale}
+
+\maketitle
+
+\begin{abstract}
+This document contains explanations of some of the math behind hgis works. It
+also serves as documentation supplemental to haddock, presernting several
+examples and sample outputs. 
+\end{abstract}
+
+\tableofcontents
+
+\section{Parsing a shapefile}
+
+\section{Geometry on a sphere}
+
+Computing perimeters on a sphere is relatively straightforward: 
+
+\subsection{Measuring compactness}
+
+\section{Making maps}
+
+\subsection{Projections}
+
+\begin{appendices}
+  \section{Installation}
+  HGIS is supported and tested with stack, available from
+  \url{https://haskellstack.org}. 
+
+  \section{Using the libaries}
+\end{appendices}
+
+\end{document}
diff --git a/hgis.cabal b/hgis.cabal
--- a/hgis.cabal
+++ b/hgis.cabal
@@ -1,5 +1,5 @@
 name: hgis
-version: 0.1.0.0
+version: 0.1.1.0
 cabal-version: >=1.10
 build-type: Simple
 license: BSD3
@@ -16,6 +16,8 @@
 author: Vanessa McHale
 extra-source-files:
     README.md
+    stack.yaml
+    docs/manual.tex
 
 source-repository head
     type: git
@@ -32,7 +34,6 @@
         GIS.Math.Projections
     build-depends:
         base >=4.7 && <5,
-        readshp >=0.1.0.0 && <0.2,
         optparse-applicative >=0.13.1.0 && <0.14,
         Chart >=1.8.2 && <1.9,
         Chart-cairo >=1.8.2 && <1.9,
@@ -43,11 +44,16 @@
         transformers >=0.5.2.0 && <0.6,
         directory >=1.3.0.0 && <1.4,
         colour >=2.3.3 && <2.4,
-        data-default >=0.7.1.1 && <0.8
+        data-default >=0.7.1.1 && <0.8,
+        binary >=0.8.3.0 && <0.9,
+        bytestring >=0.10.8.1 && <0.11,
+        data-binary-ieee754 >=0.4.4 && <0.5,
+        filepath >=1.4.1.1 && <1.5,
+        monad-loops >=0.4.3 && <0.5
     pkgconfig-depends: cairo >=0.13.3.1 && <0.14
     default-language: Haskell2010
     default-extensions: DeriveGeneric OverloadedStrings
-    hs-source-dirs: src
+    hs-source-dirs: src src/depends/readshp
     other-modules:
         GIS.Math.Utils
         GIS.Utils
@@ -55,12 +61,18 @@
         GIS.Graphics.Types
         GIS.Types
         GIS.Exe.Parser
+        Geometry.Shapefile
+        Geometry.Shapefile.MergeShpDbf
+        Geometry.Shapefile.ReadDbf
+        Geometry.Shapefile.ReadShp
+        Geometry.Shapefile.Types
+        Geometry.Shapefile.Internal
 
 executable hgis
     main-is: MainPng.hs
     build-depends:
         base >=4.9.1.0 && <4.10,
-        hgis >=0.1.0.0 && <0.2
+        hgis >=0.1.1.0 && <0.2
     default-language: Haskell2010
     hs-source-dirs: app
     ghc-options: -threaded -rtsopts -with-rtsopts=-N
@@ -70,7 +82,7 @@
     main-is: Spec.hs
     build-depends:
         base >=4.9.1.0 && <4.10,
-        hgis >=0.1.0.0 && <0.2,
+        hgis >=0.1.1.0 && <0.2,
         hspec >=2.4.2 && <2.5
     default-language: Haskell2010
     hs-source-dirs: test
diff --git a/src/depends/readshp/Geometry/Shapefile.hs b/src/depends/readshp/Geometry/Shapefile.hs
new file mode 100644
--- /dev/null
+++ b/src/depends/readshp/Geometry/Shapefile.hs
@@ -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
diff --git a/src/depends/readshp/Geometry/Shapefile/Internal.hs b/src/depends/readshp/Geometry/Shapefile/Internal.hs
new file mode 100644
--- /dev/null
+++ b/src/depends/readshp/Geometry/Shapefile/Internal.hs
@@ -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
diff --git a/src/depends/readshp/Geometry/Shapefile/MergeShpDbf.hs b/src/depends/readshp/Geometry/Shapefile/MergeShpDbf.hs
new file mode 100644
--- /dev/null
+++ b/src/depends/readshp/Geometry/Shapefile/MergeShpDbf.hs
@@ -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 }
diff --git a/src/depends/readshp/Geometry/Shapefile/ReadDbf.hs b/src/depends/readshp/Geometry/Shapefile/ReadDbf.hs
new file mode 100644
--- /dev/null
+++ b/src/depends/readshp/Geometry/Shapefile/ReadDbf.hs
@@ -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 :: 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
+
diff --git a/src/depends/readshp/Geometry/Shapefile/ReadShp.hs b/src/depends/readshp/Geometry/Shapefile/ReadShp.hs
new file mode 100644
--- /dev/null
+++ b/src/depends/readshp/Geometry/Shapefile/ReadShp.hs
@@ -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 }
+
diff --git a/src/depends/readshp/Geometry/Shapefile/Types.hs b/src/depends/readshp/Geometry/Shapefile/Types.hs
new file mode 100644
--- /dev/null
+++ b/src/depends/readshp/Geometry/Shapefile/Types.hs
@@ -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)
+
diff --git a/stack.yaml b/stack.yaml
new file mode 100644
--- /dev/null
+++ b/stack.yaml
@@ -0,0 +1,66 @@
+# This file was automatically generated by 'stack init'
+#
+# Some commonly used options have been documented as comments in this file.
+# For advanced use and comprehensive documentation of the format, please see:
+# http://docs.haskellstack.org/en/stable/yaml_configuration/
+
+# Resolver to choose a 'specific' stackage snapshot or a compiler version.
+# A snapshot resolver dictates the compiler version and the set of packages
+# to be used for project dependencies. For example:
+#
+# resolver: lts-3.5
+# resolver: nightly-2015-09-21
+# resolver: ghc-7.10.2
+# resolver: ghcjs-0.1.0_ghc-7.10.2
+# resolver:
+#  name: custom-snapshot
+#  location: "./custom-snapshot.yaml"
+resolver: lts-8.3
+
+# User packages to be built.
+# Various formats can be used as shown in the example below.
+#
+# packages:
+# - some-directory
+# - https://example.com/foo/bar/baz-0.0.2.tar.gz
+# - location:
+#    git: https://github.com/commercialhaskell/stack.git
+#    commit: e7b331f14bcffb8367cd58fbfc8b40ec7642100a
+# - location: https://github.com/commercialhaskell/stack/commit/e7b331f14bcffb8367cd58fbfc8b40ec7642100a
+#   extra-dep: true
+#  subdirs:
+#  - auto-update
+#  - wai
+#
+# A package marked 'extra-dep: true' will only be built if demanded by a
+# non-dependency (i.e. a user package), and its test suites and benchmarks
+# will not be run. This is useful for tweaking upstream packages.
+packages:
+- '.'
+# Dependency packages to be pulled from upstream that are not in the resolver
+# (e.g., acme-missiles-0.3)
+extra-deps: []
+
+# Override default flag values for local packages and extra-deps
+flags: {}
+
+# Extra package databases containing global packages
+extra-package-dbs: []
+
+# Control whether we use the GHC we find on the path
+# system-ghc: true
+#
+# Require a specific version of stack, using version ranges
+# require-stack-version: -any # Default
+# require-stack-version: ">=1.4"
+#
+# Override the architecture used by stack, especially useful on Windows
+# arch: i386
+# arch: x86_64
+#
+# Extra directories used by stack for building
+# extra-include-dirs: [/path/to/dir]
+# extra-lib-dirs: [/path/to/dir]
+#
+# Allow a newer minor version of GHC than the snapshot specifies
+# compiler-check: newer-minor
