packages feed

geojson 1.3.1 → 1.3.2

raw patch · 45 files changed

+1773/−1595 lines, 45 filesdep +geojsondep +scientificdep +tastydep −QuickCheckdep −directorydep −doctestdep ~aesondep ~basedep ~bytestringsetup-changednew-uploader

Dependencies added: geojson, scientific, tasty, tasty-hspec, tasty-quickcheck

Dependencies removed: QuickCheck, directory, doctest, filepath, template-haskell

Dependency ranges changed: aeson, base, bytestring, hlint, lens, semigroups, text, transformers, validation, vector

Files

+ LICENCE view
@@ -0,0 +1,20 @@+# BSD 3-Clause License++Copyright (c) 2014-2018, HS-GeoJSON Project+All rights reserved.++Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:++1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.++2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.++3. Neither the name of the author nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.+IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,+OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,+OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE+POSSIBILITY OF SUCH DAMAGE.
README.md view
@@ -32,9 +32,6 @@  ## Adding Unit tests -Unit tests are written with [**doctest**] [doctest-github], for instructions on how to add unit tests-see the **doctest** [**User Guide**] [doctest-userguide].- Currently only files in the `src/` directory are searched for tests, it is assumed that the code in `main/` is a thin layer of code that uses modules from `src/`. @@ -49,7 +46,5 @@ Those with newer versions of `cabal` (`>= 1.18` I think) can skip `cabal-dev` and instead run `cabal sandbox init`, and just start runnign the above instructions as is from `cabal install --only-dependencies` -[doctest-github]: https://github.com/sol/doctest-haskell "sol/doctest-haskell on GitHub.com"-[doctest-userguide]: https://github.com/sol/doctest-haskell/blob/master/README.markdown#usage "doctest Usage Guide" [cabal-dev]: https://github.com/creswick/cabal-dev "creswick/cabal-dev on GitHub.com" 
Setup.hs view
@@ -1,77 +1,7 @@-#!/usr/bin/env runhaskell-{-# LANGUAGE CPP #-}-{-# OPTIONS_GHC -Wall #-}+ module Main (main) where -import Data.List ( nub )-import Data.Version ( showVersion )-#if __GLASGOW_HASKELL__ < 710-import Control.Applicative-#endif-import Distribution.Package ( PackageName(PackageName), PackageId, InstalledPackageId, packageVersion, packageName )-import Distribution.PackageDescription ( PackageDescription(), TestSuite(..) )-import Distribution.Simple ( defaultMainWithHooks, UserHooks(..), simpleUserHooks )-import Distribution.Simple.Utils ( rewriteFile, createDirectoryIfMissingVerbose, findProgramVersion, currentDir )-import Distribution.Simple.BuildPaths ( autogenModulesDir )-import Distribution.Simple.Setup ( BuildFlags(buildVerbosity), fromFlag )-import Distribution.Simple.LocalBuildInfo ( withLibLBI, withTestLBI, LocalBuildInfo(), ComponentLocalBuildInfo(componentPackageDeps) )-import Distribution.Verbosity ( Verbosity )-import Distribution.Version ( Version )-import System.Directory ( getDirectoryContents )-import System.FilePath ( (</>) )+import Distribution.Simple  main :: IO ()-main = defaultMainWithHooks simpleUserHooks-    {   buildHook = \pkg lbi hooks flags -> do-            generateBuildModule (fromFlag (buildVerbosity flags)) pkg lbi-            buildHook simpleUserHooks pkg lbi hooks flags-    }--generateBuildModule :: Verbosity -> PackageDescription -> LocalBuildInfo -> IO ()-generateBuildModule verbosity pkg lbi = do-    let dir = autogenModulesDir lbi-    createDirectoryIfMissingVerbose verbosity True dir-    withLibLBI pkg lbi $ \_ libcfg ->-        withTestLBI pkg lbi $ \suite suitecfg -> do-            ghcOpts <--                    generateCabalDevOpts-                <$> isCabalDevPresent-                <*> getGhcVersion verbosity-            rewriteFile (dir </> "Build_" ++ testName suite ++ ".hs") $ unlines-                [   "module Build_" ++ testName suite ++ " where"-                ,   "deps :: [String]"-                ,   "deps = " ++ show (formatdeps (testDeps libcfg suitecfg))-                ,   "opts :: [String]"-                ,   "opts = " ++ show ghcOpts-                ]-    where-        formatdeps = map (formatone . snd)-        formatone p = case packageName p of-            PackageName n -> n ++ "-" ++ showVersion (packageVersion p)--isCabalDevPresent :: IO Bool-isCabalDevPresent = do-    contents <- getDirectoryContents currentDir-    return $ "cabal-dev" `elem` contents--getGhcVersion :: Verbosity -> IO (Maybe Version)-getGhcVersion verb = findProgramVersion-    "--version"-    (last . words)-    verb-    "ghc"--generateCabalDevOpts :: Bool -> Maybe Version -> [String]-generateCabalDevOpts isCabalDev version =-    case version of-        Nothing -> []-        Just version' ->-            let-                baseOpts =  [   "-Lcabal-dev/lib"-                            ,   "-package-conf=cabal-dev/packages-" ++ showVersion version' ++ ".conf"-                            ]-            in-                if isCabalDev then baseOpts else []--testDeps :: ComponentLocalBuildInfo -> ComponentLocalBuildInfo -> [(InstalledPackageId, PackageId)]-testDeps xs ys = nub $ componentPackageDeps xs ++ componentPackageDeps ys+main = defaultMain
− doctests/Main.hs
@@ -1,50 +0,0 @@-{-# LANGUAGE CPP #-}-module Main where--import Build_doctests (deps, opts)-import Control.Monad--#if __GLASGOW_HASKELL__ < 710-import Control.Applicative-#endif--import Data.List-import System.Directory-import System.FilePath-import Test.DocTest--extensions :: [String]-extensions =-    [   "OverloadedStrings"-    ]--docopts :: [FilePath]-docopts =-    [   "-isrc"-    ,   "-idist/build/autogen"-    ,   "-optP-include"-    ,   "-optPdist/build/autogen/cabal_macros.h"-    ,   "-hide-all-packages"-    ] ++ map ("-package=" ++) deps ++ opts ++ map ("-X" ++) extensions---- the list of all file paths to search for source files-sourceDirs :: [FilePath]-sourceDirs = ["src"]--main :: IO ()-main = getSources >>= \sources -> doctest $ docopts ++ sources--getFilesAndDirectories :: FilePath -> IO ([FilePath], [FilePath])-getFilesAndDirectories dir = do-    c <- map (dir </>) . filter (`notElem` ["..", "."]) <$> getDirectoryContents dir-    (,) <$> filterM doesDirectoryExist c <*> filterM doesFileExist c--isSourceFile :: FilePath -> Bool-isSourceFile p = (takeFileName p /= "Setup.hs") && (".hs" `isSuffixOf` p)--getSources :: IO [FilePath]-getSources = liftM (filter isSourceFile . concat) (mapM go sourceDirs)-    where-        go dir = do-            (dirs, files) <- getFilesAndDirectories dir-            (files ++) . concat <$> mapM go dirs
− etc/LICENCE.md
@@ -1,20 +0,0 @@-# BSD 3-Clause License--Copyright (c) 2014, Dom De Re-All rights reserved.--Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:--1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.--2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.--3. Neither the name of the author nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.--THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,-INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.-IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,-OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,-OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,-OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE-POSSIBILITY OF SUCH DAMAGE.
geojson.cabal view
@@ -1,105 +1,95 @@-name:               geojson-version:            1.3.1-license:            BSD3-license-file:       etc/LICENCE.md-author:             Dom De Re-maintainer:         Dom De Re-copyright:          Copyright (C) 2013-2014-synopsis:           A thin GeoJSON Layer above the aeson library-category:           Data-description:        A thin GeoJSON Layer above the aeson library.-                    .-                    It currently conforms to version 1.0 of the GeoJSON spec which can be found here:-                    .-                    <http://geojson.org/geojson-spec.html>+name:                  geojson+version:                1.3.2+license:                BSD3+license-file:           LICENCE+author:                 Dom De Re+maintainer:             Andrew Newman+copyright:              Copyright (C) 2013-2018+synopsis:               A thin GeoJSON Layer above the aeson library+category:               Data+description:            A thin GeoJSON Layer above the aeson library.+                        .+                        It currently conforms to version 1.0 of the GeoJSON spec which can be found here:+                        .+                        <http://geojson.org/geojson-spec.html> -homepage:           https://github.com/domdere/hs-geojson-bug-reports:        https://github.com/domdere/hs-geojson/issues-cabal-version:      >= 1.22-build-type:         Custom-tested-with:        GHC == 7.10.1-extra-source-files: README.md,-                    CHANGELOG.md+homepage:               https://github.com/newmana/hs-geojson+bug-reports:            https://github.com/newmana/hs-geojson/issues+cabal-version:          >= 1.10+build-type:             Simple+extra-source-files:     README.md,+                        CHANGELOG.md  source-repository       head     type:               git-    location:           https://github.com/domdere/hs-geojson.git+    location:           https://github.com/newmana/hs-geojson.git  source-repository       this     type:               git-    location:           https://github.com/domdere/hs-geojson.git-    tag:                1.3.0--flag                    small_base-    description:        Choose the new, split-up base package.+    location:           https://github.com/newmana/hs-geojson.git+    tag:                1.3.2  library-    default-language:   Haskell2010--    build-depends:      base < 5 &&     >= 4-                    ,   aeson           >= 0.8       && < 1.0-                    ,   lens            >= 4.11      && < 4.13-                    ,   semigroups      == 0.16.*-                    ,   text            == 1.2.*-                    ,   transformers    >= 0.3       && < 0.5-                    ,   validation      == 0.5.*-                    ,   vector          == 0.10.*--    ghc-options:        -Wall-                        -fno-warn-unused-imports-                        -fno-warn-unused-binds-                        -fno-warn-unused-do-bind-                        -fno-warn-type-defaults-     hs-source-dirs:     src-+    build-depends:      base < 5 &&     >= 4+                    ,   aeson           >= 0.8+                    ,   lens            >= 4.11+                    ,   semigroups      >= 0.16+                    ,   text            >= 1.2+                    ,   scientific      >= 0.2.0+                    ,   transformers    >= 0.3+                    ,   validation      >= 0.5+                    ,   vector          >= 0.10     exposed-modules:    Data.Geospatial                     ,   Data.LinearRing                     ,   Data.LineString--    other-modules:      Data.Geospatial.BasicTypes-                    ,   Data.Geospatial.CRS-                    ,   Data.Geospatial.GeoFeature-                    ,   Data.Geospatial.GeoFeatureCollection-                    ,   Data.Geospatial.Geometry-                    ,   Data.Geospatial.Geometry.GeoMultiLine-                    ,   Data.Geospatial.Geometry.GeoMultiPoint-                    ,   Data.Geospatial.Geometry.GeoMultiPolygon-                    ,   Data.Geospatial.Geometry.GeoLine-                    ,   Data.Geospatial.Geometry.GeoPoint-                    ,   Data.Geospatial.Geometry.GeoPolygon-                    ,   Data.Geospatial.Geometry.Aeson-                    ,   Data.Geospatial.GeoPosition--test-suite              doctests-    type:-                        exitcode-stdio-1.0--    main-is:-                        Main.hs--    default-language:-                        Haskell2010--    build-depends:      base < 5 && >= 3-                    ,   bytestring          == 0.10.*-                    ,   doctest             == 0.10.*-                    ,   filepath            >= 1.3     && < 1.5-                    ,   directory           == 1.2.*-                    ,   QuickCheck          == 2.8.*-                    ,   template-haskell+                    ,   Data.Geospatial.Internal.BasicTypes+                    ,   Data.Geospatial.Internal.CRS+                    ,   Data.Geospatial.Internal.GeoFeature+                    ,   Data.Geospatial.Internal.GeoFeatureCollection+                    ,   Data.Geospatial.Internal.Geometry+                    ,   Data.Geospatial.Internal.Geometry.GeoMultiLine+                    ,   Data.Geospatial.Internal.Geometry.GeoMultiPoint+                    ,   Data.Geospatial.Internal.Geometry.GeoMultiPolygon+                    ,   Data.Geospatial.Internal.Geometry.GeoLine+                    ,   Data.Geospatial.Internal.Geometry.GeoPoint+                    ,   Data.Geospatial.Internal.Geometry.GeoPolygon+                    ,   Data.Geospatial.Internal.Geometry.Aeson+                    ,   Data.Geospatial.Internal.GeoPosition+    default-language:   Haskell2010+    ghc-options:        -Wall -    ghc-options:-                        -Wall+test-suite              geojson-test+    hs-source-dirs:     test+    main-is:            Main.hs+    type:               exitcode-stdio-1.0+    build-depends:      base < 5 &&     >= 4+                    ,   aeson           >= 0.8+                    ,   bytestring      >= 0.10+                    ,   geojson+                    ,   tasty+                    ,   tasty-hspec+                    ,   tasty-quickcheck+                    ,   text            >= 1.2+                    ,   validation      >= 0.5+    other-modules:      Arbitrary+                    ,   Fixture+                    ,   Data.LinearRingTests+                    ,   Data.LineStringTests+                    ,   Data.Geospatial.Internal.CRSTests+                    ,   Data.Geospatial.Internal.GeoFeatureCollectionTests+                    ,   Data.Geospatial.Internal.GeoFeatureTests+                    ,   Data.Geospatial.Internal.GeometryTests+    default-language:   Haskell2010+    ghc-options:        -Wall                         -threaded -    hs-source-dirs:-                        doctests--test-suite              hlint-    default-language:   Haskell2010-    type:               exitcode-stdio-1.0-    main-is:            Main.hs+test-suite              geojson-hlint     hs-source-dirs:     hlint+    main-is:            Main.hs+    type:               exitcode-stdio-1.0     build-depends:      base-                    ,   hlint               == 1.9.*+                    ,   hlint+    default-language:   Haskell2010+    ghc-options:        -Wall+                        -threaded
hlint/Main.hs view
@@ -1,12 +1,12 @@ module Main where -import Control.Monad ( unless )-import Language.Haskell.HLint ( hlint )-import System.Environment ( getArgs )-import System.Exit ( exitFailure )+import           Control.Monad          (unless)+import           Language.Haskell.HLint (hlint)+import           System.Environment     (getArgs)+import           System.Exit            (exitFailure)  main :: IO () main = do     args <- getArgs-    hlints <- hlint $ ["src", "--cpp-define=HLINT", "--cpp-ansi"] ++ args+    hlints <- hlint $ ["src/", "test/", "hlint/", "--cpp-define=HLINT", "--cpp-ansi"] ++ args     unless (null hlints) exitFailure
src/Data/Geospatial.hs view
@@ -1,9 +1,9 @@ ------------------------------------------------------------------- -- | -- Module       : Data.Geospatial--- Copyright    : (C) 2014 Dom De Re--- License      : BSD-style (see the file etc/LICENSE.md)--- Maintainer   : Dom De Re+-- Copyright    : (C) 2014-2018 HS-GeoJSON Project+-- License      : BSD-style (see the file LICENSE.md)+-- Maintainer   : Andrew Newman -- -- Refer to the GeoJSON Spec http://www.geojson.org/geojson-spec.html --@@ -15,6 +15,7 @@     ,   Easting     ,   Northing     ,   Altitude+    ,   FeatureID(..)     ,   GeoPositionWithoutCRS     ,   GeoPosition(..)     ,   GeoPoint(..)@@ -30,7 +31,6 @@     ,   FormatString     ,   ProjectionType     ,   CRSObject(..)-    ,   FeatureID     ,   BoundingBoxWithoutCRS     ,   GeoFeature(..)     ,   GeoFeatureCollection(..)@@ -69,9 +69,9 @@     ,   _LinkedCRS     ) where -import Data.Geospatial.BasicTypes-import Data.Geospatial.CRS-import Data.Geospatial.GeoFeature-import Data.Geospatial.GeoFeatureCollection-import Data.Geospatial.Geometry-import Data.Geospatial.GeoPosition+import           Data.Geospatial.Internal.BasicTypes+import           Data.Geospatial.Internal.CRS+import           Data.Geospatial.Internal.GeoFeature+import           Data.Geospatial.Internal.GeoFeatureCollection+import           Data.Geospatial.Internal.Geometry+import           Data.Geospatial.Internal.GeoPosition
− src/Data/Geospatial/BasicTypes.hs
@@ -1,60 +0,0 @@----------------------------------------------------------------------- |--- Module       : Data.Geospatial.BasicTypes--- Copyright    : (C) 2014 Dom De Re--- License      : BSD-style (see the file etc/LICENSE.md)--- Maintainer   : Dom De Re------ Basic types for GeoJSON representations.---------------------------------------------------------------------module Data.Geospatial.BasicTypes (-    -- * Coordinate types-        Latitude-    ,   Longitude-    ,   Easting-    ,   Northing-    ,   Altitude-    ,   GeoPositionWithoutCRS-    -- * CRS Reference types-    ,   Name-    ,   Code-    ,   Href-    ,   FormatString-    ,   ProjectionType-    -- * Feature Types-    ,   BoundingBoxWithoutCRS-    ,   FeatureID-    ) where--import Data.Text ( Text )--type Latitude = Double-type Longitude = Double-type Easting = Double-type Northing = Double-type Altitude = Double---- | (`GeoPositionWithoutCRS` is a catch all for indeterminate CRSs and for expression of positions--- before a CRS has been determined----type GeoPositionWithoutCRS = [Double]--type Name = Text-type Code = Int-type Href = Text-type FormatString = Text-type ProjectionType = Text---- Feature Types--type FeatureID = Text---- | See Section 4 /Bounding Boxes/ of the GeoJSON spec,--- The length of the list/array must be 2*n where n is the dimensionality of the position type for the CRS--- with min values first followed by the max values, wich both the min/max sets following the same axis order as the CRS,--- e.g for WGS84: minLongitude, minLatitude, maxLongitude, maxLatitude--- The spec mentions that it can be part of a geometry object too but doesnt give an example,--- This implementation will ignore bboxes on Geometry objects, they can be added if required.-type BoundingBoxWithoutCRS = [Double]--
− src/Data/Geospatial/CRS.hs
@@ -1,137 +0,0 @@-{-# LANGUAGE OverloadedStrings, TemplateHaskell #-}----------------------------------------------------------------------- |--- Module       : Data.Geospatial.CRS--- Copyright    : (C) 2014 Dom De Re--- License      : BSD-style (see the file etc/LICENSE.md)--- Maintainer   : Dom De Re------ See Section 3 /Coordinate Reference System Objects/--- in the GeoJSON Spec------------------------------------------------------------------------module Data.Geospatial.CRS (-    -- * Types-        CRSObject(..)-    -- * Functions-    ,   defaultCRS-    -- * Prisms-    ,   _NoCRS-    ,   _NamedCRS-    ,   _EPSG-    ,   _LinkedCRS-    ) where--import Data.Geospatial.BasicTypes-import Data.Geospatial.Geometry-import Data.Geospatial.GeoPosition--import Control.Applicative ( (<$>), (<*>) )-import Control.Lens ( makePrisms )-import Control.Monad ( mzero )-import Data.Aeson-    (   FromJSON(..)-    ,   ToJSON(..)-    ,   Value(..)-    ,   Object-    ,   (.:)-    ,   (.=)-    ,   object-    )-import Data.Aeson.Types ( Parser )-import Data.Text ( Text )---- $setup------ >>> import qualified Data.Aeson as A--- >>> import qualified Data.ByteString.Lazy.Char8 as BS------ Test CRS Data--- >>> let testLinkCRSJSON = "{\"type\":\"link\",\"properties\":{\"href\":\"www.google.com.au\",\"type\":\"proj4\"}}"--- >>> let testLinkCRS = LinkedCRS "www.google.com.au" "proj4"--- >>> let testEPSGJSON = "{\"type\":\"epsg\",\"properties\":{\"code\":4326}}"--- >>> let testEPSG = EPSG 4326--- >>> let testNamedCRSJSON = "{\"type\":\"name\",\"properties\":{\"name\":\"urn:ogc:def:crs:OGC:1.3:CRS84\"}}"--- >>> let testNamedCRS = NamedCRS "urn:ogc:def:crs:OGC:1.3:CRS84"------- | See Section 3 /Coordinate Reference System Objects/ in the GeoJSON Spec--- `NoCRS` is required because no 'crs' attribute in a GeoJSON feature is NOT the same thing as--- a null 'crs' attribute. no 'crs' value implies the default CRS, while a null CRS means--- you cannot assume a CRS, null will mapped to `NoCRS` while a non-existent attribute will--- be mapped to a `Nothing` `Maybe` value-data CRSObject =-        NoCRS-    |   NamedCRS !Name-    |   EPSG Code-    |   LinkedCRS !Href !FormatString  deriving (Show, Eq)--makePrisms ''CRSObject---- | The default CRS according to Section 3 /Coordinate Reference System Objects/ is WGS84 which I believe,--- from <http://spatialreference.org/ref/epsg/4326/> which translates to this in JSON: <http://spatialreference.org/ref/epsg/4326/json/>)--- is represented thus:-defaultCRS :: CRSObject-defaultCRS = EPSG 4326---- instances---- |--- encode and decodes CRS Objects to and from GeoJSON------ >>> (A.decode . BS.pack) testLinkCRSJSON == Just testLinkCRS--- True------ >>> (A.decode . BS.pack) testNamedCRSJSON == Just testNamedCRS--- True------ >>> (A.decode . BS.pack) testEPSGJSON == Just testEPSG--- True------ Aeson doesnt decode "null" to `Null` unfortunately------ (A.decode . BS.pack) "null" == Just NoCRS--- True----instance FromJSON CRSObject where-    parseJSON Null = return NoCRS-    parseJSON (Object obj) = do-        crsType <- obj .: "type"-        crsObjectFromAeson crsType obj-    parseJSON _ = mzero---- |--- encode CRS Objects to GeoJSON------ >>> (A.decode . A.encode) testLinkCRS == Just testLinkCRS--- True------ >>> (A.decode . A.encode) testNamedCRS == Just testNamedCRS--- True------ >>> (A.decode . A.encode) testEPSG == Just testEPSG--- True------ >>> A.encode NoCRS--- "null"----instance ToJSON CRSObject where-    toJSON (NamedCRS name)          = object ["type" .= ("name" :: Text), "properties" .= object ["name" .= name]]-    toJSON (EPSG code)              = object ["type" .= ("epsg" :: Text), "properties" .= object ["code" .= code]]-    toJSON (LinkedCRS href format)  = object ["type" .= ("link" :: Text), "properties" .= object ["href" .= href, "type" .= format]]-    toJSON NoCRS                    = Null---- helpers--crsPropertyFromAesonObj :: (FromJSON a) => Text -> Object -> Parser a-crsPropertyFromAesonObj name obj = do-    props <- obj .: "properties"-    props .: name--crsObjectFromAeson :: Text -> Object -> Parser CRSObject-crsObjectFromAeson "name" obj   = NamedCRS <$> crsPropertyFromAesonObj "name" obj-crsObjectFromAeson "epsg" obj   = EPSG <$> crsPropertyFromAesonObj "code" obj-crsObjectFromAeson "link" obj   = LinkedCRS <$> crsPropertyFromAesonObj "href" obj <*> crsPropertyFromAesonObj "type" obj-crsObjectFromAeson _ _          = mzero--
− src/Data/Geospatial/GeoFeature.hs
@@ -1,206 +0,0 @@-{-# LANGUAGE NoImplicitPrelude, OverloadedStrings, TemplateHaskell #-}----------------------------------------------------------------------- |--- Module       : Data.Geospatial.GeoFeature--- Copyright    : (C) 2014 Dom De Re--- License      : BSD-style (see the file etc/LICENSE.md)--- Maintainer   : Dom De Re------ See Section 2.2 /Feature Objects/ of the GeoJSON spec.--- Parameterised on the property type------------------------------------------------------------------------module Data.Geospatial.GeoFeature (-    -- * Types-        GeoFeature(..)-    -- * Lenses-    ,   bbox-    ,   geometry-    ,   properties-    ,   featureId-    ) where--import Data.Geospatial.BasicTypes-import Data.Geospatial.Geometry-import Data.Geospatial.Geometry.Aeson--import Prelude ( Show, Eq(..), ($) )-import Control.Applicative ( (<$>), (<*>) )-import Control.Lens ( makeLenses )-import Control.Monad ( mzero )-import Data.Aeson ( FromJSON(..), ToJSON(..), Value(..), (.:), (.:?), (.=), object )-import Data.List ( (++) )-import Data.Maybe ( Maybe )-import Data.Text ( Text )---- $setup------ >>> import Data.LinearRing--- >>> import Data.LineString--- >>> import Data.Geospatial.Geometry.GeoMultiPolygon (mergeGeoPolygons)--- >>> import Data.Geospatial.Geometry.GeoMultiLine (mergeGeoLines)------ >>> import Control.Monad ( return )--- >>> import qualified Data.Aeson as A--- >>> import qualified Data.ByteString.Lazy.Char8 as BS--- >>> import Data.Function ( (.) )--- >>> import Data.Int ( Int )--- >>> import Data.Maybe ( Maybe(..) )--- >>> import Data.String--- >>> import qualified Data.Text as T------ >>> let decode' = A.decode . BS.pack; decode' :: (FromJSON a) => String -> Maybe a------ Test Bounding Box Data--- >>> let lshapedPolyVertices = return (makeLinearRing [120.0, -15.0] [127.0, -15.0] [127.0, -25.0] [[124.0, -25.0], [124.0, -18.0], [120.0, -18.0]]) :: [LinearRing GeoPositionWithoutCRS]--- >>> let lshapedPolyLineVertices = makeLineString [120.0, -15.0] [127.0, -15.0] [[127.0, -25.0], [124.0, -25.0], [124.0, -18.0], [120.0, -18.0]] :: LineString GeoPositionWithoutCRS--- >>> let emptyVertices = [] :: [LinearRing GeoPositionWithoutCRS]--- >>> let testLatLonBBox = [-32, 147.5, -29.5, 151.0] :: BoundingBoxWithoutCRS--- >>> let testLatLonBBoxJSON = "[-32,147.5,-29.5,151]"--- >>> let testEmptyBBox = [] :: BoundingBoxWithoutCRS--- >>> let testEmptyBBoxJSON = "[]"------ End Test Bounding Box Data--- Test Geometry Data--- Polys--- >>> let lShapedPolyJSON = "{\"coordinates\":[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18],[120,-15]]],\"type\":\"Polygon\"}"------ Upside down L Shaped Poly------ (120, -15)                (127, -15)--- *---------------------------*--- |                           |--- |                           |--- |             (124, -18)    |--- *---------------*           |--- (120, -18)      |           |---                 |           |---                 |           |---                 |           |---                 |           |---                 |           |---                 |           |---                 *-----------*---               (124, -25)  (127, -25)------ >>> let lShapedGeoPoly = GeoPolygon lshapedPolyVertices--- >>> let lShapedPoly = Polygon lShapedGeoPoly--- >>> let emptyPolyJSON = "{\"type\":\"Polygon\",\"coordinates\":[]}"--- >>> let emptyGeoPoly = GeoPolygon emptyVertices--- >>> let emptyPoly = Polygon emptyGeoPoly------ Multi Polys--- >>> let emptyMultiPolyJSON = "{\"type\":\"MultiPolygon\",\"coordinates\":[]}"--- >>> let emptyMultiGeoPoly = GeoMultiPolygon []--- >>> let emptyMultiPoly = MultiPolygon emptyMultiGeoPoly--- >>> let singlePolyMultiPolyJSON = "{\"type\":\"MultiPolygon\",\"coordinates\":[[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]]]]}"--- >>> let singlePolyGeoMultiPoly = mergeGeoPolygons [lShapedGeoPoly]--- >>> let singlePolyMultiPoly = MultiPolygon singlePolyGeoMultiPoly--- >>> let multiPolyJSON = "{\"type\":\"MultiPolygon\",\"coordinates\":[[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]]],[]]}"--- >>> let geoMultiPoly = mergeGeoPolygons [lShapedGeoPoly, emptyGeoPoly]--- >>> let multiPoly = MultiPolygon geoMultiPoly------ Line Data--- >>> let lShapedLineJSON = "{\"type\":\"Line\",\"coordinates\":[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]]}"--- >>> let lShapedGeoLine = GeoLine lshapedPolyLineVertices--- >>> let lShapedLine = Line lShapedGeoLine------ Multi Lines--- >>> let emptyMultiLineJSON = "{\"type\":\"MultiLineString\",\"coordinates\":[]}"--- >>> let emptyMultiGeoLine = GeoMultiLine []--- >>> let emptyMultiLine = MultiLine emptyMultiGeoLine--- >>> let singleLineMultiLineJSON = "{\"type\":\"MultiLineString\",\"coordinates\":[{\"type\":\"Line\",\"coordinates\":[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]]}]}"--- >>> let singleLineGeoMultiLine = mergeGeoLines [lShapedGeoLine]--- >>> let singleLineMultiLine = MultiLine singleLineGeoMultiLine--- >>> let multiLineJSON = "{\"coordinates\":[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]],[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]]],\"type\":\"MultiLineString\"}"--- >>> let geoMultiLine = mergeGeoLines [lShapedGeoLine, lShapedGeoLine]--- >>> let multiLine = MultiLine geoMultiLine--- >>> let emptyCollectionJSON = "{\"type\":\"GeometryCollection\",\"geometries\":[]}"--- >>> let emptyCollection = Collection []--- >>> let bigassCollectionJSON = "{\"geometries\":[{\"coordinates\":[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]]],\"type\":\"MultiLineString\"},{\"coordinates\":[],\"type\":\"MultiLineString\"},{\"coordinates\":[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]],[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]]],\"type\":\"MultiLineString\"},{\"coordinates\":[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]],\"type\":\"LineString\"},{\"coordinates\":[[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18],[120,-15]]],[]],\"type\":\"MultiPolygon\"},{\"coordinates\":[[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18],[120,-15]]]],\"type\":\"MultiPolygon\"},{\"coordinates\":[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18],[120,-15]]],\"type\":\"Polygon\"},{\"coordinates\":[],\"type\":\"MultiPolygon\"},{\"coordinates\":[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18],[120,-15]]],\"type\":\"Polygon\"}],\"type\":\"GeometryCollection\"}"--- >>> let bigassCollection = Collection [singleLineMultiLine, emptyMultiLine, multiLine, lShapedLine, multiPoly, singlePolyMultiPoly, lShapedPoly, emptyMultiPoly, lShapedPoly]------ End Test Geometry Data------ Test Properties--- >>> let testProperties = object [(T.pack "depth") .= (5 :: Int), (T.pack "comment") .= (T.pack "Bore run over by dump truck")]------ Test Features--- >>> let bigFeatureJSON = "{\"bbox\":[-32,147.5,-29.5,151],\"geometry\":{\"geometries\":[{\"coordinates\":[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]]],\"type\":\"MultiLineString\"},{\"coordinates\":[],\"type\":\"MultiLineString\"},{\"coordinates\":[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]],[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]]],\"type\":\"MultiLineString\"},{\"coordinates\":[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]],\"type\":\"LineString\"},{\"coordinates\":[[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18],[120,-15]]],[]],\"type\":\"MultiPolygon\"},{\"coordinates\":[[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18],[120,-15]]]],\"type\":\"MultiPolygon\"},{\"coordinates\":[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18],[120,-15]]],\"type\":\"Polygon\"},{\"coordinates\":[],\"type\":\"MultiPolygon\"},{\"coordinates\":[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18],[120,-15]]],\"type\":\"Polygon\"}],\"type\":\"GeometryCollection\"},\"id\":\"GW001\",\"type\":\"Feature\",\"properties\":{\"depth\":5,\"comment\":\"Bore run over by dump truck\"}}"--- >>> let bigFeature = GeoFeature (Just testLatLonBBox) bigassCollection testProperties (Just "GW001")--- >>> let featureWithNoPropertiesJSON = "{\"bbox\":[-32,147.5,-29.5,151],\"geometry\":{\"geometries\":[{\"coordinates\":[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]]],\"type\":\"MultiLineString\"},{\"coordinates\":[],\"type\":\"MultiLineString\"},{\"coordinates\":[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]],[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]]],\"type\":\"MultiLineString\"},{\"coordinates\":[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]],\"type\":\"LineString\"},{\"coordinates\":[[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18],[120,-15]]],[]],\"type\":\"MultiPolygon\"},{\"coordinates\":[[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18],[120,-15]]]],\"type\":\"MultiPolygon\"},{\"coordinates\":[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18],[120,-15]]],\"type\":\"Polygon\"},{\"coordinates\":[],\"type\":\"MultiPolygon\"},{\"coordinates\":[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18],[120,-15]]],\"type\":\"Polygon\"}],\"type\":\"GeometryCollection\"},\"id\":\"GW001\",\"type\":\"Feature\",\"properties\":null}"--- >>> let featureWithNoProperties = let GeoFeature bbox geometry _ featureId = bigFeature in GeoFeature bbox geometry Null featureId--- >>> let featureWithNoGeometryJSON = "{\"type\":\"Feature\",\"properties\":{\"depth\":5,\"comment\":\"Bore run over by dump truck\"},\"geometry\":null,\"bbox\":[-32,147.5,-29.5,151],\"id\":\"GW001\"}"--- >>> let featureWithNoGeometry = let GeoFeature bbox _ props featureId = bigFeature in GeoFeature bbox NoGeometry props featureId--- >>> let featureWithNoIdJSON = "{\"bbox\":[-32,147.5,-29.5,151],\"geometry\":{\"geometries\":[{\"coordinates\":[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]]],\"type\":\"MultiLineString\"},{\"coordinates\":[],\"type\":\"MultiLineString\"},{\"coordinates\":[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]],[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]]],\"type\":\"MultiLineString\"},{\"coordinates\":[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]],\"type\":\"LineString\"},{\"coordinates\":[[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18],[120,-15]]],[]],\"type\":\"MultiPolygon\"},{\"coordinates\":[[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18],[120,-15]]]],\"type\":\"MultiPolygon\"},{\"coordinates\":[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18],[120,-15]]],\"type\":\"Polygon\"},{\"coordinates\":[],\"type\":\"MultiPolygon\"},{\"coordinates\":[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18],[120,-15]]],\"type\":\"Polygon\"}],\"type\":\"GeometryCollection\"},\"type\":\"Feature\",\"properties\":{\"depth\":5,\"comment\":\"Bore run over by dump truck\"}}"--- >>> let featureWithNoId = let GeoFeature bbox geometry props _ = bigFeature in GeoFeature bbox geometry props Nothing--- >>> let featureWithNoBBoxJSON = "{\"geometry\":{\"geometries\":[{\"coordinates\":[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]]],\"type\":\"MultiLineString\"},{\"coordinates\":[],\"type\":\"MultiLineString\"},{\"coordinates\":[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]],[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]]],\"type\":\"MultiLineString\"},{\"coordinates\":[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]],\"type\":\"LineString\"},{\"coordinates\":[[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18],[120,-15]]],[]],\"type\":\"MultiPolygon\"},{\"coordinates\":[[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18],[120,-15]]]],\"type\":\"MultiPolygon\"},{\"coordinates\":[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18],[120,-15]]],\"type\":\"Polygon\"},{\"coordinates\":[],\"type\":\"MultiPolygon\"},{\"coordinates\":[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18],[120,-15]]],\"type\":\"Polygon\"}],\"type\":\"GeometryCollection\"},\"id\":\"GW001\",\"type\":\"Feature\",\"properties\":{\"depth\":5,\"comment\":\"Bore run over by dump truck\"}}"--- >>> let featureWithNoBBox = let GeoFeature _ geometry props featureId = bigFeature in GeoFeature Nothing geometry props featureId------- | See Section 2.2 /Feature Objects/ of the GeoJSON spec.--- Parameterised on the property type-data GeoFeature a = GeoFeature {-    _bbox :: Maybe BoundingBoxWithoutCRS,-    _geometry :: GeospatialGeometry,-    _properties :: a,-    _featureId :: Maybe FeatureID } deriving (Show, Eq)--makeLenses ''GeoFeature---- instances---- | decodes Feature objects to and from GeoJSON------ >>> decode' bigFeatureJSON == Just bigFeature--- True------ >>> decode' featureWithNoPropertiesJSON == Just featureWithNoProperties--- True------ >>> decode' featureWithNoIdJSON == Just featureWithNoId--- True------ >>> decode' featureWithNoBBoxJSON == Just featureWithNoBBox--- True------ >>> decode' featureWithNoGeometryJSON == Just featureWithNoGeometry--- True----instance (FromJSON a) => FromJSON (GeoFeature a) where---  parseJSON :: Value -> Parse a-    parseJSON (Object obj) = do-        objType <- obj .: ("type" :: Text)-        if objType /= ("Feature" :: Text)-            then-                mzero-            else-                GeoFeature-                    <$> obj .:? ("bbox" :: Text)-                    <*> obj .: ("geometry" :: Text)-                    <*> obj .: ("properties" :: Text)-                    <*> obj .:? ("id" :: Text)-    parseJSON _ = mzero---- | encodes Feature objects to and from GeoJSON------ >>> (A.decode . A.encode) bigFeature == Just bigFeature--- True------ >>> (A.decode . A.encode) featureWithNoProperties == Just featureWithNoProperties--- True------ >>> (A.decode . A.encode) featureWithNoId == Just featureWithNoId--- True------ >>> (A.decode . A.encode) featureWithNoBBox == Just featureWithNoBBox--- True------ >>> (A.decode . A.encode) featureWithNoGeometry == Just featureWithNoGeometry--- True----instance (ToJSON a) => ToJSON (GeoFeature a) where---  toJSON :: a -> Value-    toJSON (GeoFeature bbox' geom props featureId') = object $ baseAttributes ++ optAttributes "bbox" bbox' ++ optAttributes "id" featureId'-        where-            baseAttributes = ["type" .= ("Feature" :: Text), "properties" .= props, "geometry" .= geom]
− src/Data/Geospatial/GeoFeatureCollection.hs
@@ -1,204 +0,0 @@-{-# LANGUAGE NoImplicitPrelude, OverloadedStrings, TemplateHaskell #-}----------------------------------------------------------------------- |--- Module       : Data.Geospatial.GeoFeature--- Copyright    : (C) 2014 Dom De Re--- License      : BSD-style (see the file etc/LICENSE.md)--- Maintainer   : Dom De Re------ See Section 2.3 /Feature Collection Objects/ of the GeoJSON spec------------------------------------------------------------------------module Data.Geospatial.GeoFeatureCollection (-    -- * Types-        GeoFeatureCollection(..)-    -- * Lenses-    ,   boundingbox-    ,   geofeatures-    ) where--import Data.Geospatial.BasicTypes-import Data.Geospatial.GeoFeature-import Data.Geospatial.Geometry.Aeson--import Prelude ( Show, Eq(..), ($) )-import Control.Applicative ( (<$>), (<*>) )-import Control.Lens ( makeLenses )-import Control.Monad ( mzero )-import Data.Aeson ( FromJSON(..), ToJSON(..), Value(..), (.:), (.:?), (.=), object )-import Data.List ( (++) )-import Data.Maybe ( Maybe(..) )-import Data.Text ( Text )---- $setup------ >>> import Data.Geospatial.Geometry--- >>> import Data.LinearRing--- >>> import Data.LineString--- >>> import Data.Geospatial.Geometry.GeoMultiPolygon (mergeGeoPolygons)--- >>> import Data.Geospatial.Geometry.GeoMultiLine (mergeGeoLines)------ >>> import Control.Monad ( return )--- >>> import qualified Data.Aeson as A--- >>> import qualified Data.ByteString.Lazy.Char8 as BS--- >>> import Data.Function ( (.) )--- >>> import Data.Int ( Int )--- >>> import Data.Maybe ( Maybe(..) )--- >>> import Data.String--- >>> import qualified Data.Text as T------ >>> let decode' = A.decode . BS.pack; decode' :: (FromJSON a) => String -> Maybe a------ Test Bounding Box Data--- >>> let lshapedPolyVertices = return (makeLinearRing [120.0, -15.0] [127.0, -15.0] [127.0, -25.0] [[124.0, -25.0], [124.0, -18.0], [120.0, -18.0]]) :: [LinearRing GeoPositionWithoutCRS]--- >>> let lshapedPolyLineVertices = makeLineString [120.0, -15.0] [127.0, -15.0] [[127.0, -25.0], [124.0, -25.0], [124.0, -18.0], [120.0, -18.0]] :: LineString GeoPositionWithoutCRS--- >>> let emptyVertices = [] :: [LinearRing GeoPositionWithoutCRS]--- >>> let emptyLineVertices = [] :: [GeoPositionWithoutCRS]--- >>> let testLatLonBBox = [-32, 147.5, -29.5, 151.0] :: BoundingBoxWithoutCRS--- >>> let testLatLonBBoxJSON = "[-32,147.5,-29.5,151]"--- >>> let testEmptyBBox = [] :: BoundingBoxWithoutCRS--- >>> let testEmptyBBoxJSON = "[]"------ End Test Bounding Box Data--- Test Geometry Data--- Polys--- >>> let lShapedPolyJSON = "{\"coordinates\":[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18],[120,-15]]],\"type\":\"Polygon\"}"------ Upside down L Shaped Poly------ (120, -15)                (127, -15)--- *---------------------------*--- |                           |--- |                           |--- |             (124, -18)    |--- *---------------*           |--- (120, -18)      |           |---                 |           |---                 |           |---                 |           |---                 |           |---                 |           |---                 |           |---                 *-----------*---               (124, -25)  (127, -25)------ >>> let lShapedGeoPoly = GeoPolygon lshapedPolyVertices--- >>> let lShapedPoly = Polygon lShapedGeoPoly--- >>> let emptyPolyJSON = "{\"type\":\"Polygon\",\"coordinates\":[]}"--- >>> let emptyGeoPoly = GeoPolygon emptyVertices--- >>> let emptyPoly = Polygon emptyGeoPoly------ Multi Polys--- >>> let emptyMultiPolyJSON = "{\"type\":\"MultiPolygon\",\"coordinates\":[]}"--- >>> let emptyMultiGeoPoly = GeoMultiPolygon []--- >>> let emptyMultiPoly = MultiPolygon emptyMultiGeoPoly--- >>> let singlePolyMultiPolyJSON = "{\"type\":\"MultiPolygon\",\"coordinates\":[[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]]]]}"--- >>> let singlePolyGeoMultiPoly = mergeGeoPolygons [lShapedGeoPoly]--- >>> let singlePolyMultiPoly = MultiPolygon singlePolyGeoMultiPoly--- >>> let multiPolyJSON = "{\"type\":\"MultiPolygon\",\"coordinates\":[[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]]],[]]}"--- >>> let geoMultiPoly = mergeGeoPolygons [lShapedGeoPoly, emptyGeoPoly]--- >>> let multiPoly = MultiPolygon geoMultiPoly------ Line Data--- >>> let lShapedLineJSON = "{\"type\":\"LineString\",\"coordinates\":[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]]}"--- >>> let lShapedGeoLine = GeoLine lshapedPolyLineVertices--- >>> let lShapedLine = Line lShapedGeoLine------ Multi Lines--- >>> let emptyMultiLineJSON = "{\"type\":\"MultiLineString\",\"coordinates\":[]}"--- >>> let emptyMultiGeoLine = GeoMultiLine []--- >>> let emptyMultiLine = MultiLine emptyMultiGeoLine--- >>> let singleLineMultiLineJSON = "{\"type\":\"MultiLineString\",\"coordinates\":[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]]]}"--- >>> let singleLineGeoMultiLine = mergeGeoLines [lShapedGeoLine]--- >>> let singleLineMultiLine = MultiLine singleLineGeoMultiLine--- >>> let multiLineJSON = "{\"coordinates\":[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]],[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]]],\"type\":\"MultiLineString\"}"--- >>> let geoMultiLine = mergeGeoLines [lShapedGeoLine, lShapedGeoLine]--- >>> let multiLine = MultiLine geoMultiLine--- >>> let emptyCollectionJSON = "{\"type\":\"GeometryCollection\",\"geometries\":[]}"--- >>> let emptyCollection = Collection []--- >>> let bigassCollectionJSON = "{\"geometries\":[{\"coordinates\":[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]]],\"type\":\"MultiLineString\"},{\"coordinates\":[],\"type\":\"MultiLineString\"},{\"coordinates\":[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]],[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]]],\"type\":\"MultiLineString\"},{\"coordinates\":[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]],\"type\":\"LineString\"},{\"coordinates\":[[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18],[120,-15]]],[]],\"type\":\"MultiPolygon\"},{\"coordinates\":[[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18],[120,-15]]]],\"type\":\"MultiPolygon\"},{\"coordinates\":[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18],[120,-15]]],\"type\":\"Polygon\"},{\"coordinates\":[],\"type\":\"MultiPolygon\"},{\"coordinates\":[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18],[120,-15]]],\"type\":\"Polygon\"}],\"type\":\"GeometryCollection\"}"--- >>> let bigassCollection = Collection [singleLineMultiLine, emptyMultiLine, multiLine, lShapedLine, multiPoly, singlePolyMultiPoly, lShapedPoly, emptyMultiPoly, lShapedPoly]------ End Test Geometry Data------ Test Properties--- >>> let testProperties = object [(T.pack "depth") .= (5 :: Int), (T.pack "comment") .= (T.pack "Bore run over by dump truck")]------ Test Features--- >>> let bigFeatureJSON = "{\"bbox\":[-32,147.5,-29.5,151],\"geometry\":{\"geometries\":[{\"coordinates\":[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]]],\"type\":\"MultiLineString\"},{\"coordinates\":[],\"type\":\"MultiLineString\"},{\"coordinates\":[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]],[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]]],\"type\":\"MultiLineString\"},{\"coordinates\":[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]],\"type\":\"LineString\"},{\"coordinates\":[[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18],[120,-15]]],[]],\"type\":\"MultiPolygon\"},{\"coordinates\":[[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18],[120,-15]]]],\"type\":\"MultiPolygon\"},{\"coordinates\":[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18],[120,-15]]],\"type\":\"Polygon\"},{\"coordinates\":[],\"type\":\"MultiPolygon\"},{\"coordinates\":[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18],[120,-15]]],\"type\":\"Polygon\"}],\"type\":\"GeometryCollection\"},\"id\":\"GW001\",\"type\":\"Feature\",\"properties\":{\"depth\":5,\"comment\":\"Bore run over by dump truck\"}}"--- >>> let bigFeature = GeoFeature (Just testLatLonBBox) bigassCollection testProperties (Just "GW001")--- >>> let featureWithNoPropertiesJSON = "{\"bbox\":[-32,147.5,-29.5,151],\"geometry\":{\"geometries\":[{\"coordinates\":[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]]],\"type\":\"MultiLineString\"},{\"coordinates\":[],\"type\":\"MultiLineString\"},{\"coordinates\":[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]],[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]]],\"type\":\"MultiLineString\"},{\"coordinates\":[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]],\"type\":\"LineString\"},{\"coordinates\":[[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18],[120,-15]]],[]],\"type\":\"MultiPolygon\"},{\"coordinates\":[[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18],[120,-15]]]],\"type\":\"MultiPolygon\"},{\"coordinates\":[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18],[120,-15]]],\"type\":\"Polygon\"},{\"coordinates\":[],\"type\":\"MultiPolygon\"},{\"coordinates\":[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18],[120,-15]]],\"type\":\"Polygon\"}],\"type\":\"GeometryCollection\"},\"id\":\"GW001\",\"type\":\"Feature\",\"properties\":null}"--- >>> let featureWithNoProperties = let GeoFeature bbox geometry _ featureId = bigFeature in GeoFeature bbox geometry Null featureId--- >>> let featureWithNoGeometryJSON = "{\"type\":\"Feature\",\"properties\":{\"depth\":5,\"comment\":\"Bore run over by dump truck\"},\"geometry\":null,\"bbox\":[-32,147.5,-29.5,151],\"id\":\"GW001\"}"--- >>> let featureWithNoGeometry = let GeoFeature bbox _ props featureId = bigFeature in GeoFeature bbox NoGeometry props featureId--- >>> let featureWithNoIdJSON = "{\"bbox\":[-32,147.5,-29.5,151],\"geometry\":{\"geometries\":[{\"coordinates\":[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]]],\"type\":\"MultiLineString\"},{\"coordinates\":[],\"type\":\"MultiLineString\"},{\"coordinates\":[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]],[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]]],\"type\":\"MultiLineString\"},{\"coordinates\":[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]],\"type\":\"LineString\"},{\"coordinates\":[[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18],[120,-15]]],[]],\"type\":\"MultiPolygon\"},{\"coordinates\":[[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18],[120,-15]]]],\"type\":\"MultiPolygon\"},{\"coordinates\":[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18],[120,-15]]],\"type\":\"Polygon\"},{\"coordinates\":[],\"type\":\"MultiPolygon\"},{\"coordinates\":[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18],[120,-15]]],\"type\":\"Polygon\"}],\"type\":\"GeometryCollection\"},\"type\":\"Feature\",\"properties\":{\"depth\":5,\"comment\":\"Bore run over by dump truck\"}}"--- >>> let featureWithNoId = let GeoFeature bbox geometry props _ = bigFeature in GeoFeature bbox geometry props Nothing--- >>> let featureWithNoBBoxJSON = "{\"geometry\":{\"geometries\":[{\"coordinates\":[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]]],\"type\":\"MultiLineString\"},{\"coordinates\":[],\"type\":\"MultiLineString\"},{\"coordinates\":[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]],[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]]],\"type\":\"MultiLineString\"},{\"coordinates\":[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]],\"type\":\"LineString\"},{\"coordinates\":[[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18],[120,-15]]],[]],\"type\":\"MultiPolygon\"},{\"coordinates\":[[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18],[120,-15]]]],\"type\":\"MultiPolygon\"},{\"coordinates\":[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18],[120,-15]]],\"type\":\"Polygon\"},{\"coordinates\":[],\"type\":\"MultiPolygon\"},{\"coordinates\":[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18],[120,-15]]],\"type\":\"Polygon\"}],\"type\":\"GeometryCollection\"},\"id\":\"GW001\",\"type\":\"Feature\",\"properties\":{\"depth\":5,\"comment\":\"Bore run over by dump truck\"}}"--- >>> let featureWithNoBBox = let GeoFeature _ geometry props featureId = bigFeature in GeoFeature Nothing geometry props featureId--- >>> let bigAssFeatureCollectionJSON = "{\"bbox\":[-32,147.5,-29.5,151],\"features\":[{\"geometry\":{\"geometries\":[{\"coordinates\":[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]]],\"type\":\"MultiLineString\"},{\"coordinates\":[],\"type\":\"MultiLineString\"},{\"coordinates\":[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]],[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]]],\"type\":\"MultiLineString\"},{\"coordinates\":[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]],\"type\":\"LineString\"},{\"coordinates\":[[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18],[120,-15]]],[]],\"type\":\"MultiPolygon\"},{\"coordinates\":[[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18],[120,-15]]]],\"type\":\"MultiPolygon\"},{\"coordinates\":[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18],[120,-15]]],\"type\":\"Polygon\"},{\"coordinates\":[],\"type\":\"MultiPolygon\"},{\"coordinates\":[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18],[120,-15]]],\"type\":\"Polygon\"}],\"type\":\"GeometryCollection\"},\"id\":\"GW001\",\"type\":\"Feature\",\"properties\":{\"depth\":5,\"comment\":\"Bore run over by dump truck\"}},{\"bbox\":[-32,147.5,-29.5,151],\"geometry\":null,\"id\":\"GW001\",\"type\":\"Feature\",\"properties\":{\"depth\":5,\"comment\":\"Bore run over by dump truck\"}},{\"geometry\":{\"geometries\":[{\"coordinates\":[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]]],\"type\":\"MultiLineString\"},{\"coordinates\":[],\"type\":\"MultiLineString\"},{\"coordinates\":[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]],[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]]],\"type\":\"MultiLineString\"},{\"coordinates\":[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]],\"type\":\"LineString\"},{\"coordinates\":[[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18],[120,-15]]],[]],\"type\":\"MultiPolygon\"},{\"coordinates\":[[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18],[120,-15]]]],\"type\":\"MultiPolygon\"},{\"coordinates\":[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18],[120,-15]]],\"type\":\"Polygon\"},{\"coordinates\":[],\"type\":\"MultiPolygon\"},{\"coordinates\":[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18],[120,-15]]],\"type\":\"Polygon\"}],\"type\":\"GeometryCollection\"},\"id\":\"GW001\",\"type\":\"Feature\",\"properties\":{\"depth\":5,\"comment\":\"Bore run over by dump truck\"}},{\"bbox\":[-32,147.5,-29.5,151],\"geometry\":{\"geometries\":[{\"coordinates\":[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]]],\"type\":\"MultiLineString\"},{\"coordinates\":[],\"type\":\"MultiLineString\"},{\"coordinates\":[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]],[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]]],\"type\":\"MultiLineString\"},{\"coordinates\":[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]],\"type\":\"LineString\"},{\"coordinates\":[[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18],[120,-15]]],[]],\"type\":\"MultiPolygon\"},{\"coordinates\":[[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18],[120,-15]]]],\"type\":\"MultiPolygon\"},{\"coordinates\":[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18],[120,-15]]],\"type\":\"Polygon\"},{\"coordinates\":[],\"type\":\"MultiPolygon\"},{\"coordinates\":[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18],[120,-15]]],\"type\":\"Polygon\"}],\"type\":\"GeometryCollection\"},\"type\":\"Feature\",\"properties\":{\"depth\":5,\"comment\":\"Bore run over by dump truck\"}},{\"bbox\":[-32,147.5,-29.5,151],\"geometry\":{\"geometries\":[{\"coordinates\":[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]]],\"type\":\"MultiLineString\"},{\"coordinates\":[],\"type\":\"MultiLineString\"},{\"coordinates\":[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]],[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]]],\"type\":\"MultiLineString\"},{\"coordinates\":[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]],\"type\":\"LineString\"},{\"coordinates\":[[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18],[120,-15]]],[]],\"type\":\"MultiPolygon\"},{\"coordinates\":[[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18],[120,-15]]]],\"type\":\"MultiPolygon\"},{\"coordinates\":[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18],[120,-15]]],\"type\":\"Polygon\"},{\"coordinates\":[],\"type\":\"MultiPolygon\"},{\"coordinates\":[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18],[120,-15]]],\"type\":\"Polygon\"}],\"type\":\"GeometryCollection\"},\"id\":\"GW001\",\"type\":\"Feature\",\"properties\":null},{\"bbox\":[-32,147.5,-29.5,151],\"geometry\":{\"geometries\":[{\"coordinates\":[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]]],\"type\":\"MultiLineString\"},{\"coordinates\":[],\"type\":\"MultiLineString\"},{\"coordinates\":[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]],[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]]],\"type\":\"MultiLineString\"},{\"coordinates\":[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]],\"type\":\"LineString\"},{\"coordinates\":[[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18],[120,-15]]],[]],\"type\":\"MultiPolygon\"},{\"coordinates\":[[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18],[120,-15]]]],\"type\":\"MultiPolygon\"},{\"coordinates\":[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18],[120,-15]]],\"type\":\"Polygon\"},{\"coordinates\":[],\"type\":\"MultiPolygon\"},{\"coordinates\":[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18],[120,-15]]],\"type\":\"Polygon\"}],\"type\":\"GeometryCollection\"},\"id\":\"GW001\",\"type\":\"Feature\",\"properties\":{\"depth\":5,\"comment\":\"Bore run over by dump truck\"}}],\"type\":\"FeatureCollection\"}"--- >>> let bigAssFeatureCollection = GeoFeatureCollection (Just testLatLonBBox) [featureWithNoBBox, featureWithNoGeometry, featureWithNoBBox, featureWithNoId, featureWithNoProperties, bigFeature]--- >>> let bigAssFeatureCollectionWithNoBBoxJSON = "{\"features\":[{\"geometry\":{\"geometries\":[{\"coordinates\":[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]]],\"type\":\"MultiLineString\"},{\"coordinates\":[],\"type\":\"MultiLineString\"},{\"coordinates\":[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]],[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]]],\"type\":\"MultiLineString\"},{\"coordinates\":[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]],\"type\":\"LineString\"},{\"coordinates\":[[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18],[120,-15]]],[]],\"type\":\"MultiPolygon\"},{\"coordinates\":[[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18],[120,-15]]]],\"type\":\"MultiPolygon\"},{\"coordinates\":[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18],[120,-15]]],\"type\":\"Polygon\"},{\"coordinates\":[],\"type\":\"MultiPolygon\"},{\"coordinates\":[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18],[120,-15]]],\"type\":\"Polygon\"}],\"type\":\"GeometryCollection\"},\"id\":\"GW001\",\"type\":\"Feature\",\"properties\":{\"depth\":5,\"comment\":\"Bore run over by dump truck\"}},{\"bbox\":[-32,147.5,-29.5,151],\"geometry\":null,\"id\":\"GW001\",\"type\":\"Feature\",\"properties\":{\"depth\":5,\"comment\":\"Bore run over by dump truck\"}},{\"geometry\":{\"geometries\":[{\"coordinates\":[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]]],\"type\":\"MultiLineString\"},{\"coordinates\":[],\"type\":\"MultiLineString\"},{\"coordinates\":[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]],[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]]],\"type\":\"MultiLineString\"},{\"coordinates\":[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]],\"type\":\"LineString\"},{\"coordinates\":[[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18],[120,-15]]],[]],\"type\":\"MultiPolygon\"},{\"coordinates\":[[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18],[120,-15]]]],\"type\":\"MultiPolygon\"},{\"coordinates\":[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18],[120,-15]]],\"type\":\"Polygon\"},{\"coordinates\":[],\"type\":\"MultiPolygon\"},{\"coordinates\":[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18],[120,-15]]],\"type\":\"Polygon\"}],\"type\":\"GeometryCollection\"},\"id\":\"GW001\",\"type\":\"Feature\",\"properties\":{\"depth\":5,\"comment\":\"Bore run over by dump truck\"}},{\"bbox\":[-32,147.5,-29.5,151],\"geometry\":{\"geometries\":[{\"coordinates\":[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]]],\"type\":\"MultiLineString\"},{\"coordinates\":[],\"type\":\"MultiLineString\"},{\"coordinates\":[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]],[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]]],\"type\":\"MultiLineString\"},{\"coordinates\":[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]],\"type\":\"LineString\"},{\"coordinates\":[[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18],[120,-15]]],[]],\"type\":\"MultiPolygon\"},{\"coordinates\":[[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18],[120,-15]]]],\"type\":\"MultiPolygon\"},{\"coordinates\":[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18],[120,-15]]],\"type\":\"Polygon\"},{\"coordinates\":[],\"type\":\"MultiPolygon\"},{\"coordinates\":[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18],[120,-15]]],\"type\":\"Polygon\"}],\"type\":\"GeometryCollection\"},\"type\":\"Feature\",\"properties\":{\"depth\":5,\"comment\":\"Bore run over by dump truck\"}},{\"bbox\":[-32,147.5,-29.5,151],\"geometry\":{\"geometries\":[{\"coordinates\":[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]]],\"type\":\"MultiLineString\"},{\"coordinates\":[],\"type\":\"MultiLineString\"},{\"coordinates\":[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]],[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]]],\"type\":\"MultiLineString\"},{\"coordinates\":[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]],\"type\":\"LineString\"},{\"coordinates\":[[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18],[120,-15]]],[]],\"type\":\"MultiPolygon\"},{\"coordinates\":[[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18],[120,-15]]]],\"type\":\"MultiPolygon\"},{\"coordinates\":[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18],[120,-15]]],\"type\":\"Polygon\"},{\"coordinates\":[],\"type\":\"MultiPolygon\"},{\"coordinates\":[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18],[120,-15]]],\"type\":\"Polygon\"}],\"type\":\"GeometryCollection\"},\"id\":\"GW001\",\"type\":\"Feature\",\"properties\":null},{\"bbox\":[-32,147.5,-29.5,151],\"geometry\":{\"geometries\":[{\"coordinates\":[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]]],\"type\":\"MultiLineString\"},{\"coordinates\":[],\"type\":\"MultiLineString\"},{\"coordinates\":[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]],[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]]],\"type\":\"MultiLineString\"},{\"coordinates\":[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]],\"type\":\"LineString\"},{\"coordinates\":[[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18],[120,-15]]],[]],\"type\":\"MultiPolygon\"},{\"coordinates\":[[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18],[120,-15]]]],\"type\":\"MultiPolygon\"},{\"coordinates\":[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18],[120,-15]]],\"type\":\"Polygon\"},{\"coordinates\":[],\"type\":\"MultiPolygon\"},{\"coordinates\":[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18],[120,-15]]],\"type\":\"Polygon\"}],\"type\":\"GeometryCollection\"},\"id\":\"GW001\",\"type\":\"Feature\",\"properties\":{\"depth\":5,\"comment\":\"Bore run over by dump truck\"}}],\"type\":\"FeatureCollection\"}"--- >>> let bigAssFeatureCollectionWithNoBBox = let GeoFeatureCollection _ features = bigAssFeatureCollection in GeoFeatureCollection Nothing features--- >>> let emptyFeatureCollectionJSON = "{\"type\":\"FeatureCollection\",\"features\":[]}"--- >>> let emptyFeatureCollection = GeoFeatureCollection Nothing [] :: GeoFeatureCollection Value--- >>> let emptyFeatureCollectionWithBBoxJSON = "{\"type\":\"FeatureCollection\",\"features\":[],\"bbox\":[-32,147.5,-29.5,151]}"--- >>> let emptyFeatureCollectionWithBBox = GeoFeatureCollection (Just testLatLonBBox) []; emptyFeatureCollectionWithBBox :: GeoFeatureCollection Value------- | See Section 2.3 /Feature Collection Objects/ of the GeoJSON spec----data GeoFeatureCollection a = GeoFeatureCollection-    {   _boundingbox :: Maybe BoundingBoxWithoutCRS-    ,   _geofeatures :: [GeoFeature a]-    } deriving (Show, Eq)--makeLenses ''GeoFeatureCollection---- instances---- | Decodes FeatureCollection objects to and from GeoJSON------ >>> decode' bigAssFeatureCollectionJSON == Just bigAssFeatureCollection--- True------ >>> decode' bigAssFeatureCollectionWithNoBBoxJSON == Just bigAssFeatureCollectionWithNoBBox--- True------ >>> decode' emptyFeatureCollectionWithBBoxJSON == Just emptyFeatureCollectionWithBBox--- True------ >>> decode' emptyFeatureCollectionJSON == Just emptyFeatureCollection--- True----instance (FromJSON a) => FromJSON (GeoFeatureCollection a) where---  parseJSON :: Value -> Parse a-    parseJSON (Object obj) = do-        objType <- obj .: ("type" :: Text)-        if objType /= ("FeatureCollection" :: Text)-            then-                mzero-            else-                GeoFeatureCollection-                    <$> obj .:? ("bbox" :: Text)-                    <*> obj .: ("features" :: Text)-    parseJSON _ = mzero---- | Encodes FeatureCollection objects to and from GeoJSON------ >>> (A.decode . A.encode) bigAssFeatureCollection == Just bigAssFeatureCollection--- True------ >>> (A.decode . A.encode) bigAssFeatureCollectionWithNoBBox == Just bigAssFeatureCollectionWithNoBBox--- True------ >>> (A.decode . A.encode) emptyFeatureCollectionWithBBox == Just emptyFeatureCollectionWithBBox--- True------ >>> (A.decode . A.encode) emptyFeatureCollection == Just emptyFeatureCollection--- True----instance (ToJSON a) => ToJSON (GeoFeatureCollection a) where---  toJSON :: a -> Value-    toJSON (GeoFeatureCollection bbox' features) = object $ baseAttributes ++ optAttributes "bbox" bbox'-        where-            baseAttributes = ["type" .= ("FeatureCollection" :: Text), "features" .= features]
− src/Data/Geospatial/GeoPosition.hs
@@ -1,38 +0,0 @@----------------------------------------------------------------------- |--- Module       : Data.Geospatial.GeoPosition--- Copyright    : (C) 2014 Dom De Re--- License      : BSD-style (see the file etc/LICENSE.md)--- Maintainer   : Dom De Re------ see Section 2.1.1 /Position/ in the GeoJSON Spec---------------------------------------------------------------------module Data.Geospatial.GeoPosition (-    -- * Type-        GeoPosition(..)-    -- * Functions-    ,   stripCRSFromPosition-    ) where--import Data.Geospatial.BasicTypes---- | see Section 2.1.1 /Position/ in the GeoJSON Spec,--- I make the assumption here that the only position types we will use will--- involve easting or northing (+ve or -ve Altitude) or lon or lat (+ve or -ve Altitude)-data GeoPosition =-        LonLat Longitude Latitude-    |   LonLatAlt Longitude Latitude Altitude-    |   EastingNorthing Easting Northing-    |   EastingNorthingAlt Easting Northing Altitude---- | the `GeoPosition` is a bit special in that when you convert it to GeoJSON,--- it will lose the CRS info attached to it and cannot be read back in--- from the GeoJSON.  Hence it is ineligible for the `FromJSON` type class,--- so this function will strip it down to a `GeoPositionWithoutCRS`, which is eligible-stripCRSFromPosition :: GeoPosition -> GeoPositionWithoutCRS-stripCRSFromPosition (LonLat lon lat)                           = [lon, lat]-stripCRSFromPosition (LonLatAlt lon lat alt)                    = [lon, lat, alt]-stripCRSFromPosition (EastingNorthing easting northing)         = [easting, northing]-stripCRSFromPosition (EastingNorthingAlt easting northing alt)  = [easting, northing, alt]--
− src/Data/Geospatial/Geometry.hs
@@ -1,246 +0,0 @@-{-# LANGUAGE OverloadedStrings, TemplateHaskell #-}----------------------------------------------------------------------- |--- Module       : Data.Geospatial.Geometry--- Copyright    : (C) 2014 Dom De Re--- License      : BSD-style (see the file etc/LICENSE.md)--- Maintainer   : Dom De Re------ See section 2.1 "Geometry Objects" in the GeoJSON Spec.------------------------------------------------------------------------module Data.Geospatial.Geometry (-    -- * Types-        GeoPoint(..)-    ,   GeoMultiPoint(..), splitGeoMultiPoint, mergeGeoPoints-    ,   GeoPolygon(..)-    ,   GeoMultiPolygon(..), splitGeoMultiPolygon, mergeGeoPolygons-    ,   GeoLine(..)-    ,   GeoMultiLine(..), splitGeoMultiLine, mergeGeoLines-    ,   GeospatialGeometry(..)-    -- * Lenses-    ,   unGeoPoint-    ,   unGeoMultiPoint-    ,   unGeoPolygon-    ,   unGeoMultiPolygon-    ,   unGeoLine-    ,   unGeoMultiLine-    -- * Prisms-    ,   _NoGeometry-    ,   _Point-    ,   _MultiPoint-    ,   _Polygon-    ,   _MultiPolygon-    ,   _Line-    ,   _MultiLine-    ,   _Collection-    ) where--import Data.Geospatial.Geometry.Aeson-import Data.Geospatial.Geometry.GeoLine-import Data.Geospatial.Geometry.GeoMultiLine-import Data.Geospatial.Geometry.GeoMultiPoint-import Data.Geospatial.Geometry.GeoMultiPolygon-import Data.Geospatial.Geometry.GeoPoint-import Data.Geospatial.Geometry.GeoPolygon--import Control.Applicative ( (<$>) )-import Control.Lens ( makePrisms )-import Control.Monad ( mzero )-import Data.Aeson-    (   FromJSON(..)-    ,   ToJSON(..)-    ,   Value(..)-    ,   Object-    ,   (.:)-    ,   (.=)-    ,   object-    )-import Data.Aeson.Types ( Parser )-import Data.Text ( Text )---- $setup------ >>> import Data.Geospatial.BasicTypes--- >>> import Data.LinearRing--- >>> import Data.LineString--- >>> import Data.Geospatial.Geometry.GeoMultiPolygon (mergeGeoPolygons)--- >>> import Data.Geospatial.Geometry.GeoMultiLine (mergeGeoLines)------ >>> import Control.Monad ( return )--- >>> import qualified Data.Aeson as A--- >>> import qualified Data.ByteString.Lazy.Char8 as BS------ >>> let lshapedPolyVertices = return (makeLinearRing [120.0, -15.0] [127.0, -15.0] [127.0, -25.0] [[124.0, -25.0], [124.0, -18.0], [120.0, -18.0]]) :: [LinearRing GeoPositionWithoutCRS]--- >>> let lshapedPolyLineVertices = makeLineString [120.0, -15.0] [127.0, -15.0] [[127.0, -25.0], [124.0, -25.0], [124.0, -18.0], [120.0, -18.0]] :: LineString GeoPositionWithoutCRS--- >>> let emptyVertices = [] :: [LinearRing GeoPositionWithoutCRS]------ >>> let decode' = A.decode . BS.pack; decode' :: (FromJSON a) => String -> Maybe a------ Test Geometry Data--- Polys--- >>> let lShapedPolyJSON = "{\"coordinates\":[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18],[120,-15]]],\"type\":\"Polygon\"}"------ Upside down L Shaped Poly------ (120, -15)                (127, -15)--- *---------------------------*--- |                           |--- |                           |--- |             (124, -18)    |--- *---------------*           |--- (120, -18)      |           |---                 |           |---                 |           |---                 |           |---                 |           |---                 |           |---                 |           |---                 *-----------*---               (124, -25)  (127, -25)------ >>> let lShapedGeoPoly = GeoPolygon lshapedPolyVertices--- >>> let lShapedPoly = Polygon lShapedGeoPoly--- >>> let emptyPolyJSON = "{\"type\":\"Polygon\",\"coordinates\":[]}"--- >>> let emptyGeoPoly = GeoPolygon emptyVertices--- >>> let emptyPoly = Polygon emptyGeoPoly------ Multi Polys--- >>> let emptyMultiPolyJSON = "{\"type\":\"MultiPolygon\",\"coordinates\":[]}"--- >>> let emptyMultiGeoPoly = GeoMultiPolygon []--- >>> let emptyMultiPoly = MultiPolygon emptyMultiGeoPoly--- >>> let singlePolyMultiPolyJSON = "{\"type\":\"MultiPolygon\",\"coordinates\":[[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]]]]}"--- >>> let singlePolyGeoMultiPoly = mergeGeoPolygons [lShapedGeoPoly]--- >>> let singlePolyMultiPoly = MultiPolygon singlePolyGeoMultiPoly--- >>> let multiPolyJSON = "{\"type\":\"MultiPolygon\",\"coordinates\":[[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]]],[]]}"--- >>> let geoMultiPoly = mergeGeoPolygons [lShapedGeoPoly, emptyGeoPoly]--- >>> let multiPoly = MultiPolygon geoMultiPoly------ Line Data--- >>> let lShapedLineJSON = "{\"type\":\"Line\",\"coordinates\":[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]]}"--- >>> let lShapedGeoLine = GeoLine lshapedPolyLineVertices--- >>> let lShapedLine = Line lShapedGeoLine------ Multi Lines--- >>> let emptyMultiLineJSON = "{\"type\":\"MultiLineString\",\"coordinates\":[]}"--- >>> let emptyMultiGeoLine = GeoMultiLine []--- >>> let emptyMultiLine = MultiLine emptyMultiGeoLine--- >>> let singleLineMultiLineJSON = "{\"type\":\"MultiLineString\",\"coordinates\":[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]]]}"--- >>> let singleLineGeoMultiLine = mergeGeoLines [lShapedGeoLine]--- >>> let singleLineMultiLine = MultiLine singleLineGeoMultiLine--- >>> let multiLineJSON = "{\"coordinates\":[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]],[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]]],\"type\":\"MultiLineString\"}"--- >>> let geoMultiLine = mergeGeoLines [lShapedGeoLine, lShapedGeoLine]--- >>> let multiLine = MultiLine geoMultiLine--- >>> let emptyCollectionJSON = "{\"type\":\"GeometryCollection\",\"geometries\":[]}"--- >>> let emptyCollection = Collection []--- >>> let bigassCollectionJSON = "{\"geometries\":[{\"coordinates\":[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]]],\"type\":\"MultiLineString\"},{\"coordinates\":[],\"type\":\"MultiLineString\"},{\"coordinates\":[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]],[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]]],\"type\":\"MultiLineString\"},{\"coordinates\":[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]],\"type\":\"LineString\"},{\"coordinates\":[[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18],[120,-15]]],[]],\"type\":\"MultiPolygon\"},{\"coordinates\":[[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18],[120,-15]]]],\"type\":\"MultiPolygon\"},{\"coordinates\":[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18],[120,-15]]],\"type\":\"Polygon\"},{\"coordinates\":[],\"type\":\"MultiPolygon\"},{\"coordinates\":[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18],[120,-15]]],\"type\":\"Polygon\"}],\"type\":\"GeometryCollection\"}"--- >>> let bigassCollection = Collection [singleLineMultiLine, emptyMultiLine, multiLine, lShapedLine, multiPoly, singlePolyMultiPoly, lShapedPoly, emptyMultiPoly, lShapedPoly]------ End Test Geometry Data---------- | See section 2.1 /Geometry Objects/ in the GeoJSON Spec.-data GeospatialGeometry =-        NoGeometry-    |   Point GeoPoint-    |   MultiPoint GeoMultiPoint-    |   Polygon GeoPolygon-    |   MultiPolygon GeoMultiPolygon-    |   Line GeoLine-    |   MultiLine GeoMultiLine-    |   Collection [GeospatialGeometry] deriving (Show, Eq)--makePrisms ''GeospatialGeometry--geometryFromAeson :: String -> Value -> Parser GeospatialGeometry-geometryFromAeson "Point" obj                           = Point <$> parseJSON obj-geometryFromAeson "MultiPoint" obj                      = MultiPoint <$> parseJSON obj-geometryFromAeson "Polygon" obj                         = Polygon <$> parseJSON obj-geometryFromAeson "MultiPolygon" obj                    = MultiPolygon <$> parseJSON obj-geometryFromAeson "LineString" obj                      = Line <$> parseJSON obj-geometryFromAeson "MultiLineString" obj                 = MultiLine <$> parseJSON obj-geometryFromAeson "GeometryCollection" (Object jsonObj) = Collection <$> (jsonObj .: ("geometries" :: Text))-geometryFromAeson "GeometryCollection" _                = mzero-geometryFromAeson _ _                                   = mzero----- |--- encodes and Geometry Objects to GeoJSON--- (refer to source to see the values for the test values)------ >>> A.encode NoGeometry--- "null"------ >>> (A.decode . A.encode) lShapedPoly == Just lShapedPoly--- True------ >>> (A.decode . A.encode) emptyPoly == Just emptyPoly--- True------ >>> (A.decode . A.encode) emptyMultiPoly == Just emptyMultiPoly--- True------ >>> (A.decode . A.encode) singleLineMultiLine == Just singleLineMultiLine--- True------ >>> (A.decode . A.encode) multiLine == Just multiLine--- True------ >>> (A.decode . A.encode) emptyCollection == Just emptyCollection--- True------ >>> (A.decode . A.encode) bigassCollection == Just bigassCollection--- True----instance ToJSON GeospatialGeometry where---  toJSON :: a -> Value-    toJSON NoGeometry               = Null-    toJSON (Point point)            = toJSON point-    toJSON (MultiPoint points)      = toJSON points-    toJSON (Polygon vertices)       = toJSON vertices-    toJSON (MultiPolygon vertices)  = toJSON vertices-    toJSON (Line vertices)          = toJSON vertices-    toJSON (MultiLine vertices)     = toJSON vertices-    toJSON (Collection geometries)  = object-        [   "type" .= ("GeometryCollection" :: Text)-        ,   "geometries" .= geometries-        ]---- |--- encodes and decodes Geometry Objects to and from GeoJSON--- (refer to source to see the values for the test values)------ >>> decode' lShapedPolyJSON == Just lShapedPoly--- True------ >>> decode' emptyPolyJSON == Just emptyPoly--- True------ >>> decode' emptyMultiPolyJSON == Just emptyMultiPoly--- True------ >>> decode' singleLineMultiLineJSON == Just singleLineMultiLine--- True------ >>> decode' multiLineJSON == Just multiLine--- True------ >>> decode' emptyCollectionJSON == Just emptyCollection--- True------ >>> decode' bigassCollectionJSON == Just bigassCollection--- True------ Aeson doesnt decode "null" into `Null` unfortunately------ decode' "null" :: Maybe GeospatialGeometry--- Just NoGeometry----instance FromJSON GeospatialGeometry where---  parseJSON :: Value -> Parser a-    parseJSON Null = return NoGeometry-    parseJSON (Object obj) = do-        geometryType <- obj .: ("type" :: Text)-        geometryFromAeson geometryType (Object obj)-    parseJSON _ = mzero
− src/Data/Geospatial/Geometry/Aeson.hs
@@ -1,67 +0,0 @@-{-# LANGUAGE OverloadedStrings #-}----------------------------------------------------------------------- |--- Module       : Data.Geosptial.Geometry.Aeson--- Copyright    : (C) 2014 Dom De Re--- License      : BSD-style (see the file etc/LICENSE.md)--- Maintainer   : Dom De Re------ Some helpers for some of the common Aeson ops---------------------------------------------------------------------module Data.Geospatial.Geometry.Aeson (-    -- * Geometry-        readGeometryGeoAeson-    ,   makeGeometryGeoAeson-    -- * Optional fields-    ,   optValFromObj-    ,   optAttributes-    ) where--import Control.Applicative ( (<$>) )-import Control.Monad ( mzero )-import Data.Aeson-    (   FromJSON(..)-    ,   ToJSON(..)-    ,   Object-    ,   Value-    ,   (.:)-    ,   (.:?)-    ,   (.=)-    ,   object-    )-import Data.Aeson.Types ( Pair, Parser )-import Data.Maybe ( Maybe(..) )-import Data.Text ( Text )---- | A generic function that can be used to read in the GeoJSON for:--- `GeoPoint`, `GeoMultiPoint`, `GeoLine`, `GeoMultiLine`, `GeoPolygon` and `GeoMultiPolygon`--- Takes in a String for the GeoJSON geometry type, the type constructor--- for the datatype and the JSON object containing both the 'type' val and the 'coordinates' val----readGeometryGeoAeson :: (FromJSON a, FromJSON b) => String -> (a -> b) -> Object -> Parser b-readGeometryGeoAeson geomTypeString geomType geopointObj = do-    geometryType <- geopointObj .: "type"-    if geometryType == geomTypeString-        then-            geomType <$> geopointObj .: "coordinates"-        else-            mzero---- | The inverse to the above, you just give it the type string and the value for the coordinates--- and it will create the JSON object----makeGeometryGeoAeson :: (ToJSON a) => String -> a -> Value-makeGeometryGeoAeson typeString coordinates =-    object ["type" .= typeString, "coordinates" .= coordinates]---- | get an optional value out of a JSON object:----optValFromObj :: (FromJSON a) => Text -> Object -> Parser (Maybe a)-optValFromObj = flip (.:?)---- | The other way around, given an optional value, will return the attributes that--- should be added to the makeObj input----optAttributes :: (ToJSON a) => Text -> Maybe a -> [Pair]-optAttributes _ Nothing     = []-optAttributes name (Just x) = [name .= x]
− src/Data/Geospatial/Geometry/GeoLine.hs
@@ -1,38 +0,0 @@-{-# LANGUAGE TemplateHaskell #-}----------------------------------------------------------------------- |--- Module       : Data.Geospatial.Geometry.GeoLine--- Copyright    : (C) 2014 Dom De Re--- License      : BSD-style (see the file etc/LICENSE.md)--- Maintainer   : Dom De Re------------------------------------------------------------------------module Data.Geospatial.Geometry.GeoLine (-    -- * Type-        GeoLine(..)-    -- * Lenses-    ,   unGeoLine-    ) where--import Data.Geospatial.BasicTypes-import Data.Geospatial.Geometry.Aeson-import Data.LineString--import Control.Lens ( makeLenses )-import Control.Monad ( mzero )-import Data.Aeson ( FromJSON(..), ToJSON(..), Value(..) )--newtype GeoLine = GeoLine { _unGeoLine :: LineString GeoPositionWithoutCRS } deriving (Show, Eq)--makeLenses ''GeoLine---- instances--instance ToJSON GeoLine where---  toJSON :: a -> Value-    toJSON = makeGeometryGeoAeson "LineString" . _unGeoLine--instance FromJSON GeoLine where---  parseJSON :: Value -> Parser a-    parseJSON (Object o)    = readGeometryGeoAeson "LineString" GeoLine o-    parseJSON _             = mzero
− src/Data/Geospatial/Geometry/GeoMultiLine.hs
@@ -1,50 +0,0 @@-{-# LANGUAGE TemplateHaskell #-}----------------------------------------------------------------------- |--- Module       : Data.Geospatial.Geometry.GeoMultiLine--- Copyright    : (C) 2014 Dom De Re--- License      : BSD-style (see the file etc/LICENSE.md)--- Maintainer   : Dom De Re------------------------------------------------------------------------module Data.Geospatial.Geometry.GeoMultiLine (-    -- * Type-        GeoMultiLine(..)-    -- * Lenses-    ,   unGeoMultiLine-    -- * To Points-    ,   splitGeoMultiLine, mergeGeoLines-    ) where--import Data.Geospatial.BasicTypes-import Data.Geospatial.Geometry.GeoLine-import Data.Geospatial.Geometry.Aeson-import Data.LineString--import Control.Lens ( makeLenses )-import Control.Monad ( mzero )-import Data.Aeson ( FromJSON(..), ToJSON(..), Value(..), Object )--newtype GeoMultiLine    = GeoMultiLine { _unGeoMultiLine :: [LineString GeoPositionWithoutCRS] } deriving (Show, Eq)--makeLenses ''GeoMultiLine----- | Split GeoMultiLine coordinates into multiple GeoLines-splitGeoMultiLine:: GeoMultiLine -> [GeoLine]-splitGeoMultiLine = map GeoLine . _unGeoMultiLine---- | Merge multiple GeoLines into one GeoMultiLine-mergeGeoLines :: [GeoLine] -> GeoMultiLine-mergeGeoLines = GeoMultiLine . map _unGeoLine---- instances--instance ToJSON GeoMultiLine where---  toJSON :: a -> Value-    toJSON = makeGeometryGeoAeson "MultiLineString" . _unGeoMultiLine--instance FromJSON GeoMultiLine where---  parseJSON :: Value -> Parser a-    parseJSON (Object o)    = readGeometryGeoAeson "MultiLineString" GeoMultiLine o-    parseJSON _             = mzero
− src/Data/Geospatial/Geometry/GeoMultiPoint.hs
@@ -1,49 +0,0 @@-{-# LANGUAGE TemplateHaskell #-}----------------------------------------------------------------------- |--- Module       : Data.Geospatial.Geometry.GeoMultiPoint--- Copyright    : (C) 2014 Dom De Re--- License      : BSD-style (see the file etc/LICENSE.md)--- Maintainer   : Dom De Re------------------------------------------------------------------------module Data.Geospatial.Geometry.GeoMultiPoint (-    -- * Type-        GeoMultiPoint(..)-    -- * Lenses-    ,   unGeoMultiPoint-    -- * To Points-    ,   splitGeoMultiPoint, mergeGeoPoints-    ) where--import Data.Geospatial.BasicTypes-import Data.Geospatial.Geometry.GeoPoint-import Data.Geospatial.Geometry.Aeson--import Control.Lens ( makeLenses )-import Control.Monad ( mzero )-import Data.Aeson ( FromJSON(..), ToJSON(..), Value(..), Object )--newtype GeoMultiPoint = GeoMultiPoint { _unGeoMultiPoint :: [GeoPositionWithoutCRS] } deriving (Show, Eq)--makeLenses ''GeoMultiPoint----- | Split GeoMultiPoint coordinates into multiple GeoPoints-splitGeoMultiPoint:: GeoMultiPoint -> [GeoPoint]-splitGeoMultiPoint = map GeoPoint . _unGeoMultiPoint---- | Merge multiple GeoPoints into one GeoMultiPoint-mergeGeoPoints :: [GeoPoint] -> GeoMultiPoint-mergeGeoPoints = GeoMultiPoint . map _unGeoPoint---- instances--instance ToJSON GeoMultiPoint where---  toJSON :: a -> Value-    toJSON = makeGeometryGeoAeson "MultiPoint" . _unGeoMultiPoint--instance FromJSON GeoMultiPoint where---  parseJSON :: Value -> Parser a-    parseJSON (Object o)    = readGeometryGeoAeson "MultiPoint" GeoMultiPoint o-    parseJSON _             = mzero
− src/Data/Geospatial/Geometry/GeoMultiPolygon.hs
@@ -1,49 +0,0 @@-{-# LANGUAGE TemplateHaskell #-}----------------------------------------------------------------------- |--- Module       : Data.Geospatial.Geometry.GeoMultiPolygon--- Copyright    : (C) 2014 Dom De Re--- License      : BSD-style (see the file etc/LICENSE.md)--- Maintainer   : Dom De Re------------------------------------------------------------------------module Data.Geospatial.Geometry.GeoMultiPolygon (-    -- * Type-        GeoMultiPolygon(..)-    -- * Lenses-    ,   unGeoMultiPolygon-    -- * To Polygons-    ,   splitGeoMultiPolygon, mergeGeoPolygons-    ) where--import Data.Geospatial.BasicTypes-import Data.Geospatial.Geometry.GeoPolygon-import Data.Geospatial.Geometry.Aeson-import Data.LinearRing--import Control.Lens ( makeLenses )-import Control.Monad ( mzero )-import Data.Aeson ( FromJSON(..), ToJSON(..), Value(..), Object )--newtype GeoMultiPolygon = GeoMultiPolygon { _unGeoMultiPolygon :: [[LinearRing GeoPositionWithoutCRS]] } deriving (Show, Eq)---- | Split GeoMultiPolygon coordinates into multiple GeoPolygons-splitGeoMultiPolygon :: GeoMultiPolygon -> [GeoPolygon]-splitGeoMultiPolygon = map GeoPolygon . _unGeoMultiPolygon---- | Merge multiple GeoPolygons into one GeoMultiPolygon-mergeGeoPolygons :: [GeoPolygon] -> GeoMultiPolygon-mergeGeoPolygons = GeoMultiPolygon . map _unGeoPolygon--makeLenses ''GeoMultiPolygon---- instances--instance ToJSON GeoMultiPolygon where---  toJSON :: a -> Value-    toJSON = makeGeometryGeoAeson "MultiPolygon" . _unGeoMultiPolygon--instance FromJSON GeoMultiPolygon where---  parseJSON :: Value -> Parser a-    parseJSON (Object o)    = readGeometryGeoAeson "MultiPolygon" GeoMultiPolygon o-    parseJSON _             = mzero
− src/Data/Geospatial/Geometry/GeoPoint.hs
@@ -1,37 +0,0 @@-{-# LANGUAGE TemplateHaskell #-}----------------------------------------------------------------------- |--- Module       : Data.Geospatial.Geometry.GeoPoint--- Copyright    : (C) 2014 Dom De Re--- License      : BSD-style (see the file etc/LICENSE.md)--- Maintainer   : Dom De Re------------------------------------------------------------------------module Data.Geospatial.Geometry.GeoPoint (-    -- * Type-        GeoPoint(..)-    -- * Lenses-    ,   unGeoPoint-    ) where--import Data.Geospatial.BasicTypes-import Data.Geospatial.Geometry.Aeson--import Control.Lens ( makeLenses )-import Control.Monad ( mzero )-import Data.Aeson ( FromJSON(..), ToJSON(..), Value(..), Object )--newtype GeoPoint = GeoPoint { _unGeoPoint :: GeoPositionWithoutCRS } deriving (Show, Eq)--makeLenses ''GeoPoint---- instances--instance ToJSON GeoPoint where---  toJSON :: a -> Value-    toJSON = makeGeometryGeoAeson "Point" . _unGeoPoint--instance FromJSON GeoPoint where---  parseJSON :: Value -> Parser a-    parseJSON (Object o)    = readGeometryGeoAeson "Point" GeoPoint o-    parseJSON _             = mzero
− src/Data/Geospatial/Geometry/GeoPolygon.hs
@@ -1,39 +0,0 @@-{-# LANGUAGE TemplateHaskell #-}----------------------------------------------------------------------- |--- Module       : Data.Geospatial.Geometry.GeoPolygon--- Copyright    : (C) 2014 Dom De Re--- License      : BSD-style (see the file etc/LICENSE.md)--- Maintainer   : Dom De Re------------------------------------------------------------------------module Data.Geospatial.Geometry.GeoPolygon (-    -- * Type-        GeoPolygon(..)-    -- * Lenses-    ,   unGeoPolygon-    ) where--import Data.Geospatial.BasicTypes-import Data.Geospatial.Geometry.Aeson-import Data.Geospatial.GeoPosition-import Data.LinearRing--import Control.Lens ( makeLenses )-import Control.Monad ( mzero )-import Data.Aeson ( FromJSON(..), ToJSON(..), Value(..), Object )--newtype GeoPolygon = GeoPolygon { _unGeoPolygon :: [LinearRing GeoPositionWithoutCRS] } deriving (Show, Eq)--makeLenses ''GeoPolygon---- instances--instance ToJSON GeoPolygon where---  toJSON :: a -> Value-    toJSON = makeGeometryGeoAeson "Polygon" . _unGeoPolygon--instance FromJSON GeoPolygon where---  parseJSON :: Value -> Parser a-    parseJSON (Object o)    = readGeometryGeoAeson "Polygon" GeoPolygon o-    parseJSON _             = mzero
+ src/Data/Geospatial/Internal/BasicTypes.hs view
@@ -0,0 +1,80 @@+{-# LANGUAGE OverloadedStrings #-}++-------------------------------------------------------------------+-- |+-- Module       : Data.Geospatial.Internal.BasicTypes+-- Copyright    : (C) 2014-2018 HS-GeoJSON Project+-- License      : BSD-style (see the file LICENSE.md)+-- Maintainer   : Andrew Newman+--+-- Basic types for GeoJSON representations.+-------------------------------------------------------------------+module Data.Geospatial.Internal.BasicTypes (+    -- * Coordinate types+        Latitude+    ,   Longitude+    ,   Easting+    ,   Northing+    ,   Altitude+    ,   GeoPositionWithoutCRS+    -- * CRS Reference types+    ,   Name+    ,   Code+    ,   Href+    ,   FormatString+    ,   ProjectionType+    -- * Feature Types+    ,   BoundingBoxWithoutCRS+    ,   FeatureID (..)+    ) where++import           Data.Aeson      (FromJSON (..), ToJSON (..), Value (..))+import           Data.Scientific (Scientific, toBoundedInteger)+import           Data.Text       (Text)++type Latitude = Double+type Longitude = Double+type Easting = Double+type Northing = Double+type Altitude = Double++-- | (`GeoPositionWithoutCRS` is a catch all for indeterminate CRSs and for expression of positions+-- before a CRS has been determined+--+type GeoPositionWithoutCRS = [Double]++type Name = Text+type Code = Int+type Href = Text+type FormatString = Text+type ProjectionType = Text++-- Feature Types++data FeatureID =+        FeatureIDText Text+    |   FeatureIDNumber Int deriving (Show, Eq)++instance FromJSON FeatureID where+    parseJSON (Number nID) =+        case x of+            Nothing -> fail "Not an integer value"+            Just z  -> pure $ FeatureIDNumber z+        where+            x = toBoundedInteger nID :: Maybe Int+    parseJSON (String sID) = pure $ FeatureIDText sID+    parseJSON _            = fail "unknown id type"+++instance ToJSON FeatureID where+    toJSON (FeatureIDText a)   = String a+    toJSON (FeatureIDNumber b) = Number (fromInteger $ toInteger b :: Scientific)+++-- | See Section 4 /Bounding Boxes/ of the GeoJSON spec,+-- The length of the list/array must be 2*n where n is the dimensionality of the position type for the CRS+-- with min values first followed by the max values, wich both the min/max sets following the same axis order as the CRS,+-- e.g for WGS84: minLongitude, minLatitude, maxLongitude, maxLatitude+-- The spec mentions that it can be part of a geometry object too but doesnt give an example,+-- This implementation will ignore bboxes on Geometry objects, they can be added if required.+type BoundingBoxWithoutCRS = [Double]
+ src/Data/Geospatial/Internal/CRS.hs view
@@ -0,0 +1,90 @@+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE TemplateHaskell   #-}+-------------------------------------------------------------------+-- |+-- Module       : Data.Geospatial.Internal.CRS+-- Copyright    : (C) 2014-2018 HS-GeoJSON Project+-- License      : BSD-style (see the file LICENSE.md)+-- Maintainer   : Andrew Newman+--+-- See Section 3 /Coordinate Reference System Objects/+-- in the GeoJSON Spec+--+-------------------------------------------------------------------+module Data.Geospatial.Internal.CRS (+    -- * Types+        CRSObject(..)+    -- * Functions+    ,   defaultCRS+    -- * Prisms+    ,   _NoCRS+    ,   _NamedCRS+    ,   _EPSG+    ,   _LinkedCRS+    ) where++import           Data.Geospatial.Internal.BasicTypes++import           Control.Applicative                 ((<$>), (<*>))+import           Control.Lens                        (makePrisms)+import           Control.Monad                       (mzero)+import           Data.Aeson                          (FromJSON (..), Object,+                                                      ToJSON (..), Value (..),+                                                      object, (.:), (.=))+import           Data.Aeson.Types                    (Parser)+import           Data.Text                           (Text)++-- | See Section 3 /Coordinate Reference System Objects/ in the GeoJSON Spec+-- `NoCRS` is required because no 'crs' attribute in a GeoJSON feature is NOT the same thing as+-- a null 'crs' attribute. no 'crs' value implies the default CRS, while a null CRS means+-- you cannot assume a CRS, null will mapped to `NoCRS` while a non-existent attribute will+-- be mapped to a `Nothing` `Maybe` value+data CRSObject =+        NoCRS+    |   NamedCRS !Name+    |   EPSG Code+    |   LinkedCRS !Href !FormatString  deriving (Show, Eq)++makePrisms ''CRSObject++-- | The default CRS according to Section 3 /Coordinate Reference System Objects/ is WGS84 which I believe,+-- from <http://spatialreference.org/ref/epsg/4326/> which translates to this in JSON: <http://spatialreference.org/ref/epsg/4326/json/>)+-- is represented thus:+defaultCRS :: CRSObject+defaultCRS = EPSG 4326++-- instances++-- |+-- decode CRS Objects from GeoJSON+--+-- Aeson doesnt decode "null" to `Null` unfortunately+--+instance FromJSON CRSObject where+    parseJSON Null = return NoCRS+    parseJSON (Object obj) = do+        crsType <- obj .: "type"+        crsObjectFromAeson crsType obj+    parseJSON _ = mzero++-- |+-- encode CRS Objects to GeoJSON+--+instance ToJSON CRSObject where+    toJSON (NamedCRS name)          = object ["type" .= ("name" :: Text), "properties" .= object ["name" .= name]]+    toJSON (EPSG code)              = object ["type" .= ("epsg" :: Text), "properties" .= object ["code" .= code]]+    toJSON (LinkedCRS href format)  = object ["type" .= ("link" :: Text), "properties" .= object ["href" .= href, "type" .= format]]+    toJSON NoCRS                    = Null++-- helpers++crsPropertyFromAesonObj :: (FromJSON a) => Text -> Object -> Parser a+crsPropertyFromAesonObj name obj = do+    props <- obj .: "properties"+    props .: name++crsObjectFromAeson :: Text -> Object -> Parser CRSObject+crsObjectFromAeson "name" obj   = NamedCRS <$> crsPropertyFromAesonObj "name" obj+crsObjectFromAeson "epsg" obj   = EPSG <$> crsPropertyFromAesonObj "code" obj+crsObjectFromAeson "link" obj   = LinkedCRS <$> crsPropertyFromAesonObj "href" obj <*> crsPropertyFromAesonObj "type" obj+crsObjectFromAeson _ _          = mzero
+ src/Data/Geospatial/Internal/GeoFeature.hs view
@@ -0,0 +1,76 @@+{-# LANGUAGE NoImplicitPrelude #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE TemplateHaskell   #-}+-------------------------------------------------------------------+-- |+-- Module       : Data.Geospatial.Internal.GeoFeature+-- Copyright    : (C) 2014-2018 HS-GeoJSON Project+-- License      : BSD-style (see the file LICENSE.md)+-- Maintainer   : Andrew Newman+--+-- See Section 2.2 /Feature Objects/ of the GeoJSON spec.+-- Parameterised on the property type+--+-------------------------------------------------------------------+module Data.Geospatial.Internal.GeoFeature (+    -- * Types+        GeoFeature(..)+    -- * Lenses+    ,   bbox+    ,   geometry+    ,   properties+    ,   featureId+    ) where++import           Data.Geospatial.Internal.BasicTypes+import           Data.Geospatial.Internal.Geometry+import           Data.Geospatial.Internal.Geometry.Aeson++import           Control.Applicative                     ((<$>), (<*>))+import           Control.Lens                            (makeLenses)+import           Control.Monad                           (mzero)+import           Data.Aeson                              (FromJSON (..),+                                                          ToJSON (..),+                                                          Value (..), object,+                                                          (.:), (.:?), (.=))+import           Data.List                               ((++))+import           Data.Maybe                              (Maybe)+import           Data.Text                               (Text)+import           Prelude                                 (Eq (..), Show, ($))++-- | See Section 2.2 /Feature Objects/ of the GeoJSON spec.+-- Parameterised on the property type+data GeoFeature a = GeoFeature {+    _bbox       :: Maybe BoundingBoxWithoutCRS,+    _geometry   :: GeospatialGeometry,+    _properties :: a,+    _featureId  :: Maybe FeatureID } deriving (Show, Eq)++makeLenses ''GeoFeature++-- instances++-- | Decodes Feature objects from GeoJSON+--+instance (FromJSON a) => FromJSON (GeoFeature a) where+--  parseJSON :: Value -> Parse a+    parseJSON (Object obj) = do+        objType <- obj .: ("type" :: Text)+        if objType /= ("Feature" :: Text)+            then+                mzero+            else+                GeoFeature+                    <$> obj .:? ("bbox" :: Text)+                    <*> obj .: ("geometry" :: Text)+                    <*> obj .: ("properties" :: Text)+                    <*> obj .:? ("id" :: Text)+    parseJSON _ = mzero++-- | Encodes Feature objects to GeoJSON+--+instance (ToJSON a) => ToJSON (GeoFeature a) where+--  toJSON :: a -> Value+    toJSON (GeoFeature bbox' geom props featureId') = object $ baseAttributes ++ optAttributes "bbox" bbox' ++ optAttributes "id" featureId'+        where+            baseAttributes = ["type" .= ("Feature" :: Text), "properties" .= props, "geometry" .= geom]
+ src/Data/Geospatial/Internal/GeoFeatureCollection.hs view
@@ -0,0 +1,70 @@+{-# LANGUAGE NoImplicitPrelude #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE TemplateHaskell   #-}+-------------------------------------------------------------------+-- |+-- Module       : Data.Geospatial.Internal.GeoFeature+-- Copyright    : (C) 2014-2018 HS-GeoJSON Project+-- License      : BSD-style (see the file LICENSE.md)+-- Maintainer   : Andrew Newman+--+-- See Section 2.3 /Feature Collection Objects/ of the GeoJSON spec+--+-------------------------------------------------------------------+module Data.Geospatial.Internal.GeoFeatureCollection (+    -- * Types+        GeoFeatureCollection(..)+    -- * Lenses+    ,   boundingbox+    ,   geofeatures+    ) where++import           Data.Geospatial.Internal.BasicTypes+import           Data.Geospatial.Internal.GeoFeature+import           Data.Geospatial.Internal.Geometry.Aeson++import           Control.Applicative                     ((<$>), (<*>))+import           Control.Lens                            (makeLenses)+import           Control.Monad                           (mzero)+import           Data.Aeson                              (FromJSON (..),+                                                          ToJSON (..),+                                                          Value (..), object,+                                                          (.:), (.:?), (.=))+import           Data.List                               ((++))+import           Data.Maybe                              (Maybe (..))+import           Data.Text                               (Text)+import           Prelude                                 (Eq (..), Show, ($))++-- | See Section 2.3 /Feature Collection Objects/ of the GeoJSON spec+--+data GeoFeatureCollection a = GeoFeatureCollection+    {   _boundingbox :: Maybe BoundingBoxWithoutCRS+    ,   _geofeatures :: [GeoFeature a]+    } deriving (Show, Eq)++makeLenses ''GeoFeatureCollection++-- instances++-- | Decode FeatureCollection objects from GeoJSON+--+instance (FromJSON a) => FromJSON (GeoFeatureCollection a) where+--  parseJSON :: Value -> Parse a+    parseJSON (Object obj) = do+        objType <- obj .: ("type" :: Text)+        if objType /= ("FeatureCollection" :: Text)+            then+                mzero+            else+                GeoFeatureCollection+                    <$> obj .:? ("bbox" :: Text)+                    <*> obj .: ("features" :: Text)+    parseJSON _ = mzero++-- | Encode FeatureCollection objects to GeoJSON+--+instance (ToJSON a) => ToJSON (GeoFeatureCollection a) where+--  toJSON :: a -> Value+    toJSON (GeoFeatureCollection bbox' features) = object $ baseAttributes ++ optAttributes "bbox" bbox'+        where+            baseAttributes = ["type" .= ("FeatureCollection" :: Text), "features" .= features]
+ src/Data/Geospatial/Internal/GeoPosition.hs view
@@ -0,0 +1,38 @@+-------------------------------------------------------------------+-- |+-- Module       : Data.Geospatial.Internal.GeoPosition+-- Copyright    : (C) 2014-2018 HS-GeoJSON Project+-- License      : BSD-style (see the file LICENSE.md)+-- Maintainer   : Andrew Newman+--+-- see Section 2.1.1 /Position/ in the GeoJSON Spec+-------------------------------------------------------------------+module Data.Geospatial.Internal.GeoPosition (+    -- * Type+        GeoPosition(..)+    -- * Functions+    ,   stripCRSFromPosition+    ) where++import           Data.Geospatial.Internal.BasicTypes++-- | see Section 2.1.1 /Position/ in the GeoJSON Spec,+-- I make the assumption here that the only position types we will use will+-- involve easting or northing (+ve or -ve Altitude) or lon or lat (+ve or -ve Altitude)+data GeoPosition =+        LonLat Longitude Latitude+    |   LonLatAlt Longitude Latitude Altitude+    |   EastingNorthing Easting Northing+    |   EastingNorthingAlt Easting Northing Altitude++-- | the `GeoPosition` is a bit special in that when you convert it to GeoJSON,+-- it will lose the CRS info attached to it and cannot be read back in+-- from the GeoJSON.  Hence it is ineligible for the `FromJSON` type class,+-- so this function will strip it down to a `GeoPositionWithoutCRS`, which is eligible+stripCRSFromPosition :: GeoPosition -> GeoPositionWithoutCRS+stripCRSFromPosition (LonLat lon lat)                           = [lon, lat]+stripCRSFromPosition (LonLatAlt lon lat alt)                    = [lon, lat, alt]+stripCRSFromPosition (EastingNorthing easting northing)         = [easting, northing]+stripCRSFromPosition (EastingNorthingAlt easting northing alt)  = [easting, northing, alt]++
+ src/Data/Geospatial/Internal/Geometry.hs view
@@ -0,0 +1,111 @@+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE TemplateHaskell   #-}+-------------------------------------------------------------------+-- |+-- Module       : Data.Geospatial.Internal.Geometry+-- Copyright    : (C) 2014-2018 HS-GeoJSON Project+-- License      : BSD-style (see the file LICENSE.md)+-- Maintainer   : Andrew Newman+--+-- See section 2.1 "Geometry Objects" in the GeoJSON Spec.+--+-------------------------------------------------------------------+module Data.Geospatial.Internal.Geometry (+    -- * Types+        GeoPoint(..)+    ,   GeoMultiPoint(..), splitGeoMultiPoint, mergeGeoPoints+    ,   GeoPolygon(..)+    ,   GeoMultiPolygon(..), splitGeoMultiPolygon, mergeGeoPolygons+    ,   GeoLine(..)+    ,   GeoMultiLine(..), splitGeoMultiLine, mergeGeoLines+    ,   GeospatialGeometry(..)+    -- * Lenses+    ,   unGeoPoint+    ,   unGeoMultiPoint+    ,   unGeoPolygon+    ,   unGeoMultiPolygon+    ,   unGeoLine+    ,   unGeoMultiLine+    -- * Prisms+    ,   _NoGeometry+    ,   _Point+    ,   _MultiPoint+    ,   _Polygon+    ,   _MultiPolygon+    ,   _Line+    ,   _MultiLine+    ,   _Collection+    ) where++import           Data.Geospatial.Internal.Geometry.GeoLine+import           Data.Geospatial.Internal.Geometry.GeoMultiLine+import           Data.Geospatial.Internal.Geometry.GeoMultiPoint+import           Data.Geospatial.Internal.Geometry.GeoMultiPolygon+import           Data.Geospatial.Internal.Geometry.GeoPoint+import           Data.Geospatial.Internal.Geometry.GeoPolygon++import           Control.Applicative                               ((<$>))+import           Control.Lens                                      (makePrisms)+import           Control.Monad                                     (mzero)+import           Data.Aeson                                        (FromJSON (..),+                                                                    ToJSON (..),+                                                                    Value (..),+                                                                    object,+                                                                    (.:), (.=))+import           Data.Aeson.Types                                  (Parser)+import           Data.Text                                         (Text)++-- | See section 2.1 /Geometry Objects/ in the GeoJSON Spec.+data GeospatialGeometry =+        NoGeometry+    |   Point GeoPoint+    |   MultiPoint GeoMultiPoint+    |   Polygon GeoPolygon+    |   MultiPolygon GeoMultiPolygon+    |   Line GeoLine+    |   MultiLine GeoMultiLine+    |   Collection [GeospatialGeometry] deriving (Show, Eq)++makePrisms ''GeospatialGeometry++geometryFromAeson :: String -> Value -> Parser GeospatialGeometry+geometryFromAeson "Point" obj                           = Point <$> parseJSON obj+geometryFromAeson "MultiPoint" obj                      = MultiPoint <$> parseJSON obj+geometryFromAeson "Polygon" obj                         = Polygon <$> parseJSON obj+geometryFromAeson "MultiPolygon" obj                    = MultiPolygon <$> parseJSON obj+geometryFromAeson "LineString" obj                      = Line <$> parseJSON obj+geometryFromAeson "MultiLineString" obj                 = MultiLine <$> parseJSON obj+geometryFromAeson "GeometryCollection" (Object jsonObj) = Collection <$> (jsonObj .: ("geometries" :: Text))+geometryFromAeson "GeometryCollection" _                = mzero+geometryFromAeson _ _                                   = mzero+++-- |+-- encodes Geometry Objects to GeoJSON+--+instance ToJSON GeospatialGeometry where+--  toJSON :: a -> Value+    toJSON NoGeometry               = Null+    toJSON (Point point)            = toJSON point+    toJSON (MultiPoint points)      = toJSON points+    toJSON (Polygon vertices)       = toJSON vertices+    toJSON (MultiPolygon vertices)  = toJSON vertices+    toJSON (Line vertices)          = toJSON vertices+    toJSON (MultiLine vertices)     = toJSON vertices+    toJSON (Collection geometries)  = object+        [   "type" .= ("GeometryCollection" :: Text)+        ,   "geometries" .= geometries+        ]++-- |+-- decodes Geometry Objects from GeoJSON+--+-- Aeson doesnt decode "null" into `Null` unfortunately+--+instance FromJSON GeospatialGeometry where+--  parseJSON :: Value -> Parser a+    parseJSON Null = return NoGeometry+    parseJSON (Object obj) = do+        geometryType <- obj .: ("type" :: Text)+        geometryFromAeson geometryType (Object obj)+    parseJSON _ = mzero
+ src/Data/Geospatial/Internal/Geometry/Aeson.hs view
@@ -0,0 +1,59 @@+{-# LANGUAGE OverloadedStrings #-}+-------------------------------------------------------------------+-- |+-- Module       : Data.Geosptial.Geometry.Aeson+-- Copyright    : (C) 2014-2018 HS-GeoJSON Project+-- License      : BSD-style (see the file LICENSE.md)+-- Maintainer   : Andrew Newman+--+-- Some helpers for some of the common Aeson ops+-------------------------------------------------------------------+module Data.Geospatial.Internal.Geometry.Aeson (+    -- * Geometry+        readGeometryGeoAeson+    ,   makeGeometryGeoAeson+    -- * Optional fields+    ,   optValFromObj+    ,   optAttributes+    ) where++import           Control.Applicative ((<$>))+import           Control.Monad       (mzero)+import           Data.Aeson          (FromJSON (..), Object, ToJSON (..), Value,+                                      object, (.:), (.:?), (.=))+import           Data.Aeson.Types    (Pair, Parser)+import           Data.Maybe          (Maybe (..))+import           Data.Text           (Text)++-- | A generic function that can be used to read in the GeoJSON for:+-- `GeoPoint`, `GeoMultiPoint`, `GeoLine`, `GeoMultiLine`, `GeoPolygon` and `GeoMultiPolygon`+-- Takes in a String for the GeoJSON geometry type, the type constructor+-- for the datatype and the JSON object containing both the 'type' val and the 'coordinates' val+--+readGeometryGeoAeson :: (FromJSON a, FromJSON b) => String -> (a -> b) -> Object -> Parser b+readGeometryGeoAeson geomTypeString geomType geopointObj = do+    geometryType <- geopointObj .: "type"+    if geometryType == geomTypeString+        then+            geomType <$> geopointObj .: "coordinates"+        else+            mzero++-- | The inverse to the above, you just give it the type string and the value for the coordinates+-- and it will create the JSON object+--+makeGeometryGeoAeson :: (ToJSON a) => String -> a -> Value+makeGeometryGeoAeson typeString coordinates =+    object ["type" .= typeString, "coordinates" .= coordinates]++-- | get an optional value out of a JSON object:+--+optValFromObj :: (FromJSON a) => Text -> Object -> Parser (Maybe a)+optValFromObj = flip (.:?)++-- | The other way around, given an optional value, will return the attributes that+-- should be added to the makeObj input+--+optAttributes :: (ToJSON a) => Text -> Maybe a -> [Pair]+optAttributes _ Nothing     = []+optAttributes name (Just x) = [name .= x]
+ src/Data/Geospatial/Internal/Geometry/GeoLine.hs view
@@ -0,0 +1,40 @@+{-# LANGUAGE TemplateHaskell #-}+-------------------------------------------------------------------+-- |+-- Module       : Data.Geospatial.Internal.Geometry.GeoLine+-- Copyright    : (C) 2014-2018 HS-GeoJSON Project+-- License      : BSD-style (see the file LICENSE.md)+-- Maintainer   : Andrew Newman+--+-------------------------------------------------------------------+module Data.Geospatial.Internal.Geometry.GeoLine (+    -- * Type+        GeoLine(..)+    -- * Lenses+    ,   unGeoLine+    ) where++import           Data.Geospatial.Internal.BasicTypes+import           Data.Geospatial.Internal.Geometry.Aeson+import           Data.LineString++import           Control.Lens                            (makeLenses)+import           Control.Monad                           (mzero)+import           Data.Aeson                              (FromJSON (..),+                                                          ToJSON (..),+                                                          Value (..))++newtype GeoLine = GeoLine { _unGeoLine :: LineString GeoPositionWithoutCRS } deriving (Show, Eq)++makeLenses ''GeoLine++-- instances++instance ToJSON GeoLine where+--  toJSON :: a -> Value+    toJSON = makeGeometryGeoAeson "LineString" . _unGeoLine++instance FromJSON GeoLine where+--  parseJSON :: Value -> Parser a+    parseJSON (Object o) = readGeometryGeoAeson "LineString" GeoLine o+    parseJSON _          = mzero
+ src/Data/Geospatial/Internal/Geometry/GeoMultiLine.hs view
@@ -0,0 +1,52 @@+{-# LANGUAGE TemplateHaskell #-}+-------------------------------------------------------------------+-- |+-- Module       : Data.Geospatial.Internal.Geometry.GeoMultiLine+-- Copyright    : (C) 2014-2018 HS-GeoJSON Project+-- License      : BSD-style (see the file LICENSE.md)+-- Maintainer   : Andrew Newman+--+-------------------------------------------------------------------+module Data.Geospatial.Internal.Geometry.GeoMultiLine (+    -- * Type+        GeoMultiLine(..)+    -- * Lenses+    ,   unGeoMultiLine+    -- * To Points+    ,   splitGeoMultiLine, mergeGeoLines+    ) where++import           Data.Geospatial.Internal.BasicTypes+import           Data.Geospatial.Internal.Geometry.Aeson+import           Data.Geospatial.Internal.Geometry.GeoLine+import           Data.LineString++import           Control.Lens                              (makeLenses)+import           Control.Monad                             (mzero)+import           Data.Aeson                                (FromJSON (..),+                                                            ToJSON (..),+                                                            Value (..))++newtype GeoMultiLine    = GeoMultiLine { _unGeoMultiLine :: [LineString GeoPositionWithoutCRS] } deriving (Show, Eq)++makeLenses ''GeoMultiLine+++-- | Split GeoMultiLine coordinates into multiple GeoLines+splitGeoMultiLine:: GeoMultiLine -> [GeoLine]+splitGeoMultiLine = map GeoLine . _unGeoMultiLine++-- | Merge multiple GeoLines into one GeoMultiLine+mergeGeoLines :: [GeoLine] -> GeoMultiLine+mergeGeoLines = GeoMultiLine . map _unGeoLine++-- instances++instance ToJSON GeoMultiLine where+--  toJSON :: a -> Value+    toJSON = makeGeometryGeoAeson "MultiLineString" . _unGeoMultiLine++instance FromJSON GeoMultiLine where+--  parseJSON :: Value -> Parser a+    parseJSON (Object o)    = readGeometryGeoAeson "MultiLineString" GeoMultiLine o+    parseJSON _             = mzero
+ src/Data/Geospatial/Internal/Geometry/GeoMultiPoint.hs view
@@ -0,0 +1,51 @@+{-# LANGUAGE TemplateHaskell #-}+-------------------------------------------------------------------+-- |+-- Module       : Data.Geospatial.Internal.Geometry.GeoMultiPoint+-- Copyright    : (C) 2014-2018 HS-GeoJSON Project+-- License      : BSD-style (see the file LICENSE.md)+-- Maintainer   : Andrew Newman+--+-------------------------------------------------------------------+module Data.Geospatial.Internal.Geometry.GeoMultiPoint (+    -- * Type+        GeoMultiPoint(..)+    -- * Lenses+    ,   unGeoMultiPoint+    -- * To Points+    ,   splitGeoMultiPoint, mergeGeoPoints+    ) where++import           Data.Geospatial.Internal.BasicTypes+import           Data.Geospatial.Internal.Geometry.Aeson+import           Data.Geospatial.Internal.Geometry.GeoPoint++import           Control.Lens                               (makeLenses)+import           Control.Monad                              (mzero)+import           Data.Aeson                                 (FromJSON (..),+                                                             ToJSON (..),+                                                             Value (..))++newtype GeoMultiPoint = GeoMultiPoint { _unGeoMultiPoint :: [GeoPositionWithoutCRS] } deriving (Show, Eq)++makeLenses ''GeoMultiPoint+++-- | Split GeoMultiPoint coordinates into multiple GeoPoints+splitGeoMultiPoint:: GeoMultiPoint -> [GeoPoint]+splitGeoMultiPoint = map GeoPoint . _unGeoMultiPoint++-- | Merge multiple GeoPoints into one GeoMultiPoint+mergeGeoPoints :: [GeoPoint] -> GeoMultiPoint+mergeGeoPoints = GeoMultiPoint . map _unGeoPoint++-- instances++instance ToJSON GeoMultiPoint where+--  toJSON :: a -> Value+    toJSON = makeGeometryGeoAeson "MultiPoint" . _unGeoMultiPoint++instance FromJSON GeoMultiPoint where+--  parseJSON :: Value -> Parser a+    parseJSON (Object o) = readGeometryGeoAeson "MultiPoint" GeoMultiPoint o+    parseJSON _          = mzero
+ src/Data/Geospatial/Internal/Geometry/GeoMultiPolygon.hs view
@@ -0,0 +1,51 @@+{-# LANGUAGE TemplateHaskell #-}+-------------------------------------------------------------------+-- |+-- Module       : Data.Geospatial.Internal.Geometry.GeoMultiPolygon+-- Copyright    : (C) 2014-2018 HS-GeoJSON Project+-- License      : BSD-style (see the file LICENSE.md)+-- Maintainer   : Andrew Newman+--+-------------------------------------------------------------------+module Data.Geospatial.Internal.Geometry.GeoMultiPolygon (+    -- * Type+        GeoMultiPolygon(..)+    -- * Lenses+    ,   unGeoMultiPolygon+    -- * To Polygons+    ,   splitGeoMultiPolygon, mergeGeoPolygons+    ) where++import           Data.Geospatial.Internal.BasicTypes+import           Data.Geospatial.Internal.Geometry.Aeson+import           Data.Geospatial.Internal.Geometry.GeoPolygon+import           Data.LinearRing++import           Control.Lens                                 (makeLenses)+import           Control.Monad                                (mzero)+import           Data.Aeson                                   (FromJSON (..),+                                                               ToJSON (..),+                                                               Value (..))++newtype GeoMultiPolygon = GeoMultiPolygon { _unGeoMultiPolygon :: [[LinearRing GeoPositionWithoutCRS]] } deriving (Show, Eq)++-- | Split GeoMultiPolygon coordinates into multiple GeoPolygons+splitGeoMultiPolygon :: GeoMultiPolygon -> [GeoPolygon]+splitGeoMultiPolygon = map GeoPolygon . _unGeoMultiPolygon++-- | Merge multiple GeoPolygons into one GeoMultiPolygon+mergeGeoPolygons :: [GeoPolygon] -> GeoMultiPolygon+mergeGeoPolygons = GeoMultiPolygon . map _unGeoPolygon++makeLenses ''GeoMultiPolygon++-- instances++instance ToJSON GeoMultiPolygon where+--  toJSON :: a -> Value+    toJSON = makeGeometryGeoAeson "MultiPolygon" . _unGeoMultiPolygon++instance FromJSON GeoMultiPolygon where+--  parseJSON :: Value -> Parser a+    parseJSON (Object o)    = readGeometryGeoAeson "MultiPolygon" GeoMultiPolygon o+    parseJSON _             = mzero
+ src/Data/Geospatial/Internal/Geometry/GeoPoint.hs view
@@ -0,0 +1,39 @@+{-# LANGUAGE TemplateHaskell #-}+-------------------------------------------------------------------+-- |+-- Module       : Data.Geospatial.Internal.Geometry.GeoPoint+-- Copyright    : (C) 2014-2018 HS-GeoJSON Project+-- License      : BSD-style (see the file LICENSE.md)+-- Maintainer   : Andrew Newman+--+-------------------------------------------------------------------+module Data.Geospatial.Internal.Geometry.GeoPoint (+    -- * Type+        GeoPoint(..)+    -- * Lenses+    ,   unGeoPoint+    ) where++import           Data.Geospatial.Internal.BasicTypes+import           Data.Geospatial.Internal.Geometry.Aeson++import           Control.Lens                            (makeLenses)+import           Control.Monad                           (mzero)+import           Data.Aeson                              (FromJSON (..),+                                                          ToJSON (..),+                                                          Value (..))++newtype GeoPoint = GeoPoint { _unGeoPoint :: GeoPositionWithoutCRS } deriving (Show, Eq)++makeLenses ''GeoPoint++-- instances++instance ToJSON GeoPoint where+--  toJSON :: a -> Value+    toJSON = makeGeometryGeoAeson "Point" . _unGeoPoint++instance FromJSON GeoPoint where+--  parseJSON :: Value -> Parser a+    parseJSON (Object o) = readGeometryGeoAeson "Point" GeoPoint o+    parseJSON _          = mzero
+ src/Data/Geospatial/Internal/Geometry/GeoPolygon.hs view
@@ -0,0 +1,40 @@+{-# LANGUAGE TemplateHaskell #-}+-------------------------------------------------------------------+-- |+-- Module       : Data.Geospatial.Internal.Geometry.GeoPolygon+-- Copyright    : (C) 2014-2018 HS-GeoJSON Project+-- License      : BSD-style (see the file LICENSE.md)+-- Maintainer   : Andrew Newman+--+-------------------------------------------------------------------+module Data.Geospatial.Internal.Geometry.GeoPolygon (+    -- * Type+        GeoPolygon(..)+    -- * Lenses+    ,   unGeoPolygon+    ) where++import           Data.Geospatial.Internal.BasicTypes+import           Data.Geospatial.Internal.Geometry.Aeson+import           Data.LinearRing++import           Control.Lens                            (makeLenses)+import           Control.Monad                           (mzero)+import           Data.Aeson                              (FromJSON (..),+                                                          ToJSON (..),+                                                          Value (..))++newtype GeoPolygon = GeoPolygon { _unGeoPolygon :: [LinearRing GeoPositionWithoutCRS] } deriving (Show, Eq)++makeLenses ''GeoPolygon++-- instances++instance ToJSON GeoPolygon where+--  toJSON :: a -> Value+    toJSON = makeGeometryGeoAeson "Polygon" . _unGeoPolygon++instance FromJSON GeoPolygon where+--  parseJSON :: Value -> Parser a+    parseJSON (Object o) = readGeometryGeoAeson "Polygon" GeoPolygon o+    parseJSON _          = mzero
src/Data/LineString.hs view
@@ -2,9 +2,9 @@ ------------------------------------------------------------------- -- | -- Module       : Data.LineString--- Copyright    : (C) 2014 Dom De Re--- License      : BSD-style (see the file etc/LICENSE.md)--- Maintainer   : Dom De Re+-- Copyright    : (C) 2014-2018 HS-GeoJSON Project+-- License      : BSD-style (see the file LICENSE.md)+-- Maintainer   : Andrew Newman -- -- Refer to the GeoJSON Spec <http://geojson.org/geojson-spec.html#linestring> --@@ -24,30 +24,19 @@     ,   lineStringLength     ) where -import Prelude hiding ( foldr )--import Control.Applicative ( Applicative(..) )-import Control.Lens ( ( # ), (^?) )-import Control.Monad ( mzero )-import Data.Aeson ( ToJSON(..), FromJSON(..), Value )-import Data.Aeson.Types ( Parser, typeMismatch )-import Data.Foldable ( Foldable(..) )-import Data.Functor ( (<$>) )-import Data.Maybe ( fromMaybe )-import Data.Traversable ( Traversable(..) )-import Data.Validation ( Validate(..), Validation, _Failure, _Success )+import           Prelude             hiding (foldr) --- $setup------ >>> import Control.Applicative ( (<*>) )--- >>> import Data.Functor ( (<$>) )--- >>> import Data.Maybe ( Maybe(..) )--- >>> import Data.Monoid ( Monoid(..) )--- >>> import Test.QuickCheck------ >>> instance (Arbitrary a) => Arbitrary (LineString a) where arbitrary = makeLineString <$> arbitrary <*> arbitrary <*> arbitrary------+import           Control.Applicative (Applicative (..))+import           Control.Lens        (( # ), (^?))+import           Control.Monad       (mzero)+import           Data.Aeson          (FromJSON (..), ToJSON (..), Value)+import           Data.Aeson.Types    (Parser, typeMismatch)+import           Data.Foldable       (Foldable (..))+import           Data.Functor        ((<$>))+import           Data.Maybe          (fromMaybe)+import           Data.Traversable    (Traversable (..))+import           Data.Validation     (Validate (..), Validation, _Failure,+                                      _Success)  -- | -- a LineString has at least 2 elements@@ -63,6 +52,7 @@ data ListToLineStringError =         ListEmpty     |   SingletonList+    deriving (Eq)  -- functions @@ -78,25 +68,15 @@ lineStringLast :: LineString a -> a lineStringLast (LineString _ x xs) = fromMaybe x (safeLast xs) --- NOTE (Dom De Re): Props have been commented out until <https://github.com/sol/doctest-haskell/issues/83>--- has been resolved.- -- | -- returns the number of elements in the list, including the replicated element at the end of the list. ----- (\xs -> lineStringLength xs == (length (fromLineString xs))) (xs :: LineString Int)--- lineStringLength :: LineString a -> Int lineStringLength (LineString _ _ xs) = 2 + length xs  -- | -- This function converts it into a list and appends the given element to the end. ----- (\xs -> safeLast (fromLineString xs) == Just (lineStringHead xs)) (xs :: LineString Int)------ (\xs -> length (fromLineString xs) >= 4) (xs :: LineString Int)---- fromLineString :: LineString a -> [a] fromLineString (LineString x y zs) = x : y : zs @@ -104,21 +84,6 @@ -- creates a LineString out of a list of elements, -- if there are enough elements (needs at least 2) elements ----- >>> fromList [] :: Validation ListToLineStringError (LineString Int)--- Failure List Empty------ >>> fromList [0] :: Validation ListToLineStringError (LineString Int)--- Failure Singleton List------ >>> fromList [0, 1] :: Validation ListToLineStringError (LineString Int)--- Success [0,1]------ >>> fromList [0, 1, 2] :: Validation ListToLineStringError (LineString Int)--- Success [0,1,2]------ >>> fromList [0, 1, 2, 4, 5, 0] :: Validation ListToLineStringError (LineString Int)--- Success [0,1,2,4,5,0]--- fromList     :: (Validate v)     => [a]@@ -142,7 +107,7 @@ -- instances  instance Show ListToLineStringError where-    show ListEmpty = "List Empty"+    show ListEmpty     = "List Empty"     show SingletonList = "Singleton List"  instance (Show a) => Show (LineString a) where@@ -153,10 +118,6 @@  -- | This instance of Foldable will run through the entire ring, closing the -- loop by also passing the initial element in again at the end.------ > (\xs -> (foldr (:) [] xs) == (fromLineString xs)) (xs :: LineString Int)------ > (\xs -> (lineStringHead xs) == (foldr'' (\a -> const a) 0 xs)) (xs :: LineString Int) -- instance Foldable LineString where --  foldr :: (a -> b -> b) -> b -> LineString a -> b
src/Data/LinearRing.hs view
@@ -2,9 +2,9 @@ ------------------------------------------------------------------- -- | -- Module       : Data.LinearRing--- Copyright    : (C) 2014 Dom De Re--- License      : BSD-style (see the file etc/LICENSE.md)--- Maintainer   : Dom De Re+-- Copyright    : (C) 2014-2018 HS-GeoJSON Project+-- License      : BSD-style (see the file LICENSE.md)+-- Maintainer   : Andrew Newman -- -- Refer to the GeoJSON Spec <http://geojson.org/geojson-spec.html#polygon> --@@ -25,31 +25,20 @@     ,   ringLength     ) where -import Prelude hiding ( foldr )--import Control.Applicative ( Applicative(..) )-import Control.Lens ( ( # ), (^?) )-import Control.Monad ( mzero )-import Data.Aeson ( ToJSON(..), FromJSON(..), Value )-import Data.Aeson.Types ( Parser, typeMismatch )-import Data.Foldable ( Foldable(..) )-import Data.Functor ( (<$>) )-import Data.List ( intercalate )-import Data.List.NonEmpty as NL ( NonEmpty, toList )-import Data.Traversable ( Traversable(..) )-import Data.Validation ( Validate(..), AccValidation, _Failure, _Success )+import           Prelude             hiding (foldr) --- $setup------ >>> import Control.Applicative ( (<*>) )--- >>> import Data.Functor ( (<$>) )--- >>> import Data.Maybe ( Maybe(..) )--- >>> import Data.Monoid ( Monoid(..) )--- >>> import Test.QuickCheck------ >>> instance (Arbitrary a) => Arbitrary (LinearRing a) where arbitrary = makeLinearRing <$> arbitrary <*> arbitrary <*> arbitrary <*> arbitrary------+import           Control.Applicative (Applicative (..))+import           Control.Lens        (( # ), (^?))+import           Control.Monad       (mzero)+import           Data.Aeson          (FromJSON (..), ToJSON (..), Value)+import           Data.Aeson.Types    (Parser, typeMismatch)+import           Data.Foldable       (Foldable (..))+import           Data.Functor        ((<$>))+import           Data.List           (intercalate)+import           Data.List.NonEmpty  as NL (NonEmpty, toList)+import           Data.Traversable    (Traversable (..))+import           Data.Validation     (AccValidation, Validate (..), _Failure,+                                      _Success)  -- | -- a LinearRing has at least 3 (distinct) elements@@ -66,6 +55,7 @@ data ListToLinearRingError a =         ListTooShort Int     |   HeadNotEqualToLast a a+    deriving (Eq)  -- functions @@ -75,25 +65,15 @@ ringHead :: LinearRing a -> a ringHead (LinearRing x _ _ _)   = x --- NOTE (Dom De Re): Props have been commented out until <https://github.com/sol/doctest-haskell/issues/83>--- has been resolved.- -- | -- returns the number of elements in the list, including the replicated element at the end of the list. ----- prop> (\xs -> ringLength xs == (length (fromLinearRing xs))) (xs :: LinearRing Int)--- ringLength :: LinearRing a -> Int ringLength (LinearRing _ _ _ xs) = 4 + length xs  -- | -- This function converts it into a list and appends the given element to the end. ----- (\xs -> safeLast (fromLinearRing xs) == Just (ringHead xs)) (xs :: LinearRing Int)------ (\xs -> length (fromLinearRing xs) >= 4) (xs :: LinearRing Int)---- fromLinearRing :: LinearRing a -> [a] fromLinearRing (LinearRing x y z ws) = x : y : z : foldr (:) [x] ws @@ -112,32 +92,8 @@ -- -- And be aware that the last element of the list will be dropped. ----- >>> fromList [] :: AccValidation (NonEmpty (ListToLinearRingError Int)) (LinearRing Int)--- AccFailure (List too short: (length = 0) :| [])------ >>> fromList [0] :: AccValidation (NonEmpty (ListToLinearRingError Int)) (LinearRing Int)--- AccFailure (List too short: (length = 1) :| [])------ >>> fromList [0, 1] :: AccValidation (NonEmpty (ListToLinearRingError Int)) (LinearRing Int)--- AccFailure (List too short: (length = 2) :| [])------ >>> fromList [0, 1, 2] :: AccValidation (NonEmpty (ListToLinearRingError Int)) (LinearRing Int)--- AccFailure (List too short: (length = 3) :| [])------ >>> fromList [0, 1, 2, 3] :: AccValidation (NonEmpty (ListToLinearRingError Int)) (LinearRing Int)--- AccSuccess [0,1,2,0]------ >>> fromList [0, 1, 2, 4, 0] :: AccValidation (NonEmpty (ListToLinearRingError Int)) (LinearRing Int)--- AccSuccess [0,1,2,4,0]------ >>> fromList [0, 1, 2, 4, 5, 0] :: AccValidation (NonEmpty (ListToLinearRingError Int)) (LinearRing Int)--- AccSuccess [0,1,2,4,5,0]--- -- Unfortunately it doesn't check that the last element is the same as the first at the moment... ----- >>> fromList [0, 1, 2, 4, 5, 6] :: AccValidation (NonEmpty (ListToLinearRingError Int)) (LinearRing Int)--- AccSuccess [0,1,2,4,5,0]--- fromList     :: (Validate v, Functor (v (NonEmpty (ListToLinearRingError a))))     => [a]@@ -187,10 +143,6 @@ -- | This instance of Foldable will run through the entire ring, closing the -- loop by also passing the initial element in again at the end. ----- > (\xs -> (foldr (:) [] xs) == (fromLinearRing xs)) (xs :: LinearRing Int)------ > (\xs -> (ringHead xs) == (foldr'' (\a -> const a) 0 xs)) (xs :: LinearRing Int)--- instance Foldable LinearRing where --  foldr :: (a -> b -> b) -> b -> LinearRing a -> b     foldr f u (LinearRing x y z ws) = f x (f y (f z (foldr f (f x u) ws)))@@ -235,8 +187,8 @@         mhl xs = (,) <$> safeHead xs <*> safeLast xs  safeHead :: [a] -> Maybe a-safeHead []     = Nothing-safeHead (x:_)  = Just x+safeHead []    = Nothing+safeHead (x:_) = Just x  safeLast :: [a] -> Maybe a safeLast []     = Nothing@@ -245,8 +197,6 @@  -- | -- Does a fold but ignores the last element of the list------ > (\x xs -> length (foldrDropLast (:) [] (x : xs)) == length xs) (x :: Int) (xs :: [Int]) -- foldrDropLast :: (a -> b -> b) -> b -> [a] -> b foldrDropLast _ x []     = x
+ test/Arbitrary.hs view
@@ -0,0 +1,15 @@+{-# OPTIONS_GHC -fno-warn-orphans #-}++module Arbitrary where++import           Test.Tasty.QuickCheck (Arbitrary, arbitrary)+-- Local+import           Data.LinearRing       (LinearRing, makeLinearRing)+import           Data.LineString       (LineString, makeLineString)+++instance (Arbitrary a) => Arbitrary (LinearRing a) where+  arbitrary = makeLinearRing <$> arbitrary <*> arbitrary <*> arbitrary <*> arbitrary++instance (Arbitrary a) => Arbitrary (LineString a) where+  arbitrary = makeLineString <$> arbitrary <*> arbitrary <*> arbitrary
+ test/Data/Geospatial/Internal/CRSTests.hs view
@@ -0,0 +1,76 @@+{-# LANGUAGE OverloadedStrings #-}++module Data.Geospatial.Internal.CRSTests where++import qualified Data.Aeson                   as A+import qualified Data.ByteString.Lazy.Char8   as BS+import           Test.Tasty+import           Test.Tasty.Hspec             (Spec, context, describe, it,+                                               shouldBe, testSpec)+-- Local+import           Data.Geospatial.Internal.CRS+import           Fixture+++-- Tests++tests :: IO TestTree+tests = do+  specs <- specTests+  pure $ testGroup "Data.Geospatial.Internal.CRSTests" [specs]++specTests :: IO TestTree+specTests = do+  specs <- sequence+    [ testSpec "Data.Geospatial.Internal.CRS.fromJSON" testFromJSON+    , testSpec "Data.Geospatial.Internal.CRS.toJSON" testToJSON+    ]+  pure $ testGroup "Data.Geospatial.Internal.CRSTests.Spec" specs++-- Spec++-- >>> (A.decode . BS.pack) testLinkCRSJSON == Just testLinkCRS+-- True+--+-- >>> (A.decode . BS.pack) testNamedCRSJSON == Just testNamedCRS+-- True+--+-- >>> (A.decode . BS.pack) testEPSGJSON == Just testEPSG+-- True+--+-- (A.decode . BS.pack) "null" == Just NoCRS+-- True+--+testFromJSON :: Spec+testFromJSON =+  describe "fromJSON" $ do+    it "decode CRS Objects from GeoJSON" $ do+      A.decode testLinkCRSJSON  `shouldBe` Just testLinkCRS+      A.decode testNamedCRSJSON `shouldBe` Just testNamedCRS+      A.decode testEPSGJSON     `shouldBe` Just testEPSG+    context "when provided with invalid input" $+      it "fails" $+        (A.decode . BS.pack) "null" `shouldBe` Just NoCRS++-- >>> (A.decode . A.encode) testLinkCRS == Just testLinkCRS+-- True+--+-- >>> (A.decode . A.encode) testNamedCRS == Just testNamedCRS+-- True+--+-- >>> (A.decode . A.encode) testEPSG == Just testEPSG+-- True+--+-- >>> A.encode NoCRS+-- "null"+--+testToJSON :: Spec+testToJSON =+  describe "toJSON" $ do+    it "encode CRS Objects to GeoJSON" $ do+      (A.decode . A.encode) testLinkCRS  `shouldBe` Just testLinkCRS+      (A.decode . A.encode) testNamedCRS `shouldBe` Just testNamedCRS+      (A.decode . A.encode) testEPSG     `shouldBe` Just testEPSG+    context "when provided with invalid input" $+      it "fails" $+      A.encode NoCRS `shouldBe` "null"
+ test/Data/Geospatial/Internal/GeoFeatureCollectionTests.hs view
@@ -0,0 +1,68 @@+module Data.Geospatial.Internal.GeoFeatureCollectionTests where++import qualified Data.Aeson       as A+import           Test.Tasty+import           Test.Tasty.Hspec (Spec, describe, it, shouldBe, testSpec)+-- Local+import           Fixture+++-- Tests++tests :: IO TestTree+tests = do+  specs <- specTests+  pure $ testGroup " Data.Geospatial.Internal.GeoFeatureCollectionTests" [specs]++specTests :: IO TestTree+specTests = do+  specs <- sequence+    [ testSpec "Data.Geospatial.Internal.GeoFeatureCollection.fromJSON" testFromJSON+    , testSpec "Data.Geospatial.Internal.GeoFeatureCollection.toJSON" testToJSON+    ]+  pure $ testGroup "Data.Geospatial.Internal.GeoFeatureCollectionTests.Spec" specs+++-- Spec++-- >>> (A.decode . BS.pack) bigAssFeatureCollectionJSON == Just bigAssFeatureCollection+-- True+--+-- >>> (A.decode . BS.pack)  bigAssFeatureCollectionWithNoBBoxJSON == Just bigAssFeatureCollectionWithNoBBox+-- True+--+-- >>> (A.decode . BS.pack)  emptyFeatureCollectionWithBBoxJSON == Just emptyFeatureCollectionWithBBox+-- True+--+-- >>> (A.decode . BS.pack)  emptyFeatureCollectionJSON == Just emptyFeatureCollection+-- True+--+testFromJSON :: Spec+testFromJSON =+  describe "fromJSON" $+    it "decode FeatureCollection Objects from GeoJSON" $ do+      A.decode bigAssFeatureCollectionJSON           `shouldBe` Just bigAssFeatureCollection+      A.decode bigAssFeatureCollectionWithNoBBoxJSON `shouldBe` Just bigAssFeatureCollectionWithNoBBox+      A.decode emptyFeatureCollectionWithBBoxJSON    `shouldBe` Just emptyFeatureCollectionWithBBox+      A.decode emptyFeatureCollectionJSON            `shouldBe` Just emptyFeatureCollection++-- >>> (A.decode . A.encode) bigAssFeatureCollection == Just bigAssFeatureCollection+-- True+--+-- >>> (A.decode . A.encode) bigAssFeatureCollectionWithNoBBox == Just bigAssFeatureCollectionWithNoBBox+-- True+--+-- >>> (A.decode . A.encode) emptyFeatureCollectionWithBBox == Just emptyFeatureCollectionWithBBox+-- True+--+-- >>> (A.decode . A.encode) emptyFeatureCollection == Just emptyFeatureCollection+-- True+--+testToJSON :: Spec+testToJSON =+  describe "toJSON" $+    it "encode FeatureCollection Objects to GeoJSON" $ do+      (A.decode . A.encode) bigAssFeatureCollection           `shouldBe` Just bigAssFeatureCollection+      (A.decode . A.encode) bigAssFeatureCollectionWithNoBBox `shouldBe` Just bigAssFeatureCollectionWithNoBBox+      (A.decode . A.encode) emptyFeatureCollectionWithBBox    `shouldBe` Just emptyFeatureCollectionWithBBox+      (A.decode . A.encode) emptyFeatureCollection            `shouldBe` Just emptyFeatureCollection
+ test/Data/Geospatial/Internal/GeoFeatureTests.hs view
@@ -0,0 +1,75 @@+module Data.Geospatial.Internal.GeoFeatureTests where++import qualified Data.Aeson       as A+import           Test.Tasty+import           Test.Tasty.Hspec (Spec, describe, it, shouldBe, testSpec)+-- Local+import           Fixture+++-- Tests++tests :: IO TestTree+tests = do+  specs <- specTests+  pure $ testGroup "Data.Geospatial.Internal.GeoFeatureTests" [specs]++specTests :: IO TestTree+specTests = do+  specs <- sequence+    [ testSpec "Data.Geospatial.Internal.GeoFeature.fromJSON" testFromJSON+    , testSpec "Data.Geospatial.Internal.GeoFeature.toJSON" testToJSON+    ]+  pure $ testGroup "Data.Geospatial.Internal.GeoFeatureTests.Spec" specs++-- Spec++-- >>> (A.decode . BS.pack) bigFeatureJSON == Just bigFeature+-- True+--+-- >>> (A.decode . BS.pack) featureWithNoPropertiesJSON == Just featureWithNoProperties+-- True+--+-- >>> (A.decode . BS.pack) featureWithNoIdJSON == Just featureWithNoId+-- True+--+-- >>> (A.decode . BS.pack) featureWithNoBBoxJSON == Just featureWithNoBBox+-- True+--+-- >>> (A.decode . BS.pack) featureWithNoGeometryJSON == Just featureWithNoGeometry+-- True+--+testFromJSON :: Spec+testFromJSON =+  describe "fromJSON" $+    it "decode Feature Objects from GeoJSON" $ do+      A.decode bigFeatureJSON              `shouldBe` Just bigFeature+      A.decode featureWithNoPropertiesJSON `shouldBe` Just featureWithNoProperties+      A.decode featureWithNoIdJSON         `shouldBe` Just featureWithNoId+      A.decode featureWithNoBBoxJSON       `shouldBe` Just featureWithNoBBox+      A.decode featureWithNoGeometryJSON   `shouldBe` Just featureWithNoGeometry++-- >>> (A.decode . A.encode) bigFeature == Just bigFeature+-- True+--+-- >>> (A.decode . A.encode) featureWithNoProperties == Just featureWithNoProperties+-- True+--+-- >>> (A.decode . A.encode) featureWithNoId == Just featureWithNoId+-- True+--+-- >>> (A.decode . A.encode) featureWithNoBBox == Just featureWithNoBBox+-- True+--+-- >>> (A.decode . A.encode) featureWithNoGeometry == Just featureWithNoGeometry+-- True+--+testToJSON :: Spec+testToJSON =+  describe "toJSON" $+    it "encode Feature Objects to GeoJSON" $ do+      (A.decode . A.encode) bigFeature              `shouldBe` Just bigFeature+      (A.decode . A.encode) featureWithNoProperties `shouldBe` Just featureWithNoProperties+      (A.decode . A.encode) featureWithNoId         `shouldBe` Just featureWithNoId+      (A.decode . A.encode) featureWithNoBBox       `shouldBe` Just featureWithNoBBox+      (A.decode . A.encode) featureWithNoGeometry   `shouldBe` Just featureWithNoGeometry
+ test/Data/Geospatial/Internal/GeometryTests.hs view
@@ -0,0 +1,104 @@+{-# LANGUAGE OverloadedStrings #-}++module Data.Geospatial.Internal.GeometryTests where++import qualified Data.Aeson                        as A+import qualified Data.ByteString.Lazy.Char8        as BS+import           Test.Tasty+import           Test.Tasty.Hspec                  (Spec, describe, it,+                                                    shouldBe, testSpec)+-- Local+import           Data.Geospatial.Internal.Geometry+import           Fixture++-- Tests++tests :: IO TestTree+tests = do+  specs <- specTests+  pure $ testGroup "Data.Geospatial.Internal.GeometryTests" [specs]++specTests :: IO TestTree+specTests = do+  specs <- sequence+    [ testSpec "Data.Geospatial.Internal.Geometry.fromJSON" testFromJSON+    , testSpec "Data.Geospatial.Internal.Geometry.toJSON" testToJSON+    ]+  pure $ testGroup "Data.Geospatial.Internal.GeometryTests.Spec" specs++-- Spec++-- >>> decode' lShapedPolyJSON == Just lShapedPoly+-- True+--+-- >>> decode' emptyPolyJSON == Just emptyPoly+-- True+--+-- >>> decode' emptyMultiPolyJSON == Just emptyMultiPoly+-- True+--+-- >>> decode' singleLineMultiLineJSON == Just singleLineMultiLine+-- True+--+-- >>> decode' multiLineJSON == Just multiLine+-- True+--+-- >>> decode' emptyCollectionJSON == Just emptyCollection+-- True+--+-- >>> decode' bigassCollectionJSON == Just bigassCollection+-- True++--+-- decode' "null" :: Maybe GeospatialGeometry+-- Just NoGeometry+--+testFromJSON :: Spec+testFromJSON =+  describe "fromJSON" $+    it "decode Feature Objects from GeoJSON" $ do+      A.decode lShapedPolyJSON         `shouldBe` Just lShapedPoly+      A.decode emptyPolyJSON           `shouldBe` Just emptyPoly+      A.decode emptyMultiPolyJSON      `shouldBe` Just emptyMultiPoly+      A.decode singleLineMultiLineJSON `shouldBe` Just singleLineMultiLine+      A.decode multiLineJSON           `shouldBe` Just multiLine+      A.decode emptyCollectionJSON     `shouldBe` Just emptyCollection+      A.decode bigassCollectionJSON    `shouldBe` Just bigassCollection+      (A.decode . BS.pack) "null"      `shouldBe` Just NoGeometry++-- >>> A.encode NoGeometry+-- "null"+--+-- >>> (A.decode . A.encode) lShapedPoly == Just lShapedPoly+-- True+--+-- >>> (A.decode . A.encode) emptyPoly == Just emptyPoly+-- True+--+-- >>> (A.decode . A.encode) emptyMultiPoly == Just emptyMultiPoly+-- True+--+-- >>> (A.decode . A.encode) singleLineMultiLine == Just singleLineMultiLine+-- True+--+-- >>> (A.decode . A.encode) multiLine == Just multiLine+-- True+--+-- >>> (A.decode . A.encode) emptyCollection == Just emptyCollection+-- True+--+-- >>> (A.decode . A.encode) bigassCollection == Just bigassCollection+-- True+--+testToJSON :: Spec+testToJSON =+  describe "toJSON" $+    it "encode Feature Objects to GeoJSON" $ do+      A.encode NoGeometry                       `shouldBe` "null"+      (A.decode . A.encode) lShapedPoly         `shouldBe` Just lShapedPoly+      (A.decode . A.encode) emptyPoly           `shouldBe` Just emptyPoly+      (A.decode . A.encode) emptyMultiPoly      `shouldBe` Just emptyMultiPoly+      (A.decode . A.encode) singleLineMultiLine `shouldBe` Just singleLineMultiLine+      (A.decode . A.encode) multiLine           `shouldBe` Just multiLine+      (A.decode . A.encode) emptyCollection     `shouldBe` Just emptyCollection+      (A.decode . A.encode) bigassCollection    `shouldBe` Just bigassCollection
+ test/Data/LineStringTests.hs view
@@ -0,0 +1,85 @@+module Data.LineStringTests where++import           Data.Foldable         (Foldable (..))+import           Data.Validation       (AccValidation (..))+import           Test.Tasty+import           Test.Tasty.Hspec      (Spec, context, describe, it, shouldBe,+                                        testSpec)+import           Test.Tasty.QuickCheck (Property, property, testProperty)+-- Local+import           Arbitrary             ()+import           Data.LineString+++-- Tests++tests :: IO TestTree+tests = do+  specs <- specTests+  pure $ testGroup "Data.LineStringTests" [qcTests, specs]++qcTests :: TestTree+qcTests = testGroup "Data.LineStringTests.QuickCheck"+  [ testProperty "Data.LineString.lineStringLength" testLineStringLength+  , testProperty "Data.LineString.fromLineString" testFromLineString+  , testProperty "Data.LineString.Foldable" testFoldable+  ]++specTests :: IO TestTree+specTests = do+  specs <- sequence+    [ testSpec "Data.LineString.fromList" testFromList+    ]+  pure $ testGroup "Data.LineStringTests.Spec" specs++-- QuickCheck++-- (\xs -> lineStringLength xs == (length (fromLineString xs))) (xs :: LineString Int)+--+testLineStringLength :: LineString Int -> Property+testLineStringLength xs = property $ lineStringLength xs == length (fromLineString xs)++-- (\xs -> length (fromLineString xs) >= 4) (xs :: LineString Int)+--+testFromLineString :: LineString Int -> Property+testFromLineString xs = property $ length (fromLineString xs) >= 2++-- > (\xs -> (foldr (:) [] xs) == (fromLineString xs)) (xs :: LineString Int)+--+-- > (\xs -> (lineStringHead xs) == (foldr'' (\a -> const a) 0 xs)) (xs :: LineString Int)+--+testFoldable :: LineString Int -> Property+testFoldable xs = property $ (foldr (:) [] xs == fromLineString xs) && (lineStringHead xs == foldr' const 0 xs)++-- Spec++-- >>> fromList [] :: Validation ListToLineStringError (LineString Int)+-- Failure List Empty+--+-- >>> fromList [0] :: Validation ListToLineStringError (LineString Int)+-- Failure Singleton List+--+-- >>> fromList [0, 1] :: Validation ListToLineStringError (LineString Int)+-- Success [0,1]+--+-- >>> fromList [0, 1, 2] :: Validation ListToLineStringError (LineString Int)+-- Success [0,1,2]+--+-- >>> fromList [0, 1, 2, 4, 5, 0] :: Validation ListToLineStringError (LineString Int)+-- Success [0,1,2,4,5,0]+--+testFromList :: Spec+testFromList =+  describe "fromList" $ do+    it "creates a LineString out of a list of elements" $ do+      fromList ([0, 1] :: [Int])             `shouldBe` AccSuccess (makeLineString 0 1 [])+      fromList ([0, 1, 2] :: [Int])          `shouldBe` AccSuccess (makeLineString 0 1 [2])+      fromList ([0, 1, 2, 4, 5, 0] :: [Int]) `shouldBe` AccSuccess (makeLineString 0 1 [2, 4, 5, 0])+    context "when provided with invalid input" $+      it "fails" $ do+        fromList ([] :: [Int])  `shouldBe` AccFailure ListEmpty+        fromList ([0] :: [Int]) `shouldBe` AccFailure SingletonList++-- TODO+-- (\xs -> safeLast (fromLineString xs) == Just (lineStringHead xs)) (xs :: LineString Int)+--
+ test/Data/LinearRingTests.hs view
@@ -0,0 +1,100 @@+module Data.LinearRingTests where++import           Data.Foldable         (Foldable (..))+import           Data.List.NonEmpty    (NonEmpty (..))+import           Data.Validation       (AccValidation (..))+import           Test.Tasty+import           Test.Tasty.Hspec      (Spec, context, describe, it, shouldBe,+                                        testSpec)+import           Test.Tasty.QuickCheck (Property, property, testProperty)+-- Local+import           Arbitrary             ()+import           Data.LinearRing+++-- Tests++tests :: IO TestTree+tests = do+  specs <- specTests+  pure $ testGroup "Data.LinearRingTests" [qcTests, specs]++qcTests :: TestTree+qcTests = testGroup "Data.LinearRingTests.QuickCheck"+  [ testProperty "Data.LinearRing.ringLength" testRingLength+  , testProperty "Data.LinearRing.fromLinearRing" testFromLinearRing+  , testProperty "Data.LinearRing.Foldable" testFoldable+  ]++specTests :: IO TestTree+specTests = do+  specs <- sequence+    [ testSpec "Data.LinearRing.fromList" testFromList+    ]+  pure $ testGroup "Data.LinearRingTests.Spec" specs++-- QuickCheck++-- > (\xs -> ringLength xs == (length (fromLinearRing xs))) (xs :: LinearRing Int)+--+testRingLength :: LinearRing Int -> Property+testRingLength xs = property $ ringLength xs == length (fromLinearRing xs)++-- > (\xs -> length (fromLinearRing xs) >= 4) (xs :: LinearRing Int)+--+testFromLinearRing :: LinearRing Int -> Property+testFromLinearRing xs = property $ length (fromLinearRing xs) >= 4++-- > (\xs -> (foldr (:) [] xs) == (fromLinearRing xs)) (xs :: LinearRing Int)+--+-- > (\xs -> (ringHead xs) == (foldr'' (\a -> const a) 0 xs)) (xs :: LinearRing Int)+--+testFoldable :: LinearRing Int -> Property+testFoldable xs = property $ (foldr (:) [] xs == fromLinearRing xs) && (ringHead xs == foldr' const 0 xs)++-- Spec++-- >>> fromList [] :: AccValidation (NonEmpty (ListToLinearRingError Int)) (LinearRing Int)+-- AccFailure (List too short: (length = 0) :| [])+--+-- >>> fromList [0] :: AccValidation (NonEmpty (ListToLinearRingError Int)) (LinearRing Int)+-- AccFailure (List too short: (length = 1) :| [])+--+-- >>> fromList [0, 1] :: AccValidation (NonEmpty (ListToLinearRingError Int)) (LinearRing Int)+-- AccFailure (List too short: (length = 2) :| [])+--+-- >>> fromList [0, 1, 2] :: AccValidation (NonEmpty (ListToLinearRingError Int)) (LinearRing Int)+-- AccFailure (List too short: (length = 3) :| [])+--+-- >>> fromList [0, 1, 2, 3] :: AccValidation (NonEmpty (ListToLinearRingError Int)) (LinearRing Int)+-- AccSuccess [0,1,2,0]+--+-- >>> fromList [0, 1, 2, 4, 0] :: AccValidation (NonEmpty (ListToLinearRingError Int)) (LinearRing Int)+-- AccSuccess [0,1,2,4,0]+--+-- >>> fromList [0, 1, 2, 4, 5, 0] :: AccValidation (NonEmpty (ListToLinearRingError Int)) (LinearRing Int)+-- AccSuccess [0,1,2,4,5,0]++-- >>> fromList [0, 1, 2, 4, 5, 6] :: AccValidation (NonEmpty (ListToLinearRingError Int)) (LinearRing Int)+-- AccSuccess [0,1,2,4,5,0]+--+testFromList :: Spec+testFromList =+  describe "fromList" $ do+    it "creates a LinearRing out of a list of elements" $ do+      fromList ([0, 1, 2, 3] :: [Int])       `shouldBe` AccSuccess (makeLinearRing 0 1 2 [])+      fromList ([0, 1, 2, 4, 0] :: [Int])    `shouldBe` AccSuccess (makeLinearRing 0 1 2 [4])+      fromList ([0, 1, 2, 4, 5, 0] :: [Int]) `shouldBe` AccSuccess (makeLinearRing 0 1 2 [4, 5])+      fromList ([0, 1, 2, 4, 5, 6] :: [Int]) `shouldBe` AccSuccess (makeLinearRing 0 1 2 [4, 5])+    context "when provided with invalid input" $+      it "fails" $ do+        fromList []        `shouldBe` AccFailure (ListTooShort 0 :| [] :: NonEmpty (ListToLinearRingError Int))+        fromList [0]       `shouldBe` AccFailure (ListTooShort 1 :| [] :: NonEmpty (ListToLinearRingError Int))+        fromList [0, 1]    `shouldBe` AccFailure (ListTooShort 2 :| [] :: NonEmpty (ListToLinearRingError Int))+        fromList [0, 1, 2] `shouldBe` AccFailure (ListTooShort 3 :| [] :: NonEmpty (ListToLinearRingError Int))++-- TODO+-- > (\xs -> safeLast (fromLinearRing xs) == Just (ringHead xs)) (xs :: LinearRing Int)+--+-- > (\x xs -> length (foldrDropLast (:) [] (x : xs)) == length xs) (x :: Int) (xs :: [Int])+--
+ test/Fixture.hs view
@@ -0,0 +1,279 @@+{-# LANGUAGE OverloadedStrings #-}++module Fixture where++import qualified Data.Aeson                                        as A+import qualified Data.ByteString.Lazy.Char8                        as BS+import qualified Data.Text                                         as T+-- Local+import           Data.Geospatial.Internal.BasicTypes               (BoundingBoxWithoutCRS,+                                                                    FeatureID (..),+                                                                    GeoPositionWithoutCRS)+import           Data.Geospatial.Internal.CRS                      (CRSObject (..))+import           Data.Geospatial.Internal.GeoFeature               (GeoFeature (..))+import           Data.Geospatial.Internal.GeoFeatureCollection     (GeoFeatureCollection (..))+import           Data.Geospatial.Internal.Geometry                 (GeospatialGeometry (..))+import           Data.Geospatial.Internal.Geometry.GeoLine         (GeoLine (..))+import           Data.Geospatial.Internal.Geometry.GeoMultiLine    (GeoMultiLine (..),+                                                                    mergeGeoLines)+import           Data.Geospatial.Internal.Geometry.GeoMultiPolygon (GeoMultiPolygon (..),+                                                                    mergeGeoPolygons)+import           Data.Geospatial.Internal.Geometry.GeoPolygon      (GeoPolygon (..))+import           Data.LinearRing                                   (LinearRing, makeLinearRing)+import           Data.LineString                                   (LineString, makeLineString)+++-- CRS Data++testLinkCRSJSON :: BS.ByteString+testLinkCRSJSON = "{\"type\":\"link\",\"properties\":{\"href\":\"www.google.com.au\",\"type\":\"proj4\"}}"++testLinkCRS :: CRSObject+testLinkCRS = LinkedCRS "www.google.com.au" "proj4"++testEPSGJSON :: BS.ByteString+testEPSGJSON = "{\"type\":\"epsg\",\"properties\":{\"code\":4326}}"++testEPSG :: CRSObject+testEPSG = EPSG 4326++testNamedCRSJSON :: BS.ByteString+testNamedCRSJSON = "{\"type\":\"name\",\"properties\":{\"name\":\"urn:ogc:def:crs:OGC:1.3:CRS84\"}}"++testNamedCRS :: CRSObject+testNamedCRS = NamedCRS "urn:ogc:def:crs:OGC:1.3:CRS84"++-- Bounding Box Data++lshapedPolyVertices :: [LinearRing GeoPositionWithoutCRS]+lshapedPolyVertices = [ makeLinearRing [120.0, -15.0] [127.0, -15.0] [127.0, -25.0] [[124.0, -25.0], [124.0, -18.0], [120.0, -18.0]] ]++lshapedPolyLineVertices :: LineString GeoPositionWithoutCRS+lshapedPolyLineVertices = makeLineString [120.0, -15.0] [127.0, -15.0] [[127.0, -25.0], [124.0, -25.0], [124.0, -18.0], [120.0, -18.0]]+++emptyVertices :: [LinearRing GeoPositionWithoutCRS]+emptyVertices = []++emptyLineVertices :: [GeoPositionWithoutCRS]+emptyLineVertices = []+++testLatLonBBox :: BoundingBoxWithoutCRS+testLatLonBBox = [-32, 147.5, -29.5, 151.0]++testLatLonBBoxJSON :: BS.ByteString+testLatLonBBoxJSON = "[-32,147.5,-29.5,151]"+++testEmptyBBox :: BoundingBoxWithoutCRS+testEmptyBBox = []++testEmptyBBoxJSON :: BS.ByteString+testEmptyBBoxJSON = "[]"++-- Geometry Data+--+-- Upside down L Shaped Poly+--+-- (120, -15)                (127, -15)+-- *---------------------------*+-- |                           |+-- |                           |+-- |             (124, -18)    |+-- *---------------*           |+-- (120, -18)      |           |+--                 |           |+--                 |           |+--                 |           |+--                 |           |+--                 |           |+--                 |           |+--                 *-----------*+--               (124, -25)  (127, -25)+--++-- Polys+lShapedPolyJSON :: BS.ByteString+lShapedPolyJSON = "{\"coordinates\":[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18],[120,-15]]],\"type\":\"Polygon\"}"++lShapedGeoPoly :: GeoPolygon+lShapedGeoPoly = GeoPolygon lshapedPolyVertices++lShapedPoly :: GeospatialGeometry+lShapedPoly = Polygon lShapedGeoPoly+++emptyPolyJSON :: BS.ByteString+emptyPolyJSON = "{\"type\":\"Polygon\",\"coordinates\":[]}"++emptyGeoPoly :: GeoPolygon+emptyGeoPoly = GeoPolygon emptyVertices++emptyPoly :: GeospatialGeometry+emptyPoly = Polygon emptyGeoPoly++-- Multi Polys+emptyMultiPolyJSON :: BS.ByteString+emptyMultiPolyJSON = "{\"type\":\"MultiPolygon\",\"coordinates\":[]}"++emptyMultiGeoPoly :: GeoMultiPolygon+emptyMultiGeoPoly = GeoMultiPolygon []++emptyMultiPoly :: GeospatialGeometry+emptyMultiPoly = MultiPolygon emptyMultiGeoPoly+++singlePolyMultiPolyJSON :: BS.ByteString+singlePolyMultiPolyJSON = "{\"type\":\"MultiPolygon\",\"coordinates\":[[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]]]]}"++singlePolyGeoMultiPoly :: GeoMultiPolygon+singlePolyGeoMultiPoly = mergeGeoPolygons [lShapedGeoPoly]++singlePolyMultiPoly :: GeospatialGeometry+singlePolyMultiPoly = MultiPolygon singlePolyGeoMultiPoly+++multiPolyJSON :: BS.ByteString+multiPolyJSON = "{\"type\":\"MultiPolygon\",\"coordinates\":[[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]]],[]]}"++geoMultiPoly :: GeoMultiPolygon+geoMultiPoly = mergeGeoPolygons [lShapedGeoPoly, emptyGeoPoly]++multiPoly :: GeospatialGeometry+multiPoly = MultiPolygon geoMultiPoly++-- Lines+lShapedLineJSON :: BS.ByteString+lShapedLineJSON = "{\"type\":\"LineString\",\"coordinates\":[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]]}"++lShapedGeoLine :: GeoLine+lShapedGeoLine = GeoLine lshapedPolyLineVertices++lShapedLine :: GeospatialGeometry+lShapedLine = Line lShapedGeoLine++-- Multi Lines+emptyMultiLineJSON :: BS.ByteString+emptyMultiLineJSON = "{\"type\":\"MultiLineString\",\"coordinates\":[]}"++emptyMultiGeoLine :: GeoMultiLine+emptyMultiGeoLine = GeoMultiLine []++emptyMultiLine :: GeospatialGeometry+emptyMultiLine = MultiLine emptyMultiGeoLine+++singleLineMultiLineJSON :: BS.ByteString+singleLineMultiLineJSON = "{\"type\":\"MultiLineString\",\"coordinates\":[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]]]}"++singleLineGeoMultiLine :: GeoMultiLine+singleLineGeoMultiLine = mergeGeoLines [lShapedGeoLine]++singleLineMultiLine :: GeospatialGeometry+singleLineMultiLine = MultiLine singleLineGeoMultiLine+++multiLineJSON :: BS.ByteString+multiLineJSON = "{\"coordinates\":[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]],[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]]],\"type\":\"MultiLineString\"}"++geoMultiLine :: GeoMultiLine+geoMultiLine = mergeGeoLines [lShapedGeoLine, lShapedGeoLine]++multiLine :: GeospatialGeometry+multiLine = MultiLine geoMultiLine++-- Collection+emptyCollectionJSON :: BS.ByteString+emptyCollectionJSON = "{\"type\":\"GeometryCollection\",\"geometries\":[]}"++emptyCollection :: GeospatialGeometry+emptyCollection = Collection []+++bigassCollectionJSON :: BS.ByteString+bigassCollectionJSON = "{\"geometries\":[{\"coordinates\":[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]]],\"type\":\"MultiLineString\"},{\"coordinates\":[],\"type\":\"MultiLineString\"},{\"coordinates\":[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]],[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]]],\"type\":\"MultiLineString\"},{\"coordinates\":[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]],\"type\":\"LineString\"},{\"coordinates\":[[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18],[120,-15]]],[]],\"type\":\"MultiPolygon\"},{\"coordinates\":[[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18],[120,-15]]]],\"type\":\"MultiPolygon\"},{\"coordinates\":[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18],[120,-15]]],\"type\":\"Polygon\"},{\"coordinates\":[],\"type\":\"MultiPolygon\"},{\"coordinates\":[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18],[120,-15]]],\"type\":\"Polygon\"}],\"type\":\"GeometryCollection\"}"++bigassCollection :: GeospatialGeometry+bigassCollection = Collection [singleLineMultiLine, emptyMultiLine, multiLine, lShapedLine, multiPoly, singlePolyMultiPoly, lShapedPoly, emptyMultiPoly, lShapedPoly]++-- Properties Data++testProperties :: A.Value+testProperties = A.object [T.pack "depth" A..= (5 :: Int), T.pack "comment" A..= T.pack "Bore run over by dump truck"]++-- Feature Data++featureId :: Maybe FeatureID+featureId = Just $ FeatureIDText "GW001"++bbox :: Maybe BoundingBoxWithoutCRS+bbox = Just testLatLonBBox+++bigFeatureJSON :: BS.ByteString+bigFeatureJSON = "{\"bbox\":[-32,147.5,-29.5,151],\"geometry\":{\"geometries\":[{\"coordinates\":[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]]],\"type\":\"MultiLineString\"},{\"coordinates\":[],\"type\":\"MultiLineString\"},{\"coordinates\":[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]],[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]]],\"type\":\"MultiLineString\"},{\"coordinates\":[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]],\"type\":\"LineString\"},{\"coordinates\":[[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18],[120,-15]]],[]],\"type\":\"MultiPolygon\"},{\"coordinates\":[[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18],[120,-15]]]],\"type\":\"MultiPolygon\"},{\"coordinates\":[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18],[120,-15]]],\"type\":\"Polygon\"},{\"coordinates\":[],\"type\":\"MultiPolygon\"},{\"coordinates\":[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18],[120,-15]]],\"type\":\"Polygon\"}],\"type\":\"GeometryCollection\"},\"id\":\"GW001\",\"type\":\"Feature\",\"properties\":{\"depth\":5,\"comment\":\"Bore run over by dump truck\"}}"++bigFeature :: GeoFeature A.Value+bigFeature = GeoFeature bbox bigassCollection testProperties featureId+++featureWithNoPropertiesJSON :: BS.ByteString+featureWithNoPropertiesJSON = "{\"bbox\":[-32,147.5,-29.5,151],\"geometry\":{\"geometries\":[{\"coordinates\":[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]]],\"type\":\"MultiLineString\"},{\"coordinates\":[],\"type\":\"MultiLineString\"},{\"coordinates\":[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]],[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]]],\"type\":\"MultiLineString\"},{\"coordinates\":[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]],\"type\":\"LineString\"},{\"coordinates\":[[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18],[120,-15]]],[]],\"type\":\"MultiPolygon\"},{\"coordinates\":[[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18],[120,-15]]]],\"type\":\"MultiPolygon\"},{\"coordinates\":[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18],[120,-15]]],\"type\":\"Polygon\"},{\"coordinates\":[],\"type\":\"MultiPolygon\"},{\"coordinates\":[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18],[120,-15]]],\"type\":\"Polygon\"}],\"type\":\"GeometryCollection\"},\"id\":\"GW001\",\"type\":\"Feature\",\"properties\":null}"++featureWithNoProperties :: GeoFeature A.Value+featureWithNoProperties = GeoFeature bbox bigassCollection A.Null featureId+++featureWithNoGeometryJSON :: BS.ByteString+featureWithNoGeometryJSON = "{\"type\":\"Feature\",\"properties\":{\"depth\":5,\"comment\":\"Bore run over by dump truck\"},\"geometry\":null,\"bbox\":[-32,147.5,-29.5,151],\"id\":\"GW001\"}"++featureWithNoGeometry :: GeoFeature A.Value+featureWithNoGeometry = GeoFeature bbox NoGeometry testProperties featureId+++featureWithNoIdJSON :: BS.ByteString+featureWithNoIdJSON = "{\"bbox\":[-32,147.5,-29.5,151],\"geometry\":{\"geometries\":[{\"coordinates\":[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]]],\"type\":\"MultiLineString\"},{\"coordinates\":[],\"type\":\"MultiLineString\"},{\"coordinates\":[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]],[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]]],\"type\":\"MultiLineString\"},{\"coordinates\":[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]],\"type\":\"LineString\"},{\"coordinates\":[[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18],[120,-15]]],[]],\"type\":\"MultiPolygon\"},{\"coordinates\":[[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18],[120,-15]]]],\"type\":\"MultiPolygon\"},{\"coordinates\":[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18],[120,-15]]],\"type\":\"Polygon\"},{\"coordinates\":[],\"type\":\"MultiPolygon\"},{\"coordinates\":[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18],[120,-15]]],\"type\":\"Polygon\"}],\"type\":\"GeometryCollection\"},\"type\":\"Feature\",\"properties\":{\"depth\":5,\"comment\":\"Bore run over by dump truck\"}}"++featureWithNoId :: GeoFeature A.Value+featureWithNoId = GeoFeature bbox bigassCollection testProperties Nothing+++featureWithNoBBoxJSON :: BS.ByteString+featureWithNoBBoxJSON = "{\"geometry\":{\"geometries\":[{\"coordinates\":[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]]],\"type\":\"MultiLineString\"},{\"coordinates\":[],\"type\":\"MultiLineString\"},{\"coordinates\":[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]],[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]]],\"type\":\"MultiLineString\"},{\"coordinates\":[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]],\"type\":\"LineString\"},{\"coordinates\":[[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18],[120,-15]]],[]],\"type\":\"MultiPolygon\"},{\"coordinates\":[[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18],[120,-15]]]],\"type\":\"MultiPolygon\"},{\"coordinates\":[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18],[120,-15]]],\"type\":\"Polygon\"},{\"coordinates\":[],\"type\":\"MultiPolygon\"},{\"coordinates\":[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18],[120,-15]]],\"type\":\"Polygon\"}],\"type\":\"GeometryCollection\"},\"id\":\"GW001\",\"type\":\"Feature\",\"properties\":{\"depth\":5,\"comment\":\"Bore run over by dump truck\"}}"++featureWithNoBBox :: GeoFeature A.Value+featureWithNoBBox = GeoFeature Nothing bigassCollection testProperties featureId++-- FeatureCollection Data++features :: [GeoFeature A.Value]+features = [featureWithNoBBox, featureWithNoGeometry, featureWithNoBBox, featureWithNoId, featureWithNoProperties, bigFeature]+++bigAssFeatureCollectionJSON :: BS.ByteString+bigAssFeatureCollectionJSON = "{\"bbox\":[-32,147.5,-29.5,151],\"features\":[{\"geometry\":{\"geometries\":[{\"coordinates\":[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]]],\"type\":\"MultiLineString\"},{\"coordinates\":[],\"type\":\"MultiLineString\"},{\"coordinates\":[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]],[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]]],\"type\":\"MultiLineString\"},{\"coordinates\":[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]],\"type\":\"LineString\"},{\"coordinates\":[[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18],[120,-15]]],[]],\"type\":\"MultiPolygon\"},{\"coordinates\":[[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18],[120,-15]]]],\"type\":\"MultiPolygon\"},{\"coordinates\":[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18],[120,-15]]],\"type\":\"Polygon\"},{\"coordinates\":[],\"type\":\"MultiPolygon\"},{\"coordinates\":[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18],[120,-15]]],\"type\":\"Polygon\"}],\"type\":\"GeometryCollection\"},\"id\":\"GW001\",\"type\":\"Feature\",\"properties\":{\"depth\":5,\"comment\":\"Bore run over by dump truck\"}},{\"bbox\":[-32,147.5,-29.5,151],\"geometry\":null,\"id\":\"GW001\",\"type\":\"Feature\",\"properties\":{\"depth\":5,\"comment\":\"Bore run over by dump truck\"}},{\"geometry\":{\"geometries\":[{\"coordinates\":[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]]],\"type\":\"MultiLineString\"},{\"coordinates\":[],\"type\":\"MultiLineString\"},{\"coordinates\":[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]],[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]]],\"type\":\"MultiLineString\"},{\"coordinates\":[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]],\"type\":\"LineString\"},{\"coordinates\":[[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18],[120,-15]]],[]],\"type\":\"MultiPolygon\"},{\"coordinates\":[[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18],[120,-15]]]],\"type\":\"MultiPolygon\"},{\"coordinates\":[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18],[120,-15]]],\"type\":\"Polygon\"},{\"coordinates\":[],\"type\":\"MultiPolygon\"},{\"coordinates\":[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18],[120,-15]]],\"type\":\"Polygon\"}],\"type\":\"GeometryCollection\"},\"id\":\"GW001\",\"type\":\"Feature\",\"properties\":{\"depth\":5,\"comment\":\"Bore run over by dump truck\"}},{\"bbox\":[-32,147.5,-29.5,151],\"geometry\":{\"geometries\":[{\"coordinates\":[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]]],\"type\":\"MultiLineString\"},{\"coordinates\":[],\"type\":\"MultiLineString\"},{\"coordinates\":[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]],[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]]],\"type\":\"MultiLineString\"},{\"coordinates\":[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]],\"type\":\"LineString\"},{\"coordinates\":[[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18],[120,-15]]],[]],\"type\":\"MultiPolygon\"},{\"coordinates\":[[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18],[120,-15]]]],\"type\":\"MultiPolygon\"},{\"coordinates\":[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18],[120,-15]]],\"type\":\"Polygon\"},{\"coordinates\":[],\"type\":\"MultiPolygon\"},{\"coordinates\":[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18],[120,-15]]],\"type\":\"Polygon\"}],\"type\":\"GeometryCollection\"},\"type\":\"Feature\",\"properties\":{\"depth\":5,\"comment\":\"Bore run over by dump truck\"}},{\"bbox\":[-32,147.5,-29.5,151],\"geometry\":{\"geometries\":[{\"coordinates\":[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]]],\"type\":\"MultiLineString\"},{\"coordinates\":[],\"type\":\"MultiLineString\"},{\"coordinates\":[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]],[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]]],\"type\":\"MultiLineString\"},{\"coordinates\":[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]],\"type\":\"LineString\"},{\"coordinates\":[[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18],[120,-15]]],[]],\"type\":\"MultiPolygon\"},{\"coordinates\":[[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18],[120,-15]]]],\"type\":\"MultiPolygon\"},{\"coordinates\":[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18],[120,-15]]],\"type\":\"Polygon\"},{\"coordinates\":[],\"type\":\"MultiPolygon\"},{\"coordinates\":[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18],[120,-15]]],\"type\":\"Polygon\"}],\"type\":\"GeometryCollection\"},\"id\":\"GW001\",\"type\":\"Feature\",\"properties\":null},{\"bbox\":[-32,147.5,-29.5,151],\"geometry\":{\"geometries\":[{\"coordinates\":[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]]],\"type\":\"MultiLineString\"},{\"coordinates\":[],\"type\":\"MultiLineString\"},{\"coordinates\":[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]],[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]]],\"type\":\"MultiLineString\"},{\"coordinates\":[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]],\"type\":\"LineString\"},{\"coordinates\":[[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18],[120,-15]]],[]],\"type\":\"MultiPolygon\"},{\"coordinates\":[[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18],[120,-15]]]],\"type\":\"MultiPolygon\"},{\"coordinates\":[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18],[120,-15]]],\"type\":\"Polygon\"},{\"coordinates\":[],\"type\":\"MultiPolygon\"},{\"coordinates\":[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18],[120,-15]]],\"type\":\"Polygon\"}],\"type\":\"GeometryCollection\"},\"id\":\"GW001\",\"type\":\"Feature\",\"properties\":{\"depth\":5,\"comment\":\"Bore run over by dump truck\"}}],\"type\":\"FeatureCollection\"}"++bigAssFeatureCollection :: GeoFeatureCollection A.Value+bigAssFeatureCollection = GeoFeatureCollection bbox features+++bigAssFeatureCollectionWithNoBBoxJSON :: BS.ByteString+bigAssFeatureCollectionWithNoBBoxJSON = "{\"features\":[{\"geometry\":{\"geometries\":[{\"coordinates\":[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]]],\"type\":\"MultiLineString\"},{\"coordinates\":[],\"type\":\"MultiLineString\"},{\"coordinates\":[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]],[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]]],\"type\":\"MultiLineString\"},{\"coordinates\":[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]],\"type\":\"LineString\"},{\"coordinates\":[[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18],[120,-15]]],[]],\"type\":\"MultiPolygon\"},{\"coordinates\":[[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18],[120,-15]]]],\"type\":\"MultiPolygon\"},{\"coordinates\":[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18],[120,-15]]],\"type\":\"Polygon\"},{\"coordinates\":[],\"type\":\"MultiPolygon\"},{\"coordinates\":[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18],[120,-15]]],\"type\":\"Polygon\"}],\"type\":\"GeometryCollection\"},\"id\":\"GW001\",\"type\":\"Feature\",\"properties\":{\"depth\":5,\"comment\":\"Bore run over by dump truck\"}},{\"bbox\":[-32,147.5,-29.5,151],\"geometry\":null,\"id\":\"GW001\",\"type\":\"Feature\",\"properties\":{\"depth\":5,\"comment\":\"Bore run over by dump truck\"}},{\"geometry\":{\"geometries\":[{\"coordinates\":[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]]],\"type\":\"MultiLineString\"},{\"coordinates\":[],\"type\":\"MultiLineString\"},{\"coordinates\":[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]],[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]]],\"type\":\"MultiLineString\"},{\"coordinates\":[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]],\"type\":\"LineString\"},{\"coordinates\":[[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18],[120,-15]]],[]],\"type\":\"MultiPolygon\"},{\"coordinates\":[[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18],[120,-15]]]],\"type\":\"MultiPolygon\"},{\"coordinates\":[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18],[120,-15]]],\"type\":\"Polygon\"},{\"coordinates\":[],\"type\":\"MultiPolygon\"},{\"coordinates\":[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18],[120,-15]]],\"type\":\"Polygon\"}],\"type\":\"GeometryCollection\"},\"id\":\"GW001\",\"type\":\"Feature\",\"properties\":{\"depth\":5,\"comment\":\"Bore run over by dump truck\"}},{\"bbox\":[-32,147.5,-29.5,151],\"geometry\":{\"geometries\":[{\"coordinates\":[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]]],\"type\":\"MultiLineString\"},{\"coordinates\":[],\"type\":\"MultiLineString\"},{\"coordinates\":[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]],[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]]],\"type\":\"MultiLineString\"},{\"coordinates\":[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]],\"type\":\"LineString\"},{\"coordinates\":[[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18],[120,-15]]],[]],\"type\":\"MultiPolygon\"},{\"coordinates\":[[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18],[120,-15]]]],\"type\":\"MultiPolygon\"},{\"coordinates\":[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18],[120,-15]]],\"type\":\"Polygon\"},{\"coordinates\":[],\"type\":\"MultiPolygon\"},{\"coordinates\":[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18],[120,-15]]],\"type\":\"Polygon\"}],\"type\":\"GeometryCollection\"},\"type\":\"Feature\",\"properties\":{\"depth\":5,\"comment\":\"Bore run over by dump truck\"}},{\"bbox\":[-32,147.5,-29.5,151],\"geometry\":{\"geometries\":[{\"coordinates\":[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]]],\"type\":\"MultiLineString\"},{\"coordinates\":[],\"type\":\"MultiLineString\"},{\"coordinates\":[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]],[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]]],\"type\":\"MultiLineString\"},{\"coordinates\":[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]],\"type\":\"LineString\"},{\"coordinates\":[[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18],[120,-15]]],[]],\"type\":\"MultiPolygon\"},{\"coordinates\":[[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18],[120,-15]]]],\"type\":\"MultiPolygon\"},{\"coordinates\":[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18],[120,-15]]],\"type\":\"Polygon\"},{\"coordinates\":[],\"type\":\"MultiPolygon\"},{\"coordinates\":[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18],[120,-15]]],\"type\":\"Polygon\"}],\"type\":\"GeometryCollection\"},\"id\":\"GW001\",\"type\":\"Feature\",\"properties\":null},{\"bbox\":[-32,147.5,-29.5,151],\"geometry\":{\"geometries\":[{\"coordinates\":[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]]],\"type\":\"MultiLineString\"},{\"coordinates\":[],\"type\":\"MultiLineString\"},{\"coordinates\":[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]],[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]]],\"type\":\"MultiLineString\"},{\"coordinates\":[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18]],\"type\":\"LineString\"},{\"coordinates\":[[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18],[120,-15]]],[]],\"type\":\"MultiPolygon\"},{\"coordinates\":[[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18],[120,-15]]]],\"type\":\"MultiPolygon\"},{\"coordinates\":[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18],[120,-15]]],\"type\":\"Polygon\"},{\"coordinates\":[],\"type\":\"MultiPolygon\"},{\"coordinates\":[[[120,-15],[127,-15],[127,-25],[124,-25],[124,-18],[120,-18],[120,-15]]],\"type\":\"Polygon\"}],\"type\":\"GeometryCollection\"},\"id\":\"GW001\",\"type\":\"Feature\",\"properties\":{\"depth\":5,\"comment\":\"Bore run over by dump truck\"}}],\"type\":\"FeatureCollection\"}"++bigAssFeatureCollectionWithNoBBox :: GeoFeatureCollection A.Value+bigAssFeatureCollectionWithNoBBox = GeoFeatureCollection Nothing features+++emptyFeatureCollectionJSON :: BS.ByteString+emptyFeatureCollectionJSON = "{\"type\":\"FeatureCollection\",\"features\":[]}"++emptyFeatureCollection :: GeoFeatureCollection A.Value+emptyFeatureCollection = GeoFeatureCollection Nothing []+++emptyFeatureCollectionWithBBoxJSON :: BS.ByteString+emptyFeatureCollectionWithBBoxJSON = "{\"type\":\"FeatureCollection\",\"features\":[],\"bbox\":[-32,147.5,-29.5,151]}"++emptyFeatureCollectionWithBBox :: GeoFeatureCollection A.Value+emptyFeatureCollectionWithBBox = GeoFeatureCollection bbox []
+ test/Main.hs view
@@ -0,0 +1,23 @@+module Main (main) where++import           Test.Tasty+-- Local+import qualified Data.Geospatial.Internal.CRSTests                  as CRS+import qualified Data.Geospatial.Internal.GeoFeatureCollectionTests as FC+import qualified Data.Geospatial.Internal.GeoFeatureTests           as F+import qualified Data.Geospatial.Internal.GeometryTests             as G+import qualified Data.LinearRingTests                               as LR+import qualified Data.LineStringTests                               as LS+++main :: IO ()+main = do+  tests <- sequence+    [ LR.tests+    , LS.tests+    , CRS.tests+    , FC.tests+    , F.tests+    , G.tests+    ]+  defaultMain (testGroup "tests" tests)