hgis 0.1.3.8 → 1.0.0.0
raw patch · 14 files changed
+120/−274 lines, 14 filesdep −optparse-applicativedep ~basePVP ok
version bump matches the API change (PVP)
Dependencies removed: optparse-applicative
Dependency ranges changed: base
API changes (from Hackage documentation)
- GIS.Exe.Opt: exec :: IO ()
- GIS.Exe.Opt: makeFoldersSVG :: [Map] -> IO ()
- GIS.Exe.Opt: pick :: Program -> IO ()
- GIS.Exe.OptCairo: Program :: Command -> FilePath -> Program
- GIS.Exe.OptCairo: [com] :: Program -> Command
- GIS.Exe.OptCairo: [file] :: Program -> FilePath
- GIS.Exe.OptCairo: data Program
- GIS.Exe.OptCairo: exec :: IO ()
- GIS.Math.Spherical: areaPolyRectangular :: Polygon -> Double
- GIS.Math.Spherical: avg :: (RealFrac a, Foldable t) => t a -> a
- GIS.Math.Spherical: centralAngle :: (Double, Double) -> (Double, Double) -> Double
- GIS.Math.Spherical: compactness1 :: Polygon -> Double
- GIS.Math.Spherical: shittyCentroid :: Polygon -> Point
Files
- app/MainPng.hs +0/−6
- cabal.project.local +1/−1
- hgis.cabal +19/−37
- src/GIS/Exe/Opt.hs +0/−27
- src/GIS/Exe/OptCairo.hs +0/−43
- src/GIS/Exe/Parser.hs +0/−107
- src/GIS/Graphics/Plot.hs +9/−5
- src/GIS/Hylo.hs +14/−10
- src/GIS/Math/Projections.hs +29/−14
- src/GIS/Math/Spherical.hs +15/−15
- src/GIS/Math/Utils.hs +16/−3
- src/GIS/Types.hs +11/−2
- stack.yaml +1/−1
- test/Spec.hs +5/−3
− app/MainPng.hs
@@ -1,6 +0,0 @@-module Main where--import GIS.Exe.OptCairo--main :: IO ()-main = exec
cabal.project.local view
@@ -1,4 +1,4 @@-constraints: hgis +development+constraints: hgis +development +cairo with-compiler: ghc-8.2.2 optimization: 2 documentation: true
hgis.cabal view
@@ -1,14 +1,14 @@ cabal-version: 1.18 name: hgis-version: 0.1.3.8+version: 1.0.0.0 license: BSD3 license-file: LICENSE copyright: Copyright: (c) 2016-2018 Vanessa McHale maintainer: vamchale@gmail.com author: Vanessa McHale-synopsis: Package and command-line for GIS with Haskell+synopsis: Library and for GIS with Haskell description:- Package containting functions to make graphs, read+ Package containing functions to make graphs, read shapefiles, and compute areas/perimeters of geographic features. category: Web@@ -21,13 +21,8 @@ src/depends/readshp/LICENSE source-repository head- type: git- location: https://github.com/vmchale/hgis--flag llvm-fast- description:- Enable build with llvm backend- default: False+ type: darcs+ location: https://hub.darcs.net/vmchale/hgis flag development description:@@ -35,23 +30,24 @@ default: False manual: True +flag cairo+ description:+ Enable build using cairo backend, allowing generation of PNGs+ default: True+ library exposed-modules:- GIS.Exe.Opt- GIS.Exe.OptCairo+ GIS.Math.Projections GIS.Math.Spherical- GIS.Graphics.PlotPNG- GIS.Graphics.PlotSVG GIS.Hylo- GIS.Math.Projections+ GIS.Graphics.PlotSVG hs-source-dirs: src src/depends/readshp other-modules:+ GIS.Graphics.Types GIS.Math.Utils GIS.Utils GIS.Graphics.Plot- GIS.Graphics.Types GIS.Types- GIS.Exe.Parser Geometry.Shapefile Geometry.Shapefile.MergeShpDbf Geometry.Shapefile.ReadDbf@@ -63,9 +59,7 @@ ghc-options: -Wall build-depends: base >=4.7 && <5,- optparse-applicative -any, Chart -any,- Chart-cairo -any, Chart-diagrams -any, lens -any, composition-prelude >=1.1.0.1,@@ -79,25 +73,13 @@ filepath -any, monad-loops -any - if flag(development)- ghc-options: -Werror- - if impl(ghc >=8.0)- ghc-options: -Wincomplete-uni-patterns -Wincomplete-record-updates--executable hgis- main-is: MainPng.hs- hs-source-dirs: app- default-language: Haskell2010- ghc-options: -Wall- build-depends:- base >4.9 && <5,- hgis -any+ if flag(cairo)+ exposed-modules:+ GIS.Graphics.PlotPNG - if flag(llvm-fast)- ghc-options: -threaded -rtsopts -with-rtsopts=-N -fllvm -optlo-O3- else- ghc-options: -threaded -rtsopts -with-rtsopts=-N+ if flag(cairo)+ build-depends:+ Chart-cairo -any if flag(development) ghc-options: -Werror
− src/GIS/Exe/Opt.hs
@@ -1,27 +0,0 @@-module GIS.Exe.Opt where--import Control.Lens-import Data.Semigroup-import GIS.Exe.Parser hiding (projection)-import GIS.Graphics.PlotSVG-import GIS.Graphics.Types hiding (projection)-import GIS.Hylo-import Options.Applicative-import System.Directory--pick :: Program -> IO ()-pick (Program (MapMaker outfile False projection) infile) = let p = pickProjection projection in- mkMapSVG outfile =<< districtToMapP p <$> getDistricts infile-pick (Program (MapMaker _ True projection) infile) = let p = pickProjection projection in- makeFoldersSVG =<< districtToMapFilesP p <$> getDistricts infile-pick (Program (MapLabel outfile False projection lensName) infile) = let p = pickProjection projection in- mkMapSVG outfile =<< districtToMapLensP p (pickLens lensName) <$> getDistricts infile -- FIXME make it work with -a (generate-all)-pick _ = error "not yet implemented."--exec :: IO ()-exec = pick =<< execParser helpDisplay--makeFoldersSVG :: [Map] -> IO ()-makeFoldersSVG maps = do- createDirectoryIfMissing False "maps"- mapM_ (\m -> mkMapSVG ("maps/" <> view title m <> ".svg") m) maps
− src/GIS/Exe/OptCairo.hs
@@ -1,43 +0,0 @@--- | This contains the main program executable for the program when built with--- the Cairo libary-module GIS.Exe.OptCairo- ( -- * Data types- Program (..)- -- * Functions- , exec- ) where--import Control.Lens-import Data.Semigroup-import GIS.Exe.Parser hiding (projection)-import GIS.Graphics.PlotPNG-import GIS.Graphics.Types hiding (projection)-import GIS.Hylo-import Options.Applicative-import System.Directory---- | Main executable; parses command line options and runs program-exec :: IO ()-exec = pick =<< execParser helpDisplay---- | Execute the program with parsed command-line input-pick :: Program -> IO ()-pick (Program (MapMaker outfile False projection) infile) = let p = pickProjection projection in- mkMap outfile =<< districtToMapP p <$> getDistricts infile -- svg now idk?-pick (Program (MapMaker _ True projection) infile) = let p = pickProjection projection in- makeFoldersPng =<< districtToMapFilesP p <$> getDistricts infile -- TODO fix this-pick (Program (Computation comp Nothing) infile) = --slightly wrong but eh.- case comp of- "perimeter" -> putStrLn =<< districtPerimeter <$> getDistricts infile- "area" -> putStrLn =<< districtArea <$> getDistricts infile- "compactness" -> putStrLn =<< districtCompactness <$> getDistricts infile- _ -> putStrLn "computation not recognized"-pick (Program (MapLabel outfile False _ lensName) infile) =- mkLensMap "" outfile (pickLens lensName) =<< getDistricts infile -- FIXME make it work with -a (generate-all) and make it not bind things together??-pick _ = error "not yet supported."---- | Make maps as png files.-makeFoldersPng :: [Map] -> IO ()-makeFoldersPng maps = do- createDirectoryIfMissing False "maps"- mapM_ (\m -> mkMapPng ("maps/" <> view title m <> ".png") m) maps
− src/GIS/Exe/Parser.hs
@@ -1,107 +0,0 @@-{-# LANGUAGE RankNTypes #-}--- | This module contains the parser for the command-line options-module GIS.Exe.Parser where--import Control.Lens hiding (argument)-import Data.Monoid-import GIS.Math.Projections-import GIS.Types-import Options.Applicative---- | Data type for one run of the program-data Program = Program { com :: Command- , file :: FilePath -- "Path to dbf file"- }---- | Data type for the appropriate subcommand-data Command = Computation { computation :: String , outputM :: Maybe FilePath }- | MapMaker { output :: FilePath , generateAll :: Bool , projection :: Maybe String }- | MapLabel { output :: FilePath , generateAll :: Bool , projection :: Maybe String , computatation :: String }---- | Parses the `Program` data type-program :: Parser Program-program = Program- <$> hsubparser- ( command "compute" (info computationP ( progDesc "Compute perimeter, area, etc. of map" ))- <> command "map" (info mapMaker ( progDesc "Make a map from a shapefile database." ))- <> command "labelmap" (info mapLabelMaker ( progDesc "Make a map from a shapefile database, and label areas with relevant info" )))- <*> argument str- (metavar "SHAPEFILE"- <> help "Path to .shp file"- <> completer (bashCompleter "file -X '!*.shp' -o plusdirs"))---- | Parses the `Command` datatype into a Computation-computationP :: Parser Command-computationP = Computation- <$> argument str- ( metavar "COMPUTATION"- <> help "which computation to perform, e.g. area, perimeter, etc." )- <*> optional (strOption -- because it's a maybe filepath- ( long "output"- <> short 'o'- <> metavar "OUTPUT"- <> help "Where to write output" ))---- | Parses the `Command` datatype into a map-mapLabelMaker :: Parser Command-mapLabelMaker = MapLabel- <$> strOption- (long "output"- <> short 'o'- <> metavar "OUTPUT"- <> help "Where to write the image/map" )- <*> switch- (long "generate-all"- <> short 'a'- <> help "Whether to generate a separate file for each object in the shapefile" )- <*> optional (strOption- (long "projection"- <> short 'p'- <> help "Which projection to use, e.g. mercator etc."))- <*> strOption- (long "label"- <> short 'p'- <> help "What aspect to label (area, perimeter, compactness)")---- | Parses the `Command` datatype into a map-mapMaker :: Parser Command-mapMaker = MapMaker- <$> strOption- (long "output"- <> short 'o'- <> metavar "OUTPUT"- <> help "Where to write the image/map" )- <*> switch- (long "generate-all"- <> short 'a'- <> help "Whether to generate a separate file for each object in the shapefile" )- <*> optional (strOption- (long "projection"- <> short 'p'- <> help "Which projection to use, e.g. mercator etc."))---- | display help if user enters e.g.------ > hgis --help wrongcommand-helpDisplay :: ParserInfo Program-helpDisplay = info ( program <**> helper)- ( fullDesc- <> progDesc "GIS for Haskell. Can make maps, compute areas/perimeters for shapefiles."- <> header "hgis - GIS in Haskell" )--pickLens :: String -> Lens' District Double-pickLens "compactness" = compactness-pickLens "perimeter" = perimeter-pickLens _ = error "must be one of 'compactness', 'perimeter'"---- | Parse a `Maybe String` into the appropriate `Projection`, doing nothing for--- a `Nothing`-pickProjection :: Maybe String -> Projection-pickProjection s = case s of- Just "mercator" -> mercator- Just "bonne" -> bonne- Just "albers" -> albers washingtonDC- Just "winkel3" -> winkel3- Just "littow" -> littow- Just _ -> error "unknown projection. try one of 'mercator', 'bonne', 'albers', 'winkel3', or 'littow'"- Nothing -> id
src/GIS/Graphics/Plot.hs view
@@ -1,11 +1,13 @@-{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE RecordWildCards #-} -- | Module containing renderable objects and plots for our maps module GIS.Graphics.Plot where +import Control.Arrow import Data.Colour.Names import GIS.Graphics.Types hiding (title)-import GIS.Math.Spherical+import GIS.Math.Utils import GIS.Types import GIS.Utils import Graphics.Rendering.Chart@@ -14,7 +16,9 @@ -- | Given a map, return a `Renderable ()` for use with the -- """Graphics.Rendering.Char""" module. mkMapR :: Map -> Renderable ()-mkMapR Map { _title = tit , _labelEntities = le , _labelledDistricts = points } = if le then mkRenderableLabelled tit points else mkRenderableLabelled tit $ fmap (over _2 (pure "")) points+mkMapR Map{..} = if _labelEntities+ then mkRenderableLabelled _title _labelledDistricts+ else mkRenderableLabelled _title $ second (pure "") <$> _labelledDistricts mkRenderablePlots :: String -> [Plot Double Double] -> Renderable () mkRenderablePlots title plots = toRenderable $@@ -34,13 +38,13 @@ -- | Set labels via a lens field. labelByLens :: (Show a) => Lens' District a -> [District] -> [([Polygon], String)]-labelByLens lens districts = zip (fmap (view shape) districts) (fmap (show . view lens) districts)+labelByLens lens districts = zip (fmap _shape districts) (fmap (show . view lens) districts) -- | Helper function to plot labels on a map. The ordinate will be plotted at -- the centroid of the abscissa, which may be outside the polygon if it is -- concave. plotLabels :: [([Polygon], String)] -> Plot Double Double plotLabels points = toPlot texts- where pairs = fmap (flatten . over _1 (shittyCentroid . concat)) points+ where pairs = fmap (flatten . first (shittyCentroid . concat)) points fontStyle = font_size .~ 15 $ font_weight .~ FontWeightBold $ def texts = plot_annotation_values .~ pairs $ plot_annotation_style .~ fontStyle $ def
src/GIS/Hylo.hs view
@@ -1,4 +1,6 @@-{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE RecordWildCards #-}+ -- | Module to transform shapefiles. module GIS.Hylo where @@ -13,7 +15,7 @@ import GIS.Graphics.Types import GIS.Math.Projections import GIS.Math.Spherical-import GIS.Types hiding (area, perimeter)+import GIS.Types hiding (area) import GIS.Utils import System.Directory @@ -63,16 +65,18 @@ getDistricts filepath = do dbfExists <- doesFileExist (stripExt filepath <> ".dbf") file <- if dbfExists then readShpWithDbf filepath else readShpFile filepath- let districtLabels = fromJust $ fmap labels $ mapM shpRecLabel . shpRecs $ file -- labels + field descriptions?- let shapes = fmap (getPolygon . fromJust . shpRecContents) . shpRecs $ file- let perimeters = fmap (totalPerimeter . getPolygon . fromJust . shpRecContents) . shpRecs $ file- let areas = fmap (fmap areaPolygon . getPolygon . fromJust . shpRecContents) . shpRecs $ file- let compacticity = fmap (relativeCompactness . concat . getPolygon . fromJust . shpRecContents) . shpRecs $ file+ -- FIXME throw a real error.+ let extract f = fmap (f . getPolygon . fromJust . shpRecContents) . shpRecs $ file+ districtLabels = fromJust $ fmap labels $ traverse shpRecLabel . shpRecs $ file+ shapes = extract id+ perimeters = extract totalPerimeter+ areas = extract (fmap areaPolygon)+ compacticity = extract (relativeCompactness . concat) pure $ zipWith5 District shapes districtLabels perimeters areas compacticity -- | Helper function for extracting from shapefiles. getPolygon :: RecContents -> [Polygon]-getPolygon RecPolygon { recPolPoints = pt } = pt-getPolygon RecPolygonM { recPolMPoints = pt } = pt-getPolygon RecPolygonZ { recPolZPoints = pt } = pt+getPolygon RecPolygon {..} = recPolPoints+getPolygon RecPolygonM {..} = recPolMPoints+getPolygon RecPolygonZ {..} = recPolZPoints getPolygon _ = error "shapefile contents not supported. please ensure the shapefile contains a list of polygons."
src/GIS/Math/Projections.hs view
@@ -1,18 +1,32 @@ -- | Module containing several useful projections for generating maps.-module GIS.Math.Projections where+module GIS.Math.Projections ( -- * Reference points+ washingtonDC+ , mecca+ -- * Projections+ , littow+ , craig+ , winkel3+ , mercator+ , bonne+ , albers+ -- * Helper functions+ , project+ , projectMap+ ) where +import Control.Arrow import Control.Lens import GIS.Graphics.Types import GIS.Math.Utils import GIS.Types --- | For use as a reference point in certain projections.+-- | For use as a reference point washingtonDC :: Point-washingtonDC = over _2 radians $ over _1 radians (38.9072, -77.0369)+washingtonDC = toRadians (38.9072, -77.0369) --- | For use as a reference point with the Craig retroazimuthal projection+-- | For use as a reference point mecca :: Point-mecca = over _2 radians $ over _1 radians (21.3891, 39.8579)+mecca = toRadians (21.3891, 39.8579) -- | Littow retroazimuthal + conformal projection littow :: Projection@@ -20,14 +34,14 @@ where referenceLong = radians 0 -- | Craig retroazimuthal projection--- (works on a subset of the world) craig :: Point -> Projection craig referencePoint (long, lat) = (long - referenceLong, y) where (referenceLong, referenceLat) = referencePoint- y = if long - referenceLong == 0 then expr else (long - referenceLong)/sin(long - referenceLong) * expr expr = sin lat * cos (long - referenceLong) - tan referenceLat * cos lat+ y | long - referenceLong == 0 = expr+ | otherwise = (long - referenceLong) / sin (long - referenceLong) * expr --- | Winkel Tripel projection (standard for the National Geographic Society since+-- | Winkel Tripel projection winkel3 :: Projection winkel3 (long, lat) = ((lambda * cos phi1 + (2 * cos lat * sin (lambda/2)/sinc alpha))/2, (lat + sin lat/sinc alpha)/2) where lambda = long - lambda0@@ -41,7 +55,7 @@ where meridian = radians (-98.5795) -- | Bonne projection with standard parallel at 45 N and central meridian--- centered at Washington DC+-- centered at Washington DC. bonne :: Projection bonne (long, lat) = (rho * sin e, cot phi1 - rho * cos e) where rho = cot phi1 + phi1 - lat@@ -50,17 +64,18 @@ meridian = radians (-77.0369) -- central meridian @ dc cot = (1/) . tan --- | Albers projection for a given reference point. To make it usable you can--- use+-- | Albers projection for a given reference point.+-- -- > ablers washingtonDC albers :: Point -> Projection albers referencePoint (long, lat) = (rho * sin theta, rho' - rho * cos theta)- where n = (sin phi1 + sin phi2)/2+ where n = (sin phi1 + sin phi2) / 2 theta = n * (long - referenceLong) c = cos phi1^(2 :: Int) + 2 * n * sin phi1 rho = sqrt (c - 2 * n * sin lat) / n rho' = sqrt (c - 2 * n * sin referenceLat) / n- phi1 = radians 20 -- standard parallels @ 20, 50 degrees+ -- standard parallels @ 20, 50 degrees+ phi1 = radians 20 phi2 = radians 50 (referenceLong, referenceLat) = referencePoint @@ -70,4 +85,4 @@ -- | Helper to apply a projection given a `Map`. projectMap :: Projection -> Map -> Map-projectMap p = over labelledDistricts (fmap (over _1 (fmap (project p))))+projectMap p = over labelledDistricts (fmap (first (fmap (project p))))
src/GIS/Math/Spherical.hs view
@@ -1,20 +1,20 @@ -- | Utilities to compute area, perimeterPolygon, etc. on the surface of a sphere.-module GIS.Math.Spherical where+module GIS.Math.Spherical ( areaPolygon+ , perimeterPolygon+ , relativeCompactness+ , totalPerimeter+ -- * Spherical geometry+ , areaTriangle+ , areaConvex+ -- * Internal functions+ , distance+ ) where import Control.Composition-import Control.Lens import GIS.Math.Projections import GIS.Math.Utils import GIS.Types --- | averages the coördinates of a polygon, returning a point.-shittyCentroid :: Polygon -> Point-shittyCentroid poly = (avg $ fmap fst poly, avg $ fmap snd poly)---- | Average over a foldable container-avg :: (RealFrac a, Foldable t) => t a -> a-avg list = sum list / (fromIntegral . length $ list)- -- | Compute the area of a triangle using L'Huillier's formula areaTriangle :: Point -> Point -> Point -> Double areaTriangle x1 x2 x3 = r^(2 :: Int) * e@@ -29,18 +29,18 @@ -- TODO mandelbrot/fractal dimension? -- consider "area of largest circumscribable circle" as well. --- | Relative compactness, i.e. compactness divided by the compactness of a Euclidean circle+-- | Relative compactness. Dimensionless. relativeCompactness :: Polygon -> Double relativeCompactness = (*scale) . compactness1 where scale = 1/4*pi --- | Take the area of the polygon and divide by the perimeter squared. This is a dimensionless measurement.+-- | Take the area of the polygon and divide by the perimeter squared. Dimensionless. compactness1 :: Polygon -> Double compactness1 p = areaPolygon p/perimeterPolygon p^(2 :: Int) -- | Compute the area of a convex polygon on the surface of a sphere. areaConvex :: Polygon -> Double-areaConvex (base1:base2:pts) = view _1 $ foldr stepArea (0,base2) pts+areaConvex (base1:base2:pts) = fst $ foldr stepArea (0,base2) pts where stepArea point (sum', base) = (sum' + areaTriangle base1 base point, point) areaConvex _ = error "attempted to take area of polygon with < 3 points" @@ -50,9 +50,9 @@ areaPolygon = (*factor) . areaPolyRectangular . fmap (bonne . toRadians) where factor = 1717856/4.219690791828533e-2 --- | Given a list of polygons, return the total area.+-- | Given a list of polygons, return the total perimeter. totalPerimeter :: [Polygon] -> Double-totalPerimeter ps = sum $ fmap perimeterPolygon ps+totalPerimeter = sum . fmap perimeterPolygon perimeterPolygon :: Polygon -> Double perimeterPolygon [x1, x2] = distance x1 x2
src/GIS/Math/Utils.hs view
@@ -1,9 +1,22 @@ -- | Miscellaneous utils for math-module GIS.Math.Utils where+module GIS.Math.Utils ( radians+ , toRadians+ , sinc+ , avg+ , shittyCentroid+ ) where -import Control.Lens+import Control.Composition import GIS.Types +-- | Averages the coördinates of a polygon, returning a point.+shittyCentroid :: Polygon -> Point+shittyCentroid poly = (avg $ fmap fst poly, avg $ fmap snd poly)++-- | Average over a foldable container+avg :: (RealFrac a, Foldable t) => t a -> a+avg list = sum list / (fromIntegral . length $ list)+ sinc :: Floating a => a -> a sinc x = sin x / x @@ -13,4 +26,4 @@ -- | Convert both coördinates to radians. toRadians :: Point -> Point-toRadians = over _1 radians . over _2 radians+toRadians = both radians
src/GIS/Types.hs view
@@ -2,7 +2,16 @@ {-# LANGUAGE TemplateHaskell #-} -- | Module containing types for various geometrical objects. Lenses included.-module GIS.Types where+module GIS.Types ( -- * Types+ Point+ , Polygon+ , Projection+ , District (..)+ , DbfReadError (..)+ -- * Lenses+ , districtLabel+ , area+ ) where import Control.Lens import GHC.Generics@@ -21,7 +30,7 @@ , _compactness :: Double } deriving (Generic, Show) -makeLenses ''District+makeLensesFor [("_districtLabel", "districtLabel"), ("_area", "area")] ''District data DbfReadError = NotAPolygon | ShpNull
stack.yaml view
@@ -1,5 +1,5 @@ ----resolver: lts-11.3+resolver: lts-11.6 extra-deps: - Chart-diagrams-1.8.3 - composition-prelude-1.3.0.8
test/Spec.hs view
@@ -1,3 +1,5 @@+module Main (main) where+ import GIS.Graphics.PlotPNG import GIS.Graphics.PlotSVG import GIS.Hylo@@ -9,10 +11,10 @@ main = hspec $ do describe "distance" $ it "computes distances along geodesics" $- distance (-74.0059, 40.7128) (-118.2347, 34.0522) `shouldBe` 3934.997673163554+ distance (-74.0059, 40.7128) (-118.2347, 34.0522) `shouldBe` 3934.997673163554 describe "mercatorFullPng" $ it "Reads a map and writes a .png with the mercator projection" $- (mkMapPng "test/testmap.png" =<< districtToMapP bonne <$> getDistricts "test/data/worldmap/TM_WORLD_BORDERS-0.3.shp") >>= (`shouldBe` ())+ (mkMapPng "test/testmap.png" =<< districtToMapP bonne <$> getDistricts "test/data/worldmap/TM_WORLD_BORDERS-0.3.shp") >>= (`shouldBe` ()) describe "mercatorFullSVG" $ it "Reads a map and writes a .svg with the mercator projection" $- (mkMapSVG "test/testmap.svg" =<< districtToMapP bonne <$> getDistricts "test/data/worldmap/TM_WORLD_BORDERS-0.3.shp") >>= (`shouldBe` ())+ (mkMapSVG "test/testmap.svg" =<< districtToMapP bonne <$> getDistricts "test/data/worldmap/TM_WORLD_BORDERS-0.3.shp") >>= (`shouldBe` ())