wkt-geom (empty) → 0.0.1
raw patch · 33 files changed
+1782/−0 lines, 33 filesdep +QuickCheckdep +ansi-wl-pprintdep +basesetup-changed
Dependencies added: QuickCheck, ansi-wl-pprint, base, binary, bytestring, geojson, hspec, lens, scientific, trifecta, utf8-string, vector, wkt-geom
Files
- LICENSE +190/−0
- README.md +6/−0
- Setup.hs +2/−0
- src/Data/Ewkb.hs +23/−0
- src/Data/Internal/Ewkb/Geometry.hs +80/−0
- src/Data/Internal/Wkb/Endian.hs +39/−0
- src/Data/Internal/Wkb/Geometry.hs +58/−0
- src/Data/Internal/Wkb/GeometryCollection.hs +34/−0
- src/Data/Internal/Wkb/Geospatial.hs +42/−0
- src/Data/Internal/Wkb/Line.hs +37/−0
- src/Data/Internal/Wkb/Point.hs +60/−0
- src/Data/Internal/Wkb/Polygon.hs +56/−0
- src/Data/Internal/Wkt/Common.hs +26/−0
- src/Data/Internal/Wkt/GeometryCollection.hs +39/−0
- src/Data/Internal/Wkt/Line.hs +60/−0
- src/Data/Internal/Wkt/Point.hs +67/−0
- src/Data/Internal/Wkt/Polygon.hs +63/−0
- src/Data/Wkb.hs +23/−0
- src/Data/Wkt.hs +55/−0
- test/Data/Internal/Ewkb/GeometrySpec.hs +118/−0
- test/Data/Internal/Wkb/EndianSpec.hs +29/−0
- test/Data/Internal/Wkb/GeometrySpec.hs +73/−0
- test/Data/SpecHelper.hs +30/−0
- test/Data/WkbGeometryCollectionSpec.hs +54/−0
- test/Data/WkbLineSpec.hs +63/−0
- test/Data/WkbPointSpec.hs +55/−0
- test/Data/WkbPolygonSpec.hs +100/−0
- test/Data/WktGeometryCollectionSpec.hs +34/−0
- test/Data/WktLineSpec.hs +57/−0
- test/Data/WktPointSpec.hs +60/−0
- test/Data/WktPolygonSpec.hs +64/−0
- test/Spec.hs +1/−0
- wkt-geom.cabal +84/−0
+ LICENSE view
@@ -0,0 +1,190 @@+ Apache License+ Version 2.0, January 2004+ http://www.apache.org/licenses/++ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION++ 1. Definitions.++ "License" shall mean the terms and conditions for use, reproduction,+ and distribution as defined by Sections 1 through 9 of this document.++ "Licensor" shall mean the copyright owner or entity authorized by+ the copyright owner that is granting the License.++ "Legal Entity" shall mean the union of the acting entity and all+ other entities that control, are controlled by, or are under common+ control with that entity. For the purposes of this definition,+ "control" means (i) the power, direct or indirect, to cause the+ direction or management of such entity, whether by contract or+ otherwise, or (ii) ownership of fifty percent (50%) or more of the+ outstanding shares, or (iii) beneficial ownership of such entity.++ "You" (or "Your") shall mean an individual or Legal Entity+ exercising permissions granted by this License.++ "Source" form shall mean the preferred form for making modifications,+ including but not limited to software source code, documentation+ source, and configuration files.++ "Object" form shall mean any form resulting from mechanical+ transformation or translation of a Source form, including but+ not limited to compiled object code, generated documentation,+ and conversions to other media types.++ "Work" shall mean the work of authorship, whether in Source or+ Object form, made available under the License, as indicated by a+ copyright notice that is included in or attached to the work+ (an example is provided in the Appendix below).++ "Derivative Works" shall mean any work, whether in Source or Object+ form, that is based on (or derived from) the Work and for which the+ editorial revisions, annotations, elaborations, or other modifications+ represent, as a whole, an original work of authorship. For the purposes+ of this License, Derivative Works shall not include works that remain+ separable from, or merely link (or bind by name) to the interfaces of,+ the Work and Derivative Works thereof.++ "Contribution" shall mean any work of authorship, including+ the original version of the Work and any modifications or additions+ to that Work or Derivative Works thereof, that is intentionally+ submitted to Licensor for inclusion in the Work by the copyright owner+ or by an individual or Legal Entity authorized to submit on behalf of+ the copyright owner. For the purposes of this definition, "submitted"+ means any form of electronic, verbal, or written communication sent+ to the Licensor or its representatives, including but not limited to+ communication on electronic mailing lists, source code control systems,+ and issue tracking systems that are managed by, or on behalf of, the+ Licensor for the purpose of discussing and improving the Work, but+ excluding communication that is conspicuously marked or otherwise+ designated in writing by the copyright owner as "Not a Contribution."++ "Contributor" shall mean Licensor and any individual or Legal Entity+ on behalf of whom a Contribution has been received by Licensor and+ subsequently incorporated within the Work.++ 2. Grant of Copyright License. Subject to the terms and conditions of+ this License, each Contributor hereby grants to You a perpetual,+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable+ copyright license to reproduce, prepare Derivative Works of,+ publicly display, publicly perform, sublicense, and distribute the+ Work and such Derivative Works in Source or Object form.++ 3. Grant of Patent License. Subject to the terms and conditions of+ this License, each Contributor hereby grants to You a perpetual,+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable+ (except as stated in this section) patent license to make, have made,+ use, offer to sell, sell, import, and otherwise transfer the Work,+ where such license applies only to those patent claims licensable+ by such Contributor that are necessarily infringed by their+ Contribution(s) alone or by combination of their Contribution(s)+ with the Work to which such Contribution(s) was submitted. If You+ institute patent litigation against any entity (including a+ cross-claim or counterclaim in a lawsuit) alleging that the Work+ or a Contribution incorporated within the Work constitutes direct+ or contributory patent infringement, then any patent licenses+ granted to You under this License for that Work shall terminate+ as of the date such litigation is filed.++ 4. Redistribution. You may reproduce and distribute copies of the+ Work or Derivative Works thereof in any medium, with or without+ modifications, and in Source or Object form, provided that You+ meet the following conditions:++ (a) You must give any other recipients of the Work or+ Derivative Works a copy of this License; and++ (b) You must cause any modified files to carry prominent notices+ stating that You changed the files; and++ (c) You must retain, in the Source form of any Derivative Works+ that You distribute, all copyright, patent, trademark, and+ attribution notices from the Source form of the Work,+ excluding those notices that do not pertain to any part of+ the Derivative Works; and++ (d) If the Work includes a "NOTICE" text file as part of its+ distribution, then any Derivative Works that You distribute must+ include a readable copy of the attribution notices contained+ within such NOTICE file, excluding those notices that do not+ pertain to any part of the Derivative Works, in at least one+ of the following places: within a NOTICE text file distributed+ as part of the Derivative Works; within the Source form or+ documentation, if provided along with the Derivative Works; or,+ within a display generated by the Derivative Works, if and+ wherever such third-party notices normally appear. The contents+ of the NOTICE file are for informational purposes only and+ do not modify the License. You may add Your own attribution+ notices within Derivative Works that You distribute, alongside+ or as an addendum to the NOTICE text from the Work, provided+ that such additional attribution notices cannot be construed+ as modifying the License.++ You may add Your own copyright statement to Your modifications and+ may provide additional or different license terms and conditions+ for use, reproduction, or distribution of Your modifications, or+ for any such Derivative Works as a whole, provided Your use,+ reproduction, and distribution of the Work otherwise complies with+ the conditions stated in this License.++ 5. Submission of Contributions. Unless You explicitly state otherwise,+ any Contribution intentionally submitted for inclusion in the Work+ by You to the Licensor shall be under the terms and conditions of+ this License, without any additional terms or conditions.+ Notwithstanding the above, nothing herein shall supersede or modify+ the terms of any separate license agreement you may have executed+ with Licensor regarding such Contributions.++ 6. Trademarks. This License does not grant permission to use the trade+ names, trademarks, service marks, or product names of the Licensor,+ except as required for reasonable and customary use in describing the+ origin of the Work and reproducing the content of the NOTICE file.++ 7. Disclaimer of Warranty. Unless required by applicable law or+ agreed to in writing, Licensor provides the Work (and each+ Contributor provides its Contributions) on an "AS IS" BASIS,+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or+ implied, including, without limitation, any warranties or conditions+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A+ PARTICULAR PURPOSE. You are solely responsible for determining the+ appropriateness of using or redistributing the Work and assume any+ risks associated with Your exercise of permissions under this License.++ 8. Limitation of Liability. In no event and under no legal theory,+ whether in tort (including negligence), contract, or otherwise,+ unless required by applicable law (such as deliberate and grossly+ negligent acts) or agreed to in writing, shall any Contributor be+ liable to You for damages, including any direct, indirect, special,+ incidental, or consequential damages of any character arising as a+ result of this License or out of the use or inability to use the+ Work (including but not limited to damages for loss of goodwill,+ work stoppage, computer failure or malfunction, or any and all+ other commercial damages or losses), even if such Contributor+ has been advised of the possibility of such damages.++ 9. Accepting Warranty or Additional Liability. While redistributing+ the Work or Derivative Works thereof, You may choose to offer,+ and charge a fee for, acceptance of support, warranty, indemnity,+ or other liability obligations and/or rights consistent with this+ License. However, in accepting such obligations, You may act only+ on Your own behalf and on Your sole responsibility, not on behalf+ of any other Contributor, and only if You agree to indemnify,+ defend, and hold each Contributor harmless for any liability+ incurred by, or claims asserted against, such Contributor by reason+ of your accepting any such warranty or additional liability.++ END OF TERMS AND CONDITIONS++ Copyright 2017-2018 wkt-geom Project++ Licensed under the Apache License, Version 2.0 (the "License");+ you may not use this file except in compliance with the License.+ You may obtain a copy of the License at++ http://www.apache.org/licenses/LICENSE-2.0++ Unless required by applicable law or agreed to in writing, software+ distributed under the License is distributed on an "AS IS" BASIS,+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+ See the License for the specific language governing permissions and+ limitations under the License.
+ README.md view
@@ -0,0 +1,6 @@+# wkt-geom++++http://www.opengeospatial.org/standards/sfa (Section 7)+http://edndoc.esri.com/arcsde/9.0/general_topics/wkt_representation.htm
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ src/Data/Ewkb.hs view
@@ -0,0 +1,23 @@+-- Refer to the eWKB Postgis Documentation <https://postgis.net/docs/using_postgis_dbmanagement.html#EWKB_EWKT>+--+-- Allows parsing of ByteString into a Geospatial Object.+--+-------------------------------------------------------------------+module Data.Ewkb+ ( parseByteString+ ) where++import qualified Data.Binary.Get as BinaryGet+import qualified Data.ByteString.Lazy as LazyByteString+import qualified Data.Geospatial as Geospatial++import qualified Data.Internal.Ewkb.Geometry as EwkbGeometry+import qualified Data.Internal.Wkb.Geospatial as WkbGeospatial++parseByteString :: LazyByteString.ByteString -> Either String Geospatial.GeospatialGeometry+parseByteString byteString =+ case BinaryGet.runGetOrFail+ (WkbGeospatial.geospatialGeometry EwkbGeometry.wkbGeometryType)+ byteString of+ Left (_, _, err) -> Left $ "Could not parse ewkb: " ++ err+ Right (_, _, geoSpatialGeometry) -> Right geoSpatialGeometry
+ src/Data/Internal/Ewkb/Geometry.hs view
@@ -0,0 +1,80 @@+module Data.Internal.Ewkb.Geometry+ ( EwkbGeometryType (..)+ , SridType (..)+ , ewkbGeometryType+ , wkbGeometryType+ ) where++import qualified Control.Monad as Monad+import qualified Data.Binary.Get as BinaryGet+import Data.Bits ((.&.))+import qualified Data.Word as Word++import qualified Data.Internal.Wkb.Endian as Endian+import qualified Data.Internal.Wkb.Geometry as Geometry++data SridType = Srid Word.Word32 | NoSrid deriving (Show, Eq)++data EwkbGeometryType = EwkbGeom Geometry.WkbGeometryType SridType deriving (Show, Eq)++ewkbGeometryType :: Endian.EndianType -> BinaryGet.Get EwkbGeometryType+ewkbGeometryType endianType = do+ rawGeometryType <- Endian.fourBytes endianType+ ewkbSrid <- getEwkbSrid endianType rawGeometryType+ geomType <- rawtoWkbGeometryType rawGeometryType+ pure $ EwkbGeom geomType ewkbSrid++wkbGeometryType :: Endian.EndianType -> BinaryGet.Get Geometry.WkbGeometryType+wkbGeometryType endianType = do+ rawGeometryType <- Endian.fourBytes endianType+ _ <- getEwkbSrid endianType rawGeometryType+ rawtoWkbGeometryType rawGeometryType++rawtoWkbGeometryType :: Word.Word32 -> BinaryGet.Get Geometry.WkbGeometryType+rawtoWkbGeometryType rawGeometryType = do+ let geomType = intToGeometryType rawGeometryType+ coordType = intToCoordinateType rawGeometryType+ case geomType of+ Just g -> pure $ Geometry.WkbGeom g coordType+ _ -> Monad.fail $ "Invalid EwkbGeometry: " ++ show rawGeometryType++getEwkbSrid :: Endian.EndianType -> Word.Word32 -> BinaryGet.Get SridType+getEwkbSrid endianType int =+ if int .&. 0x20000000 /= 0 then do+ srid <- Endian.fourBytes endianType+ if srid == 4326 then+ pure $ Srid srid+ else+ Monad.fail $ "Invalid SRID only 4326 supported: " ++ show srid+ else+ pure NoSrid++intToGeometryType :: Word.Word32 -> Maybe Geometry.GeometryType+intToGeometryType int =+ case int .&. 0x0fffffff of+ 0 -> Just Geometry.Geometry+ 1 -> Just Geometry.Point+ 2 -> Just Geometry.LineString+ 3 -> Just Geometry.Polygon+ 4 -> Just Geometry.MultiPoint+ 5 -> Just Geometry.MultiLineString+ 6 -> Just Geometry.MultiPolygon+ 7 -> Just Geometry.GeometryCollection+ _ -> Nothing++intToCoordinateType :: Word.Word32 -> Geometry.CoordinateType+intToCoordinateType int =+ case (hasZ int, hasM int) of+ (False, False) -> Geometry.TwoD+ (False, True) -> Geometry.M+ (True, False) -> Geometry.Z+ (True, True) -> Geometry.ZM++hasZ :: Word.Word32 -> Bool+hasZ int =+ int .&. 0x80000000 /= 0++hasM :: Word.Word32 -> Bool+hasM int =+ int .&. 0x40000000 /= 0+
+ src/Data/Internal/Wkb/Endian.hs view
@@ -0,0 +1,39 @@+module Data.Internal.Wkb.Endian+ ( EndianType (..)+ , endianType+ , fourBytes+ , doubleBytes+ ) where++import qualified Control.Monad as Monad+import qualified Data.Binary.Get as BinaryGet+import qualified Data.Word as Word++data EndianType = LittleEndian | BigEndian deriving (Show, Eq)++endianType :: BinaryGet.Get EndianType+endianType = do+ byte <- BinaryGet.getWord8+ case byte of+ 0 ->+ pure BigEndian+ 1 ->+ pure LittleEndian+ _ ->+ Monad.fail "Invalid EndianType"++fourBytes :: EndianType -> BinaryGet.Get Word.Word32+fourBytes et =+ case et of+ LittleEndian ->+ BinaryGet.getWord32le+ BigEndian ->+ BinaryGet.getWord32be++doubleBytes :: EndianType -> BinaryGet.Get Double+doubleBytes et =+ case et of+ LittleEndian ->+ BinaryGet.getDoublele+ BigEndian ->+ BinaryGet.getDoublebe
+ src/Data/Internal/Wkb/Geometry.hs view
@@ -0,0 +1,58 @@+module Data.Internal.Wkb.Geometry+ ( GeometryType (..)+ , CoordinateType (..)+ , WkbGeometryType (..)+ , geometryTypeWithCoords+ ) where++import qualified Control.Monad as Monad+import qualified Data.Binary.Get as BinaryGet+import qualified Data.Word as Word++import qualified Data.Internal.Wkb.Endian as Endian++data GeometryType+ = Geometry+ | Point+ | LineString+ | Polygon+ | MultiPoint+ | MultiLineString+ | MultiPolygon+ | GeometryCollection deriving (Show, Eq)++data CoordinateType = TwoD | Z | M | ZM deriving (Show, Eq)++data WkbGeometryType = WkbGeom GeometryType CoordinateType deriving (Show, Eq)++geometryTypeWithCoords :: Endian.EndianType -> BinaryGet.Get WkbGeometryType+geometryTypeWithCoords endianType = do+ fullGeometryType <- Endian.fourBytes endianType+ let geomType = intToGeometryType $ fullGeometryType `rem` 1000+ coordType = intToCoordinateType $ fullGeometryType `div` 1000+ case (geomType, coordType) of+ (Just g, Just c) -> pure $ WkbGeom g c+ _ ->+ Monad.fail $ "Invalid WkbGeometryType: " ++ show fullGeometryType++intToGeometryType :: Word.Word32 -> Maybe GeometryType+intToGeometryType int =+ case int of+ 0 -> Just Geometry+ 1 -> Just Point+ 2 -> Just LineString+ 3 -> Just Polygon+ 4 -> Just MultiPoint+ 5 -> Just MultiLineString+ 6 -> Just MultiPolygon+ 7 -> Just GeometryCollection+ _ -> Nothing++intToCoordinateType :: Word.Word32 -> Maybe CoordinateType+intToCoordinateType int =+ case int of+ 0 -> Just TwoD+ 1 -> Just Z+ 2 -> Just M+ 3 -> Just ZM+ _ -> Nothing
+ src/Data/Internal/Wkb/GeometryCollection.hs view
@@ -0,0 +1,34 @@+module Data.Internal.Wkb.GeometryCollection+ ( geometryCollection+ , enclosedFeature+ ) where++import qualified Control.Monad as Monad+import qualified Data.Binary.Get as BinaryGet+import qualified Data.Geospatial as Geospatial+import qualified Data.Vector as Vector++import qualified Data.Internal.Wkb.Endian as Endian+import qualified Data.Internal.Wkb.Geometry as Geometry++geometryCollection :: BinaryGet.Get Geospatial.GeospatialGeometry+ -> Endian.EndianType+ -> Geometry.CoordinateType+ -> BinaryGet.Get Geospatial.GeospatialGeometry+geometryCollection getGeospatialGeometry endianType _ = do+ numberOfGeometries <- Endian.fourBytes endianType+ geoSpatialGeometries <- Vector.generateM (fromIntegral numberOfGeometries) (const getGeospatialGeometry)+ pure $ Geospatial.Collection geoSpatialGeometries++enclosedFeature :: (Endian.EndianType -> BinaryGet.Get Geometry.WkbGeometryType)+ -> Geometry.GeometryType+ -> (Endian.EndianType -> Geometry.CoordinateType -> BinaryGet.Get feature)+ -> BinaryGet.Get feature+enclosedFeature getWkbGeom expectedGeometryType getFeature = do+ endianType <- Endian.endianType+ geometryTypeWithCoords <- getWkbGeom endianType+ let (Geometry.WkbGeom geoType coordType) = geometryTypeWithCoords+ if geoType == expectedGeometryType then+ getFeature endianType coordType+ else+ Monad.fail "Wrong geometry type of enclosed feature"
+ src/Data/Internal/Wkb/Geospatial.hs view
@@ -0,0 +1,42 @@+module Data.Internal.Wkb.Geospatial+ ( geospatialGeometry+ ) where++import qualified Data.Binary.Get as BinaryGet+import qualified Data.Geospatial as Geospatial++import qualified Data.Internal.Wkb.Endian as Endian+import qualified Data.Internal.Wkb.Geometry as Geometry+import qualified Data.Internal.Wkb.GeometryCollection as GeometryCollection+import qualified Data.Internal.Wkb.Line as Line+import qualified Data.Internal.Wkb.Point as Point+import qualified Data.Internal.Wkb.Polygon as Polygon++geospatialGeometry :: (Endian.EndianType -> BinaryGet.Get Geometry.WkbGeometryType)+ -> BinaryGet.Get Geospatial.GeospatialGeometry+geospatialGeometry getWkbGeom = do+ endianType <- Endian.endianType+ geometryTypeWithCoords <- getWkbGeom endianType+ let (Geometry.WkbGeom geomType coordType) = geometryTypeWithCoords+ getFeature geomType getWkbGeom endianType coordType++getFeature :: Geometry.GeometryType+ -> (Endian.EndianType -> BinaryGet.Get Geometry.WkbGeometryType)+ -> Endian.EndianType+ -> Geometry.CoordinateType+ -> BinaryGet.Get Geospatial.GeospatialGeometry+getFeature geomType getWkbGeom =+ case geomType of+ Geometry.Geometry -> getNoGeometry+ Geometry.Point -> Point.point+ Geometry.LineString -> Line.line+ Geometry.Polygon -> Polygon.polygon+ Geometry.MultiPoint -> Point.multiPoint getWkbGeom+ Geometry.MultiLineString -> Line.multiLine getWkbGeom+ Geometry.MultiPolygon -> Polygon.multiPolygon getWkbGeom+ Geometry.GeometryCollection -> GeometryCollection.geometryCollection (geospatialGeometry getWkbGeom)++getNoGeometry :: Endian.EndianType -> Geometry.CoordinateType -> BinaryGet.Get Geospatial.GeospatialGeometry+getNoGeometry _ _ =+ pure Geospatial.NoGeometry+
+ src/Data/Internal/Wkb/Line.hs view
@@ -0,0 +1,37 @@+module Data.Internal.Wkb.Line+ ( Data.Internal.Wkb.Line.line+ , multiLine+ ) where++import qualified Control.Monad as Monad+import qualified Data.Binary.Get as BinaryGet+import qualified Data.Geospatial as Geospatial+import qualified Data.LineString as LineString+import qualified Data.Vector as Vector++import qualified Data.Internal.Wkb.Endian as Endian+import qualified Data.Internal.Wkb.Geometry as Geometry+import qualified Data.Internal.Wkb.GeometryCollection as GeometryCollection+import qualified Data.Internal.Wkb.Point as Point++line :: Endian.EndianType -> Geometry.CoordinateType -> BinaryGet.Get Geospatial.GeospatialGeometry+line endianType coordType = do+ gl <- geoLine endianType coordType+ pure $ Geospatial.Line gl++multiLine :: (Endian.EndianType -> BinaryGet.Get Geometry.WkbGeometryType) -> Endian.EndianType -> Geometry.CoordinateType -> BinaryGet.Get Geospatial.GeospatialGeometry+multiLine getWkbGeom endianType _ = do+ numberOfLines <- Endian.fourBytes endianType+ geoLines <- Vector.generateM (fromIntegral numberOfLines) (const $ GeometryCollection.enclosedFeature getWkbGeom Geometry.LineString geoLine)+ pure $ Geospatial.MultiLine $ Geospatial.mergeGeoLines geoLines++geoLine :: Endian.EndianType -> Geometry.CoordinateType -> BinaryGet.Get Geospatial.GeoLine+geoLine endianType coordType = do+ numberOfPoints <- Endian.fourBytes endianType+ if numberOfPoints >= 2 then do+ p1 <- Point.coordPoint endianType coordType+ p2 <- Point.coordPoint endianType coordType+ pts <- Point.coordPoints endianType coordType (numberOfPoints - 2)+ pure $ Geospatial.GeoLine $ LineString.makeLineString p1 p2 pts+ else+ Monad.fail "Must have at least two points for a line"
+ src/Data/Internal/Wkb/Point.hs view
@@ -0,0 +1,60 @@+module Data.Internal.Wkb.Point+ ( point+ , multiPoint+ , coordPoint+ , coordPoints+ ) where++import qualified Data.Binary.Get as BinaryGet+import qualified Data.Geospatial as Geospatial+import qualified Data.Vector as Vector+import qualified Data.Vector.Storable as VectorStorable+import qualified Data.Word as Word++import qualified Data.Internal.Wkb.Endian as Endian+import qualified Data.Internal.Wkb.Geometry as Geometry+import qualified Data.Internal.Wkb.GeometryCollection as GeometryCollection++point :: Endian.EndianType -> Geometry.CoordinateType -> BinaryGet.Get Geospatial.GeospatialGeometry+point endianType coordType = do+ gp <- geoPoint endianType coordType+ pure $ Geospatial.Point gp++multiPoint :: (Endian.EndianType -> BinaryGet.Get Geometry.WkbGeometryType) -> Endian.EndianType -> Geometry.CoordinateType -> BinaryGet.Get Geospatial.GeospatialGeometry+multiPoint getWkbGeom endianType _ = do+ numberOfPoints <- Endian.fourBytes endianType+ geoPoints <- Vector.generateM (fromIntegral numberOfPoints) (const $ GeometryCollection.enclosedFeature getWkbGeom Geometry.Point geoPoint)+ pure $ Geospatial.MultiPoint $ Geospatial.mergeGeoPoints geoPoints++geoPoint :: Endian.EndianType -> Geometry.CoordinateType -> BinaryGet.Get Geospatial.GeoPoint+geoPoint endianType coordType = do+ p <- coordPoint endianType coordType+ pure $ Geospatial.GeoPoint p++coordPoints :: Endian.EndianType -> Geometry.CoordinateType -> Word.Word32 -> BinaryGet.Get (VectorStorable.Vector Geospatial.GeoPositionWithoutCRS)+coordPoints endianType coordType numberOfPoints =+ VectorStorable.generateM (fromIntegral numberOfPoints) (const $ coordPoint endianType coordType)++coordPoint :: Endian.EndianType -> Geometry.CoordinateType -> BinaryGet.Get Geospatial.GeoPositionWithoutCRS+coordPoint endianType coordType =+ case coordType of+ Geometry.TwoD -> do+ x <- Endian.doubleBytes endianType+ y <- Endian.doubleBytes endianType+ pure $ Geospatial.GeoPointXY (Geospatial.PointXY x y)+ Geometry.Z -> do+ x <- Endian.doubleBytes endianType+ y <- Endian.doubleBytes endianType+ z <- Endian.doubleBytes endianType+ pure $ Geospatial.GeoPointXYZ (Geospatial.PointXYZ x y z)+ Geometry.M -> do+ x <- Endian.doubleBytes endianType+ y <- Endian.doubleBytes endianType+ m <- Endian.doubleBytes endianType+ pure $ Geospatial.GeoPointXYZ (Geospatial.PointXYZ x y m)+ Geometry.ZM -> do+ x <- Endian.doubleBytes endianType+ y <- Endian.doubleBytes endianType+ z <- Endian.doubleBytes endianType+ m <- Endian.doubleBytes endianType+ pure $ Geospatial.GeoPointXYZM (Geospatial.PointXYZM x y z m)
+ src/Data/Internal/Wkb/Polygon.hs view
@@ -0,0 +1,56 @@+module Data.Internal.Wkb.Polygon+ ( polygon+ , multiPolygon+ ) where++import qualified Control.Monad as Monad+import qualified Data.Binary.Get as BinaryGet+import qualified Data.Geospatial as Geospatial+import qualified Data.LinearRing as LinearRing+import qualified Data.Vector as Vector+import qualified Data.Vector.Storable as VectorStorable++import qualified Data.Internal.Wkb.Endian as Endian+import qualified Data.Internal.Wkb.Geometry as Geometry+import qualified Data.Internal.Wkb.GeometryCollection as GeometryCollection+import qualified Data.Internal.Wkb.Point as Point++polygon :: Endian.EndianType -> Geometry.CoordinateType -> BinaryGet.Get Geospatial.GeospatialGeometry+polygon endianType coordType = do+ geoPolygon <- getGeoPolygon endianType coordType+ pure $ Geospatial.Polygon geoPolygon++multiPolygon :: (Endian.EndianType -> BinaryGet.Get Geometry.WkbGeometryType) -> Endian.EndianType -> Geometry.CoordinateType -> BinaryGet.Get Geospatial.GeospatialGeometry+multiPolygon getWkbGeom endianType _ = do+ numberOfPolygons <- Endian.fourBytes endianType+ geoPolygons <- Vector.generateM (fromIntegral numberOfPolygons) (const $ GeometryCollection.enclosedFeature getWkbGeom Geometry.Polygon getGeoPolygon)+ pure $ Geospatial.MultiPolygon $ Geospatial.mergeGeoPolygons geoPolygons++getGeoPolygon :: Endian.EndianType -> Geometry.CoordinateType -> BinaryGet.Get Geospatial.GeoPolygon+getGeoPolygon endianType coordType = do+ linearRings <- getLinearRings endianType coordType+ pure $ Geospatial.GeoPolygon linearRings++getLinearRings :: Endian.EndianType -> Geometry.CoordinateType -> BinaryGet.Get (Vector.Vector (LinearRing.LinearRing Geospatial.GeoPositionWithoutCRS))+getLinearRings endianType coordType = do+ numberOfRings <- Endian.fourBytes endianType+ Vector.generateM (fromIntegral numberOfRings) (const $ getLinearRing endianType coordType)++getLinearRing :: Endian.EndianType -> Geometry.CoordinateType -> BinaryGet.Get (LinearRing.LinearRing Geospatial.GeoPositionWithoutCRS)+getLinearRing endianType coordType = do+ numberOfPoints <- Endian.fourBytes endianType+ if numberOfPoints >= 4 then do+ p1 <- Point.coordPoint endianType coordType+ p2 <- Point.coordPoint endianType coordType+ p3 <- Point.coordPoint endianType coordType+ pts <- Point.coordPoints endianType coordType (numberOfPoints - 3)+ if VectorStorable.last pts == p1 then+ pure $ LinearRing.makeLinearRing p1 p2 p3 (VectorStorable.init pts)+ else+ Monad.fail $+ "First and last points of linear ring are different: first="+ ++ show p1 ++ " last=" ++ show (VectorStorable.last pts)+ else+ Monad.fail $+ "Must have at least four points for a linear ring: "+ ++ show numberOfPoints
+ src/Data/Internal/Wkt/Common.hs view
@@ -0,0 +1,26 @@+module Data.Internal.Wkt.Common+ ( emptySet+ , emptySets+ , number+ ) where++import Control.Applicative ((<|>))+import qualified Data.Scientific as Scientific+import qualified Data.Vector as Vector+import qualified Text.Trifecta as Trifecta++emptySet :: Trifecta.Parser (Vector.Vector a)+emptySet = do+ _ <- Trifecta.string "empty"+ pure Vector.empty++emptySets :: Trifecta.Parser ([a], [[a]])+emptySets = do+ _ <- Trifecta.string "empty"+ pure ([], [])++number :: Trifecta.Parser Scientific.Scientific+number = do+ sign <- (Trifecta.char '+' >> pure id) <|> (Trifecta.char '-' >> pure negate) <|> pure id+ sign . either fromInteger id <$> Trifecta.integerOrScientific+
+ src/Data/Internal/Wkt/GeometryCollection.hs view
@@ -0,0 +1,39 @@+module Data.Internal.Wkt.GeometryCollection+ ( geometryCollection+ , emptyGeometryCollection+ ) where++import Control.Applicative ((<|>))+import qualified Data.Geospatial as Geospatial+import qualified Data.Vector as Vector+import qualified Text.Trifecta as Trifecta++import qualified Data.Internal.Wkt.Common as Wkt+import qualified Data.Internal.Wkt.Line as Line+import qualified Data.Internal.Wkt.Point as Point+import qualified Data.Internal.Wkt.Polygon as Polygon++geometryCollection :: Trifecta.Parser (Vector.Vector Geospatial.GeospatialGeometry)+geometryCollection = do+ _ <- Trifecta.string "geometrycollection"+ _ <- Trifecta.spaces+ Wkt.emptySet <|> bracketedAll++bracketedAll :: Trifecta.Parser (Vector.Vector Geospatial.GeospatialGeometry)+bracketedAll = do+ _ <- Trifecta.char '(' >> Trifecta.spaces+ x <- parseAll+ _ <- Trifecta.spaces >> Trifecta.char ')'+ pure x++parseAll :: Trifecta.Parser (Vector.Vector Geospatial.GeospatialGeometry)+parseAll = do+ let+ single = Geospatial.Point <$> Point.point <|> Geospatial.Line <$> Line.lineString <|> Geospatial.Polygon <$> Polygon.polygon+ multi = Geospatial.MultiPoint <$> Point.multiPoint <|> Geospatial.MultiLine <$> Line.multiLineString <|> Geospatial.MultiPolygon <$> Polygon.multiPolygon+ x <- single <|> multi+ xs <- Trifecta.many (Trifecta.char ',' >> Trifecta.spaces >> (single <|> multi))+ pure $ Vector.cons x (Vector.fromList xs)++emptyGeometryCollection :: Vector.Vector Geospatial.GeospatialGeometry+emptyGeometryCollection = Vector.empty
+ src/Data/Internal/Wkt/Line.hs view
@@ -0,0 +1,60 @@+module Data.Internal.Wkt.Line+ ( line+ , lineString+ , multiLineString+ , emptyLine+ , emptyMultiLine+ , commandPoint+ ) where++import Control.Applicative ((<|>))+import qualified Data.Geospatial as Geospatial+import qualified Data.LineString as LineString+import qualified Data.Vector as Vector+import qualified Data.Vector.Storable as VectorStorable+import qualified Text.Trifecta as Trifecta++import qualified Data.Internal.Wkt.Common as Wkt+import qualified Data.Internal.Wkt.Point as Point++lineString :: Trifecta.Parser Geospatial.GeoLine+lineString = do+ _ <- Trifecta.string "linestring"+ _ <- Trifecta.spaces+ (Trifecta.string "empty" >> pure emptyLine) <|> Geospatial.GeoLine <$> line++multiLineString :: Trifecta.Parser Geospatial.GeoMultiLine+multiLineString = do+ _ <- Trifecta.string "multilinestring"+ _ <- Trifecta.spaces+ x <- Wkt.emptySet <|> manyLines+ pure $ Geospatial.GeoMultiLine x++manyLines :: Trifecta.Parser (Vector.Vector (LineString.LineString Geospatial.GeoPositionWithoutCRS))+manyLines = do+ _ <- Trifecta.spaces >> Trifecta.char '('+ x <- line+ xs <- Trifecta.many (Trifecta.char ',' >> Trifecta.spaces >> line)+ _ <- Trifecta.char ')' >> Trifecta.spaces+ pure $ Vector.cons x (Vector.fromList xs)++line :: Trifecta.Parser (LineString.LineString Geospatial.GeoPositionWithoutCRS)+line = do+ _ <- Trifecta.spaces >> Trifecta.char '(' >> Trifecta.spaces+ first <- Point.justPoints+ second <- commandPoint+ rest <- Trifecta.many commandPoint+ _ <- Trifecta.char ')' >> Trifecta.spaces+ pure $ LineString.makeLineString first second (VectorStorable.fromList rest)++commandPoint :: Trifecta.Parser Geospatial.GeoPositionWithoutCRS+commandPoint = do+ _ <- Trifecta.char ','+ _ <- Trifecta.spaces+ Point.justPoints++emptyLine :: Geospatial.GeoLine+emptyLine = Geospatial.GeoLine $ LineString.makeLineString Geospatial.GeoEmpty Geospatial.GeoEmpty VectorStorable.empty++emptyMultiLine :: Geospatial.GeoMultiLine+emptyMultiLine = Geospatial.mergeGeoLines Vector.empty
+ src/Data/Internal/Wkt/Point.hs view
@@ -0,0 +1,67 @@+module Data.Internal.Wkt.Point+ ( point+ , multiPoint+ , justPoints+ , emptyPoint+ , emptyMultiPoint+ ) where++import Control.Applicative ((<|>))+import qualified Data.Geospatial as Geospatial+import qualified Data.Scientific as Scientific+import qualified Data.Vector as Vector+import qualified Text.Trifecta as Trifecta++import qualified Data.Internal.Wkt.Common as Wkt++point :: Trifecta.Parser Geospatial.GeoPoint+point = do+ _ <- Trifecta.string "point"+ _ <- Trifecta.spaces+ (Trifecta.string "empty" >> pure emptyPoint) <|> fmap Geospatial.GeoPoint bracketedPoint++multiPoint :: Trifecta.Parser Geospatial.GeoMultiPoint+multiPoint = do+ _ <- Trifecta.string "multipoint"+ _ <- Trifecta.spaces+ xl <- Wkt.emptySet <|> manyPoints+ pure $ Geospatial.GeoMultiPoint xl++manyPoints :: Trifecta.Parser (Vector.Vector Geospatial.GeoPositionWithoutCRS)+manyPoints = do+ _ <- Trifecta.char '(' >> Trifecta.spaces+ xl <- unbracketedPoints <|> bracketedPoints+ _ <- Trifecta.spaces >> Trifecta.char ')' >> Trifecta.spaces+ pure xl++unbracketedPoints :: Trifecta.Parser (Vector.Vector Geospatial.GeoPositionWithoutCRS)+unbracketedPoints = do+ x <- justPoints+ xs <- Trifecta.many (Trifecta.char ',' >> Trifecta.spaces >> justPoints)+ pure $ Vector.cons x (Vector.fromList xs)++bracketedPoints :: Trifecta.Parser (Vector.Vector Geospatial.GeoPositionWithoutCRS)+bracketedPoints = do+ x <- bracketedPoint+ xs <- Trifecta.many (Trifecta.char ',' >> Trifecta.spaces >> bracketedPoint)+ pure $ Vector.cons x (Vector.fromList xs)++bracketedPoint :: Trifecta.Parser Geospatial.GeoPositionWithoutCRS+bracketedPoint = do+ _ <- Trifecta.spaces >> Trifecta.char '(' >> Trifecta.spaces+ x <- justPoints+ _ <- Trifecta.spaces >> Trifecta.char ')' >> Trifecta.spaces+ pure x++justPoints :: Trifecta.Parser Geospatial.GeoPositionWithoutCRS+justPoints = do+ x <- Wkt.number+ _ <- Trifecta.spaces+ y <- Wkt.number+ pure $ Geospatial.GeoPointXY (Geospatial.PointXY (Scientific.toRealFloat x) (Scientific.toRealFloat y))++emptyPoint :: Geospatial.GeoPoint+emptyPoint = Geospatial.GeoPoint Geospatial.GeoEmpty++emptyMultiPoint :: Geospatial.GeoMultiPoint+emptyMultiPoint = Geospatial.GeoMultiPoint Vector.empty
+ src/Data/Internal/Wkt/Polygon.hs view
@@ -0,0 +1,63 @@+module Data.Internal.Wkt.Polygon+ ( polygon+ , multiPolygon+ , emptyPolygon+ , emptyMultiPolygon+ ) where++import Control.Applicative ((<|>))+import qualified Data.Geospatial as Geospatial+import qualified Data.LinearRing as LinearRing+import qualified Data.Vector as Vector+import qualified Data.Vector.Storable as VectorStorable+import qualified Text.Trifecta as Trifecta++import qualified Data.Internal.Wkt.Common as Wkt+import qualified Data.Internal.Wkt.Line as Line+import qualified Data.Internal.Wkt.Point as Point++polygon :: Trifecta.Parser Geospatial.GeoPolygon+polygon = do+ _ <- Trifecta.string "polygon"+ _ <- Trifecta.spaces+ x <- Wkt.emptySet <|> polygon'+ pure $ Geospatial.GeoPolygon x++multiPolygon :: Trifecta.Parser Geospatial.GeoMultiPolygon+multiPolygon = do+ _ <- Trifecta.string "multipolygon"+ _ <- Trifecta.spaces+ xs <- Wkt.emptySet <|> multiPolygon'+ pure $ Geospatial.GeoMultiPolygon xs++polygon' :: Trifecta.Parser (Vector.Vector (LinearRing.LinearRing Geospatial.GeoPositionWithoutCRS))+polygon' = do+ _ <- Trifecta.spaces >> Trifecta.char '('+ x <- linearRing+ xs <- Trifecta.many (Trifecta.char ',' >> Trifecta.spaces >> linearRing)+ _ <- Trifecta.char ')' >> Trifecta.spaces+ pure $ Vector.cons x (Vector.fromList xs)++multiPolygon' :: Trifecta.Parser (Vector.Vector (Vector.Vector (LinearRing.LinearRing Geospatial.GeoPositionWithoutCRS)))+multiPolygon' = do+ _ <- Trifecta.spaces >> Trifecta.char '('+ x <- polygon'+ xs <- Trifecta.many (Trifecta.char ',' >> Trifecta.spaces >> polygon')+ _ <- Trifecta.char ')' >> Trifecta.spaces+ pure $ Vector.cons x (Vector.fromList xs)++linearRing :: Trifecta.Parser (LinearRing.LinearRing Geospatial.GeoPositionWithoutCRS)+linearRing = do+ _ <- Trifecta.spaces >> Trifecta.char '(' >> Trifecta.spaces+ first <- Point.justPoints+ second <- Line.commandPoint+ third <- Line.commandPoint+ rest <- Trifecta.many Line.commandPoint+ _ <- Trifecta.char ')' >> Trifecta.spaces+ pure $ LinearRing.makeLinearRing first second third (VectorStorable.init $ VectorStorable.fromList rest)++emptyPolygon :: Geospatial.GeoPolygon+emptyPolygon = Geospatial.GeoPolygon Vector.empty++emptyMultiPolygon :: Geospatial.GeoMultiPolygon+emptyMultiPolygon = Geospatial.mergeGeoPolygons Vector.empty
+ src/Data/Wkb.hs view
@@ -0,0 +1,23 @@+-- Refer to the WKB Wikipedia page <https://en.wikipedia.org/wiki/Well-known_text#Well-known_binary>+--+-- Allows parsing of ByteString into a Geospatial Object.+--+-------------------------------------------------------------------+module Data.Wkb+ ( parseByteString+ ) where++import qualified Data.Binary.Get as BinaryGet+import qualified Data.ByteString.Lazy as LazyByteString+import qualified Data.Geospatial as Geospatial++import qualified Data.Internal.Wkb.Geometry as Geometry+import qualified Data.Internal.Wkb.Geospatial as WkbGeospatial++parseByteString :: LazyByteString.ByteString -> Either String Geospatial.GeospatialGeometry+parseByteString byteString =+ case BinaryGet.runGetOrFail+ (WkbGeospatial.geospatialGeometry Geometry.geometryTypeWithCoords)+ byteString of+ Left (_, _, err) -> Left $ "Could not parse wkb: " ++ err+ Right (_, _, geoSpatialGeometry) -> Right geoSpatialGeometry
+ src/Data/Wkt.hs view
@@ -0,0 +1,55 @@+-- Refer to the WKT Spec <http://www.opengeospatial.org/standards/wkt-crs>+--+-- Allows parsing of String or ByteString into a Wkt object.+--+-------------------------------------------------------------------+module Data.Wkt+ ( parseByteString+ , parseString+ , WktGeometryCollection.geometryCollection+ , WktGeometryCollection.emptyGeometryCollection+ , WktLine.emptyLine+ , WktLine.emptyMultiLine+ , WktLine.lineString+ , WktLine.multiLineString+ , WktPoint.emptyPoint+ , WktPoint.emptyMultiPoint+ , WktPoint.point+ , WktPoint.multiPoint+ , WktPolygon.emptyPolygon+ , WktPolygon.emptyMultiPolygon+ , WktPolygon.polygon+ , WktPolygon.multiPolygon+ ) where++import qualified Data.ByteString as ByteString+import qualified Data.ByteString.UTF8 as UTF8+import qualified Data.Char as Char+import qualified Text.Trifecta as Trifecta+import qualified Text.Trifecta.Delta as TrifectaDelta++import qualified Data.Internal.Wkt.GeometryCollection as WktGeometryCollection+import qualified Data.Internal.Wkt.Line as WktLine+import qualified Data.Internal.Wkt.Point as WktPoint+import qualified Data.Internal.Wkt.Polygon as WktPolygon++delta :: String -> TrifectaDelta.Delta+delta str = TrifectaDelta.Directed (UTF8.fromString str) 0 0 0 0++parseByteString :: Trifecta.Parser a -> ByteString.ByteString -> Trifecta.Result a+parseByteString p bs = Trifecta.parseByteString p (TrifectaDelta.Directed lowerBs 0 0 0 0) lowerBs+ where+ lowerBs = d8ToLower bs+ d8ToLower = ByteString.map f+ where+ f w | w >= 65 && w <= 90 = w + 32+ | otherwise = w++parseString :: Trifecta.Parser a -> String -> Trifecta.Result a+parseString p s = Trifecta.parseString p (Data.Wkt.delta lowerS) lowerS+ where+ lowerS = asciiToLower s+ asciiToLower = fmap f+ where+ f c | Char.isAsciiUpper c = Char.chr (Char.ord c + 32)+ | otherwise = c
+ test/Data/Internal/Ewkb/GeometrySpec.hs view
@@ -0,0 +1,118 @@+{-# LANGUAGE OverloadedStrings #-}++module Data.Internal.Ewkb.GeometrySpec where++import qualified Data.Binary.Get as BinaryGet+import qualified Data.ByteString.Builder as ByteStringBuilder+import qualified Data.ByteString.Lazy as LazyByteString+import Data.Monoid ((<>))+import qualified Data.Word as Word+import Test.Hspec (Spec, describe, it, shouldBe)++import qualified Data.Internal.Ewkb.Geometry as Ewkb+import qualified Data.Internal.Wkb.Endian as Endian+import qualified Data.Internal.Wkb.Geometry as Wkb++spec :: Spec+spec =+ testValidGetEwkbGeometryType++testValidGetEwkbGeometryType :: Spec+testValidGetEwkbGeometryType =+ describe "get extended wkb geometry type for valid data" $+ mapM_ testGetEwkbGeometryType expectations++testGetEwkbGeometryType :: (Word.Word32, Maybe Word.Word32, Ewkb.EwkbGeometryType) -> Spec+testGetEwkbGeometryType (rawGeomType, maybeSrid, expected) =+ it ("Parse " ++ show expected) $+ mapM_ test [Endian.BigEndian, Endian.LittleEndian]+ where test endianType =+ BinaryGet.runGet (Ewkb.ewkbGeometryType endianType) (getByteString endianType rawGeomType maybeSrid) `shouldBe` expected++getByteString :: Endian.EndianType -> Word.Word32 -> Maybe Word.Word32 -> LazyByteString.ByteString+getByteString endianType rawGeomType maybeSrid =+ ByteStringBuilder.toLazyByteString $+ case maybeSrid of+ Just srid' ->+ case endianType of+ Endian.LittleEndian ->+ ByteStringBuilder.word32LE rawGeomType <> ByteStringBuilder.word32LE srid'+ Endian.BigEndian ->+ ByteStringBuilder.word32BE rawGeomType <> ByteStringBuilder.word32BE srid'+ Nothing ->+ case endianType of+ Endian.LittleEndian ->+ ByteStringBuilder.word32LE rawGeomType+ Endian.BigEndian ->+ ByteStringBuilder.word32BE rawGeomType++expectations :: [(Word.Word32, Maybe Word.Word32, Ewkb.EwkbGeometryType)]+expectations =+ [ (0x00000000, Nothing, Ewkb.EwkbGeom (Wkb.WkbGeom Wkb.Geometry Wkb.TwoD) Ewkb.NoSrid)+ , (0x00000001, Nothing, Ewkb.EwkbGeom (Wkb.WkbGeom Wkb.Point Wkb.TwoD) Ewkb.NoSrid)+ , (0x00000002, Nothing, Ewkb.EwkbGeom (Wkb.WkbGeom Wkb.LineString Wkb.TwoD) Ewkb.NoSrid)+ , (0x00000003, Nothing, Ewkb.EwkbGeom (Wkb.WkbGeom Wkb.Polygon Wkb.TwoD) Ewkb.NoSrid)+ , (0x00000004, Nothing, Ewkb.EwkbGeom (Wkb.WkbGeom Wkb.MultiPoint Wkb.TwoD) Ewkb.NoSrid)+ , (0x00000005, Nothing, Ewkb.EwkbGeom (Wkb.WkbGeom Wkb.MultiLineString Wkb.TwoD) Ewkb.NoSrid)+ , (0x00000006, Nothing, Ewkb.EwkbGeom (Wkb.WkbGeom Wkb.MultiPolygon Wkb.TwoD) Ewkb.NoSrid)+ , (0x00000007, Nothing, Ewkb.EwkbGeom (Wkb.WkbGeom Wkb.GeometryCollection Wkb.TwoD) Ewkb.NoSrid)+ , (0x80000000, Nothing, Ewkb.EwkbGeom (Wkb.WkbGeom Wkb.Geometry Wkb.Z) Ewkb.NoSrid)+ , (0x80000001, Nothing, Ewkb.EwkbGeom (Wkb.WkbGeom Wkb.Point Wkb.Z) Ewkb.NoSrid)+ , (0x80000002, Nothing, Ewkb.EwkbGeom (Wkb.WkbGeom Wkb.LineString Wkb.Z) Ewkb.NoSrid)+ , (0x80000003, Nothing, Ewkb.EwkbGeom (Wkb.WkbGeom Wkb.Polygon Wkb.Z) Ewkb.NoSrid)+ , (0x80000004, Nothing, Ewkb.EwkbGeom (Wkb.WkbGeom Wkb.MultiPoint Wkb.Z) Ewkb.NoSrid)+ , (0x80000005, Nothing, Ewkb.EwkbGeom (Wkb.WkbGeom Wkb.MultiLineString Wkb.Z) Ewkb.NoSrid)+ , (0x80000006, Nothing, Ewkb.EwkbGeom (Wkb.WkbGeom Wkb.MultiPolygon Wkb.Z) Ewkb.NoSrid)+ , (0x80000007, Nothing, Ewkb.EwkbGeom (Wkb.WkbGeom Wkb.GeometryCollection Wkb.Z) Ewkb.NoSrid)+ , (0x40000000, Nothing, Ewkb.EwkbGeom (Wkb.WkbGeom Wkb.Geometry Wkb.M) Ewkb.NoSrid)+ , (0x40000001, Nothing, Ewkb.EwkbGeom (Wkb.WkbGeom Wkb.Point Wkb.M) Ewkb.NoSrid)+ , (0x40000002, Nothing, Ewkb.EwkbGeom (Wkb.WkbGeom Wkb.LineString Wkb.M) Ewkb.NoSrid)+ , (0x40000003, Nothing, Ewkb.EwkbGeom (Wkb.WkbGeom Wkb.Polygon Wkb.M) Ewkb.NoSrid)+ , (0x40000004, Nothing, Ewkb.EwkbGeom (Wkb.WkbGeom Wkb.MultiPoint Wkb.M) Ewkb.NoSrid)+ , (0x40000005, Nothing, Ewkb.EwkbGeom (Wkb.WkbGeom Wkb.MultiLineString Wkb.M) Ewkb.NoSrid)+ , (0x40000006, Nothing, Ewkb.EwkbGeom (Wkb.WkbGeom Wkb.MultiPolygon Wkb.M) Ewkb.NoSrid)+ , (0x40000007, Nothing, Ewkb.EwkbGeom (Wkb.WkbGeom Wkb.GeometryCollection Wkb.M) Ewkb.NoSrid)+ , (0xC0000000, Nothing, Ewkb.EwkbGeom (Wkb.WkbGeom Wkb.Geometry Wkb.ZM) Ewkb.NoSrid)+ , (0xC0000001, Nothing, Ewkb.EwkbGeom (Wkb.WkbGeom Wkb.Point Wkb.ZM) Ewkb.NoSrid)+ , (0xC0000002, Nothing, Ewkb.EwkbGeom (Wkb.WkbGeom Wkb.LineString Wkb.ZM) Ewkb.NoSrid)+ , (0xC0000003, Nothing, Ewkb.EwkbGeom (Wkb.WkbGeom Wkb.Polygon Wkb.ZM) Ewkb.NoSrid)+ , (0xC0000004, Nothing, Ewkb.EwkbGeom (Wkb.WkbGeom Wkb.MultiPoint Wkb.ZM) Ewkb.NoSrid)+ , (0xC0000005, Nothing, Ewkb.EwkbGeom (Wkb.WkbGeom Wkb.MultiLineString Wkb.ZM) Ewkb.NoSrid)+ , (0xC0000006, Nothing, Ewkb.EwkbGeom (Wkb.WkbGeom Wkb.MultiPolygon Wkb.ZM) Ewkb.NoSrid)+ , (0xC0000007, Nothing, Ewkb.EwkbGeom (Wkb.WkbGeom Wkb.GeometryCollection Wkb.ZM) Ewkb.NoSrid)+ , (0x20000000, Just srid, Ewkb.EwkbGeom (Wkb.WkbGeom Wkb.Geometry Wkb.TwoD) (Ewkb.Srid srid))+ , (0x20000001, Just srid, Ewkb.EwkbGeom (Wkb.WkbGeom Wkb.Point Wkb.TwoD) (Ewkb.Srid srid))+ , (0x20000002, Just srid, Ewkb.EwkbGeom (Wkb.WkbGeom Wkb.LineString Wkb.TwoD) (Ewkb.Srid srid))+ , (0x20000003, Just srid, Ewkb.EwkbGeom (Wkb.WkbGeom Wkb.Polygon Wkb.TwoD) (Ewkb.Srid srid))+ , (0x20000004, Just srid, Ewkb.EwkbGeom (Wkb.WkbGeom Wkb.MultiPoint Wkb.TwoD) (Ewkb.Srid srid))+ , (0x20000005, Just srid, Ewkb.EwkbGeom (Wkb.WkbGeom Wkb.MultiLineString Wkb.TwoD) (Ewkb.Srid srid))+ , (0x20000006, Just srid, Ewkb.EwkbGeom (Wkb.WkbGeom Wkb.MultiPolygon Wkb.TwoD) (Ewkb.Srid srid))+ , (0x20000007, Just srid, Ewkb.EwkbGeom (Wkb.WkbGeom Wkb.GeometryCollection Wkb.TwoD) (Ewkb.Srid srid))+ , (0xA0000000, Just srid, Ewkb.EwkbGeom (Wkb.WkbGeom Wkb.Geometry Wkb.Z) (Ewkb.Srid srid))+ , (0xA0000001, Just srid, Ewkb.EwkbGeom (Wkb.WkbGeom Wkb.Point Wkb.Z) (Ewkb.Srid srid))+ , (0xA0000002, Just srid, Ewkb.EwkbGeom (Wkb.WkbGeom Wkb.LineString Wkb.Z) (Ewkb.Srid srid))+ , (0xA0000003, Just srid, Ewkb.EwkbGeom (Wkb.WkbGeom Wkb.Polygon Wkb.Z) (Ewkb.Srid srid))+ , (0xA0000004, Just srid, Ewkb.EwkbGeom (Wkb.WkbGeom Wkb.MultiPoint Wkb.Z) (Ewkb.Srid srid))+ , (0xA0000005, Just srid, Ewkb.EwkbGeom (Wkb.WkbGeom Wkb.MultiLineString Wkb.Z) (Ewkb.Srid srid))+ , (0xA0000006, Just srid, Ewkb.EwkbGeom (Wkb.WkbGeom Wkb.MultiPolygon Wkb.Z) (Ewkb.Srid srid))+ , (0xA0000007, Just srid, Ewkb.EwkbGeom (Wkb.WkbGeom Wkb.GeometryCollection Wkb.Z) (Ewkb.Srid srid))+ , (0x60000000, Just srid, Ewkb.EwkbGeom (Wkb.WkbGeom Wkb.Geometry Wkb.M) (Ewkb.Srid srid))+ , (0x60000001, Just srid, Ewkb.EwkbGeom (Wkb.WkbGeom Wkb.Point Wkb.M) (Ewkb.Srid srid))+ , (0x60000002, Just srid, Ewkb.EwkbGeom (Wkb.WkbGeom Wkb.LineString Wkb.M) (Ewkb.Srid srid))+ , (0x60000003, Just srid, Ewkb.EwkbGeom (Wkb.WkbGeom Wkb.Polygon Wkb.M) (Ewkb.Srid srid))+ , (0x60000004, Just srid, Ewkb.EwkbGeom (Wkb.WkbGeom Wkb.MultiPoint Wkb.M) (Ewkb.Srid srid))+ , (0x60000005, Just srid, Ewkb.EwkbGeom (Wkb.WkbGeom Wkb.MultiLineString Wkb.M) (Ewkb.Srid srid))+ , (0x60000006, Just srid, Ewkb.EwkbGeom (Wkb.WkbGeom Wkb.MultiPolygon Wkb.M) (Ewkb.Srid srid))+ , (0x60000007, Just srid, Ewkb.EwkbGeom (Wkb.WkbGeom Wkb.GeometryCollection Wkb.M) (Ewkb.Srid srid))+ , (0xE0000000, Just srid, Ewkb.EwkbGeom (Wkb.WkbGeom Wkb.Geometry Wkb.ZM) (Ewkb.Srid srid))+ , (0xE0000001, Just srid, Ewkb.EwkbGeom (Wkb.WkbGeom Wkb.Point Wkb.ZM) (Ewkb.Srid srid))+ , (0xE0000002, Just srid, Ewkb.EwkbGeom (Wkb.WkbGeom Wkb.LineString Wkb.ZM) (Ewkb.Srid srid))+ , (0xE0000003, Just srid, Ewkb.EwkbGeom (Wkb.WkbGeom Wkb.Polygon Wkb.ZM) (Ewkb.Srid srid))+ , (0xE0000004, Just srid, Ewkb.EwkbGeom (Wkb.WkbGeom Wkb.MultiPoint Wkb.ZM) (Ewkb.Srid srid))+ , (0xE0000005, Just srid, Ewkb.EwkbGeom (Wkb.WkbGeom Wkb.MultiLineString Wkb.ZM) (Ewkb.Srid srid))+ , (0xE0000006, Just srid, Ewkb.EwkbGeom (Wkb.WkbGeom Wkb.MultiPolygon Wkb.ZM) (Ewkb.Srid srid))+ , (0xE0000007, Just srid, Ewkb.EwkbGeom (Wkb.WkbGeom Wkb.GeometryCollection Wkb.ZM) (Ewkb.Srid srid))+ ]++srid :: Word.Word32+srid = 4326
+ test/Data/Internal/Wkb/EndianSpec.hs view
@@ -0,0 +1,29 @@+{-# LANGUAGE OverloadedStrings #-}++module Data.Internal.Wkb.EndianSpec where++import qualified Data.Binary.Get as BinaryGet+import qualified Data.ByteString.Builder as ByteStringBuilder+import qualified Data.ByteString.Lazy as LazyByteString+import qualified GHC.Word as Word+import Test.Hspec (Spec, describe, it, shouldBe)++import qualified Data.Internal.Wkb.Endian as Endian++spec :: Spec+spec =+ testGetEndian++testGetEndian :: Spec+testGetEndian =+ describe "get endian" $ do+ it "Returns BigEndian for 0" $+ BinaryGet.runGet Endian.endianType (getByteString 0) `shouldBe` Endian.BigEndian+ it "Returns LittleEndian for 1" $+ BinaryGet.runGet Endian.endianType (getByteString 1) `shouldBe` Endian.LittleEndian+ it "Returns fail for other" $+ BinaryGet.runGetOrFail Endian.endianType (getByteString 5) `shouldBe` Left ("", 1, "Invalid EndianType")++getByteString :: Word.Word8 -> LazyByteString.ByteString+getByteString int =+ ByteStringBuilder.toLazyByteString $ ByteStringBuilder.word8 int
+ test/Data/Internal/Wkb/GeometrySpec.hs view
@@ -0,0 +1,73 @@+{-# LANGUAGE OverloadedStrings #-}++module Data.Internal.Wkb.GeometrySpec where++import qualified Data.Binary.Get as BinaryGet+import qualified Data.ByteString.Builder as ByteStringBuilder+import qualified Data.ByteString.Lazy as LazyByteString+import qualified Data.Word as Word+import Test.Hspec (Spec, describe, it, shouldBe)++import qualified Data.Internal.Wkb.Endian as Endian+import qualified Data.Internal.Wkb.Geometry as Geometry++spec :: Spec+spec =+ testValidGetGeometryTypeWithCoords++testValidGetGeometryTypeWithCoords :: Spec+testValidGetGeometryTypeWithCoords =+ describe "get geometry type with coords type for valid data" $+ mapM_ testGetGeometryTypeWithCoords expectations++testGetGeometryTypeWithCoords :: (Word.Word32, Geometry.WkbGeometryType) -> Spec+testGetGeometryTypeWithCoords (int, expected) =+ it ("Parse " ++ show expected) $+ mapM_ test [Endian.BigEndian, Endian.LittleEndian]+ where test endianType =+ BinaryGet.runGet (Geometry.geometryTypeWithCoords endianType) (getByteString endianType int) `shouldBe` expected++getByteString :: Endian.EndianType -> Word.Word32 -> LazyByteString.ByteString+getByteString endianType int =+ ByteStringBuilder.toLazyByteString $+ case endianType of+ Endian.LittleEndian ->+ ByteStringBuilder.word32LE int+ Endian.BigEndian ->+ ByteStringBuilder.word32BE int++expectations :: [(Word.Word32, Geometry.WkbGeometryType)]+expectations =+ [ (0000, Geometry.WkbGeom Geometry.Geometry Geometry.TwoD)+ , (0001, Geometry.WkbGeom Geometry.Point Geometry.TwoD)+ , (0002, Geometry.WkbGeom Geometry.LineString Geometry.TwoD)+ , (0003, Geometry.WkbGeom Geometry.Polygon Geometry.TwoD)+ , (0004, Geometry.WkbGeom Geometry.MultiPoint Geometry.TwoD)+ , (0005, Geometry.WkbGeom Geometry.MultiLineString Geometry.TwoD)+ , (0006, Geometry.WkbGeom Geometry.MultiPolygon Geometry.TwoD)+ , (0007, Geometry.WkbGeom Geometry.GeometryCollection Geometry.TwoD)+ , (1000, Geometry.WkbGeom Geometry.Geometry Geometry.Z)+ , (1001, Geometry.WkbGeom Geometry.Point Geometry.Z)+ , (1002, Geometry.WkbGeom Geometry.LineString Geometry.Z)+ , (1003, Geometry.WkbGeom Geometry.Polygon Geometry.Z)+ , (1004, Geometry.WkbGeom Geometry.MultiPoint Geometry.Z)+ , (1005, Geometry.WkbGeom Geometry.MultiLineString Geometry.Z)+ , (1006, Geometry.WkbGeom Geometry.MultiPolygon Geometry.Z)+ , (1007, Geometry.WkbGeom Geometry.GeometryCollection Geometry.Z)+ , (2000, Geometry.WkbGeom Geometry.Geometry Geometry.M)+ , (2001, Geometry.WkbGeom Geometry.Point Geometry.M)+ , (2002, Geometry.WkbGeom Geometry.LineString Geometry.M)+ , (2003, Geometry.WkbGeom Geometry.Polygon Geometry.M)+ , (2004, Geometry.WkbGeom Geometry.MultiPoint Geometry.M)+ , (2005, Geometry.WkbGeom Geometry.MultiLineString Geometry.M)+ , (2006, Geometry.WkbGeom Geometry.MultiPolygon Geometry.M)+ , (2007, Geometry.WkbGeom Geometry.GeometryCollection Geometry.M)+ , (3000, Geometry.WkbGeom Geometry.Geometry Geometry.ZM)+ , (3001, Geometry.WkbGeom Geometry.Point Geometry.ZM)+ , (3002, Geometry.WkbGeom Geometry.LineString Geometry.ZM)+ , (3003, Geometry.WkbGeom Geometry.Polygon Geometry.ZM)+ , (3004, Geometry.WkbGeom Geometry.MultiPoint Geometry.ZM)+ , (3005, Geometry.WkbGeom Geometry.MultiLineString Geometry.ZM)+ , (3006, Geometry.WkbGeom Geometry.MultiPolygon Geometry.ZM)+ , (3007, Geometry.WkbGeom Geometry.GeometryCollection Geometry.ZM)+ ]
+ test/Data/SpecHelper.hs view
@@ -0,0 +1,30 @@+module Data.SpecHelper where++import qualified Data.Geospatial as Geospatial+import qualified Data.LinearRing as LinearRing+import qualified Data.LineString as LineString+import qualified Data.Vector.Storable as VectorStorable++point1 :: Geospatial.GeoPositionWithoutCRS+point1 = Geospatial.GeoPointXY (Geospatial.PointXY 1.0 2.0)++point2 :: Geospatial.GeoPositionWithoutCRS+point2 = Geospatial.GeoPointXY (Geospatial.PointXY 3.0 4.0)++lineString1 :: LineString.LineString Geospatial.GeoPositionWithoutCRS+lineString1 = LineString.makeLineString (Geospatial.GeoPointXY (Geospatial.PointXY 1.0 2.0)) (Geospatial.GeoPointXY (Geospatial.PointXY 3.0 4.0)) VectorStorable.empty++lineString2 :: LineString.LineString Geospatial.GeoPositionWithoutCRS+lineString2 = LineString.makeLineString (Geospatial.GeoPointXY (Geospatial.PointXY 1.5 2.5)) (Geospatial.GeoPointXY (Geospatial.PointXY 3.5 4.5)) VectorStorable.empty++lineString3 :: LineString.LineString Geospatial.GeoPositionWithoutCRS+lineString3 = LineString.makeLineString (Geospatial.GeoPointXY (Geospatial.PointXY 15 15)) (Geospatial.GeoPointXY (Geospatial.PointXY 20 20)) (VectorStorable.singleton (Geospatial.GeoPointXY (Geospatial.PointXY 20 20)))++lineString4 :: LineString.LineString Geospatial.GeoPositionWithoutCRS+lineString4 = LineString.makeLineString (Geospatial.GeoPointXY (Geospatial.PointXY 15 15)) (Geospatial.GeoPointXY (Geospatial.PointXY 20 20)) (VectorStorable.singleton (Geospatial.GeoPointXY (Geospatial.PointXY 25 25)))++linearRing1 :: LinearRing.LinearRing Geospatial.GeoPositionWithoutCRS+linearRing1 = LinearRing.makeLinearRing (Geospatial.GeoPointXY (Geospatial.PointXY 1.0 2.0)) (Geospatial.GeoPointXY (Geospatial.PointXY 3.0 4.0)) (Geospatial.GeoPointXY (Geospatial.PointXY 5.0 6.0)) VectorStorable.empty++linearRing2 :: LinearRing.LinearRing Geospatial.GeoPositionWithoutCRS+linearRing2 = LinearRing.makeLinearRing (Geospatial.GeoPointXY (Geospatial.PointXY 1.5 2.5)) (Geospatial.GeoPointXY (Geospatial.PointXY 3.5 4.5)) (Geospatial.GeoPointXY (Geospatial.PointXY 5.5 6.5)) VectorStorable.empty
+ test/Data/WkbGeometryCollectionSpec.hs view
@@ -0,0 +1,54 @@++{-# LANGUAGE OverloadedStrings #-}++module Data.WkbGeometryCollectionSpec where++import qualified Data.ByteString.Builder as ByteStringBuilder+import qualified Data.ByteString.Lazy as LazyByteString+import qualified Data.Geospatial as Geospatial+import Data.Monoid ((<>))+import qualified Data.Vector as Vector+import qualified Data.Wkb as Wkb+import Test.Hspec (Spec, describe, it, shouldBe)++import qualified Data.SpecHelper as SpecHelper++spec :: Spec+spec =+ testWkbGeometryCollectionParsing++testWkbGeometryCollectionParsing :: Spec+testWkbGeometryCollectionParsing =+ describe "Test wkb geometry collection" $+ it "Parse valid wkb geometry collection" $+ Wkb.parseByteString exampleWkbGeometryCollection `shouldBe` (Right . Geospatial.Collection $+ Vector.fromList+ [ Geospatial.Point $ Geospatial.GeoPoint SpecHelper.point1+ , Geospatial.Point $ Geospatial.GeoPoint SpecHelper.point2+ , Geospatial.Line $ Geospatial.GeoLine SpecHelper.lineString3+ ]+ )++exampleWkbGeometryCollection :: LazyByteString.ByteString+exampleWkbGeometryCollection =+ ByteStringBuilder.toLazyByteString $+ ByteStringBuilder.word8 0+ <> ByteStringBuilder.int32BE 7+ <> ByteStringBuilder.int32BE 3+ <> ByteStringBuilder.word8 0+ <> ByteStringBuilder.int32BE 1+ <> ByteStringBuilder.doubleBE 1+ <> ByteStringBuilder.doubleBE 2+ <> ByteStringBuilder.word8 0+ <> ByteStringBuilder.int32BE 1+ <> ByteStringBuilder.doubleBE 3+ <> ByteStringBuilder.doubleBE 4+ <> ByteStringBuilder.word8 0+ <> ByteStringBuilder.int32BE 2+ <> ByteStringBuilder.int32BE 3+ <> ByteStringBuilder.doubleBE 15+ <> ByteStringBuilder.doubleBE 15+ <> ByteStringBuilder.doubleBE 20+ <> ByteStringBuilder.doubleBE 20+ <> ByteStringBuilder.doubleBE 20+ <> ByteStringBuilder.doubleBE 20
+ test/Data/WkbLineSpec.hs view
@@ -0,0 +1,63 @@+{-# LANGUAGE OverloadedStrings #-}++module Data.WkbLineSpec where++import qualified Data.ByteString.Builder as ByteStringBuilder+import qualified Data.ByteString.Lazy as LazyByteString+import qualified Data.Geospatial as Geospatial+import Data.Monoid ((<>))+import qualified Data.Vector as Vector+import Test.Hspec (Spec, describe, it, shouldBe)++import qualified Data.Wkb as Wkb++import qualified Data.SpecHelper as SpecHelper++spec :: Spec+spec = do+ testWkbLineParsing+ testWkbMultiLineParsing++testWkbLineParsing :: Spec+testWkbLineParsing =+ describe "Test wkb line" $+ it "Parse valid wkb line" $+ Wkb.parseByteString exampleWkbLine `shouldBe` (Right $ Geospatial.Line $ Geospatial.GeoLine SpecHelper.lineString1)++exampleWkbLine :: LazyByteString.ByteString+exampleWkbLine =+ ByteStringBuilder.toLazyByteString $+ ByteStringBuilder.word8 0+ <> ByteStringBuilder.int32BE 2+ <> ByteStringBuilder.int32BE 2+ <> ByteStringBuilder.doubleBE 1.0+ <> ByteStringBuilder.doubleBE 2.0+ <> ByteStringBuilder.doubleBE 3.0+ <> ByteStringBuilder.doubleBE 4.0++testWkbMultiLineParsing :: Spec+testWkbMultiLineParsing =+ describe "Test wkb multi line" $+ it "Parse valid wkb multi line" $+ Wkb.parseByteString exampleWkbMultiLine `shouldBe` (Right $ Geospatial.MultiLine $ Geospatial.GeoMultiLine (Vector.fromList [SpecHelper.lineString1, SpecHelper.lineString2]))++exampleWkbMultiLine :: LazyByteString.ByteString+exampleWkbMultiLine =+ ByteStringBuilder.toLazyByteString $+ ByteStringBuilder.word8 0+ <> ByteStringBuilder.int32BE 5+ <> ByteStringBuilder.int32BE 2+ <> ByteStringBuilder.word8 0+ <> ByteStringBuilder.int32BE 2+ <> ByteStringBuilder.int32BE 2+ <> ByteStringBuilder.doubleBE 1.0+ <> ByteStringBuilder.doubleBE 2.0+ <> ByteStringBuilder.doubleBE 3.0+ <> ByteStringBuilder.doubleBE 4.0+ <> ByteStringBuilder.word8 0+ <> ByteStringBuilder.int32BE 2+ <> ByteStringBuilder.int32BE 2+ <> ByteStringBuilder.doubleBE 1.5+ <> ByteStringBuilder.doubleBE 2.5+ <> ByteStringBuilder.doubleBE 3.5+ <> ByteStringBuilder.doubleBE 4.5
+ test/Data/WkbPointSpec.hs view
@@ -0,0 +1,55 @@+{-# LANGUAGE OverloadedStrings #-}++module Data.WkbPointSpec where++import qualified Data.ByteString.Builder as ByteStringBuilder+import qualified Data.ByteString.Lazy as LazyByteString+import qualified Data.Geospatial as Geospatial+import Data.Monoid ((<>))+import qualified Data.Vector as Vector+import Test.Hspec (Spec, describe, it, shouldBe)++import qualified Data.Wkb as Wkb++import qualified Data.SpecHelper as SpecHelper++spec :: Spec+spec = do+ testWkbPointParsing+ testWkbMultiPointParsing++testWkbPointParsing :: Spec+testWkbPointParsing =+ describe "Test wkb point" $+ it "Parse valid wkb point" $+ Wkb.parseByteString exampleWkbPoint `shouldBe` (Right . Geospatial.Point $ Geospatial.GeoPoint SpecHelper.point1)++exampleWkbPoint :: LazyByteString.ByteString+exampleWkbPoint =+ ByteStringBuilder.toLazyByteString $+ ByteStringBuilder.word8 0+ <> ByteStringBuilder.int32BE 1+ <> ByteStringBuilder.doubleBE 1.0+ <> ByteStringBuilder.doubleBE 2.0++testWkbMultiPointParsing :: Spec+testWkbMultiPointParsing =+ describe "Test wkb multi point" $+ it "Parse valid wkb multi point" $+ Wkb.parseByteString exampleWkbMultiPoint `shouldBe` (Right $ Geospatial.MultiPoint (Geospatial.GeoMultiPoint (Vector.fromList [SpecHelper.point1, SpecHelper.point2])))++exampleWkbMultiPoint :: LazyByteString.ByteString+exampleWkbMultiPoint =+ ByteStringBuilder.toLazyByteString $+ ByteStringBuilder.word8 0+ <> ByteStringBuilder.int32BE 4+ <> ByteStringBuilder.int32BE 2+ <> ByteStringBuilder.word8 0+ <> ByteStringBuilder.int32BE 1+ <> ByteStringBuilder.doubleBE 1.0+ <> ByteStringBuilder.doubleBE 2.0+ <> ByteStringBuilder.word8 0+ <> ByteStringBuilder.int32BE 1+ <> ByteStringBuilder.doubleBE 3.0+ <> ByteStringBuilder.doubleBE 4.0+
+ test/Data/WkbPolygonSpec.hs view
@@ -0,0 +1,100 @@+{-# LANGUAGE OverloadedStrings #-}++module Data.WkbPolygonSpec where++import qualified Data.ByteString.Builder as ByteStringBuilder+import qualified Data.ByteString.Lazy as LazyByteString+import qualified Data.Geospatial as Geospatial+import Data.Monoid ((<>))+import qualified Data.Vector as Vector+import Test.Hspec (Spec, describe, it, shouldBe)++import qualified Data.Wkb as Wkb++import qualified Data.SpecHelper as SpecHelper++spec :: Spec+spec = do+ testWkbPolygonParsing+ testWkbMultiPolygonParsing++testWkbPolygonParsing :: Spec+testWkbPolygonParsing =+ describe "Test wkb polygon" $ do+ it "Parse valid wkb polygon" $+ Wkb.parseByteString exampleWkbPolygon `shouldBe` (Right $ Geospatial.Polygon $ Geospatial.GeoPolygon (Vector.singleton SpecHelper.linearRing1))+ it "Not parse bad wkb polygon" $+ Wkb.parseByteString exampleBadWkbPolygon `shouldBe` Left "Could not parse wkb: First and last points of linear ring are different: first=GeoPointXY (PointXY {_xyX = 1.0, _xyY = 2.0}) last=GeoPointXY (PointXY {_xyX = 7.0, _xyY = 8.0})"++exampleWkbPolygon :: LazyByteString.ByteString+exampleWkbPolygon =+ ByteStringBuilder.toLazyByteString $+ ByteStringBuilder.word8 0+ <> ByteStringBuilder.int32BE 3+ <> ByteStringBuilder.int32BE 1+ <> ByteStringBuilder.int32BE 4+ <> ByteStringBuilder.doubleBE 1.0+ <> ByteStringBuilder.doubleBE 2.0+ <> ByteStringBuilder.doubleBE 3.0+ <> ByteStringBuilder.doubleBE 4.0+ <> ByteStringBuilder.doubleBE 5.0+ <> ByteStringBuilder.doubleBE 6.0+ <> ByteStringBuilder.doubleBE 1.0+ <> ByteStringBuilder.doubleBE 2.0++exampleBadWkbPolygon :: LazyByteString.ByteString+exampleBadWkbPolygon =+ ByteStringBuilder.toLazyByteString $+ ByteStringBuilder.word8 0+ <> ByteStringBuilder.int32BE 3+ <> ByteStringBuilder.int32BE 1+ <> ByteStringBuilder.int32BE 4+ <> ByteStringBuilder.doubleBE 1.0+ <> ByteStringBuilder.doubleBE 2.0+ <> ByteStringBuilder.doubleBE 3.0+ <> ByteStringBuilder.doubleBE 4.0+ <> ByteStringBuilder.doubleBE 5.0+ <> ByteStringBuilder.doubleBE 6.0+ <> ByteStringBuilder.doubleBE 7.0+ <> ByteStringBuilder.doubleBE 8.0++testWkbMultiPolygonParsing :: Spec+testWkbMultiPolygonParsing =+ describe "Test wkb multi polygon" $+ it "Parse valid wkb multi polygon" $+ Wkb.parseByteString exampleWkbMultiPolygon `shouldBe` (Right $ Geospatial.MultiPolygon $ Geospatial.GeoMultiPolygon+ (Vector.fromList+ [ Vector.singleton SpecHelper.linearRing1+ , Vector.singleton SpecHelper.linearRing2+ ]))++exampleWkbMultiPolygon :: LazyByteString.ByteString+exampleWkbMultiPolygon =+ ByteStringBuilder.toLazyByteString $+ ByteStringBuilder.word8 0+ <> ByteStringBuilder.int32BE 6+ <> ByteStringBuilder.int32BE 2+ <> ByteStringBuilder.word8 0+ <> ByteStringBuilder.int32BE 3+ <> ByteStringBuilder.int32BE 1+ <> ByteStringBuilder.int32BE 4+ <> ByteStringBuilder.doubleBE 1.0+ <> ByteStringBuilder.doubleBE 2.0+ <> ByteStringBuilder.doubleBE 3.0+ <> ByteStringBuilder.doubleBE 4.0+ <> ByteStringBuilder.doubleBE 5.0+ <> ByteStringBuilder.doubleBE 6.0+ <> ByteStringBuilder.doubleBE 1.0+ <> ByteStringBuilder.doubleBE 2.0+ <> ByteStringBuilder.word8 0+ <> ByteStringBuilder.int32BE 3+ <> ByteStringBuilder.int32BE 1+ <> ByteStringBuilder.int32BE 4+ <> ByteStringBuilder.doubleBE 1.5+ <> ByteStringBuilder.doubleBE 2.5+ <> ByteStringBuilder.doubleBE 3.5+ <> ByteStringBuilder.doubleBE 4.5+ <> ByteStringBuilder.doubleBE 5.5+ <> ByteStringBuilder.doubleBE 6.5+ <> ByteStringBuilder.doubleBE 1.5+ <> ByteStringBuilder.doubleBE 2.5
+ test/Data/WktGeometryCollectionSpec.hs view
@@ -0,0 +1,34 @@+{-# LANGUAGE OverloadedStrings #-}++module Data.WktGeometryCollectionSpec where++import Control.Lens ((^?!))+import qualified Data.Geospatial as Geospatial+import qualified Data.Vector as Vector+import Test.Hspec (Spec, describe, it, shouldBe)+import qualified Text.Trifecta as Trifecta++import qualified Data.Wkt as Wkt++import qualified Data.SpecHelper as SpecHelper++spec :: Spec+spec = testGeometryCollection++testGeometryCollection :: Spec+testGeometryCollection =+ describe "simple geometry collections" $ do+ it "Parse empty" $+ Wkt.parseString Wkt.geometryCollection "geometrycollection empty" ^?! Trifecta._Success `shouldBe` Wkt.emptyGeometryCollection+ it "Parse something" $+ Wkt.parseString Wkt.geometryCollection "GeometryCollection(POINT (1 2),POINT (3 4),LINESTRING (15 15, 20 20, 25 25))" ^?! Trifecta._Success `shouldBe` exampleGeometryCollection+ it "Parse something with spaces" $+ Wkt.parseString Wkt.geometryCollection "GeometryCollection ( POINT ( 1 2) , POINT (3 4) , LINESTRING (15 15 , 20 20, 25 25 ) ) " ^?! Trifecta._Success `shouldBe` exampleGeometryCollection++exampleGeometryCollection :: Vector.Vector Geospatial.GeospatialGeometry+exampleGeometryCollection =+ Vector.fromList+ [ Geospatial.Point $ Geospatial.GeoPoint SpecHelper.point1+ , Geospatial.Point $ Geospatial.GeoPoint SpecHelper.point2+ , Geospatial.Line $ Geospatial.GeoLine SpecHelper.lineString4+ ]
+ test/Data/WktLineSpec.hs view
@@ -0,0 +1,57 @@+{-# LANGUAGE OverloadedStrings #-}++module Data.WktLineSpec where++import Control.Lens ((^?), (^?!))+import qualified Data.Geospatial as Geospatial+import qualified Data.LineString as LineString+import qualified Data.Maybe as Maybe+import qualified Data.Vector as Vector+import qualified Data.Vector.Storable as VectorStorable+import Test.Hspec (Spec, describe, it, shouldBe,+ shouldSatisfy)+import qualified Text.Trifecta as Trifecta++import qualified Data.Wkt as Wkt++spec :: Spec+spec = do+ testLines+ testMultiLines++testLines :: Spec+testLines =+ describe "simple lines" $ do+ it "Parse incomplete" $+ Wkt.parseString Wkt.lineString "linestring" `shouldSatisfy` (Maybe.isJust . flip (^?) Trifecta._Failure)+ it "Parse empty" $+ Wkt.parseString Wkt.lineString "linestring empty" ^?! Trifecta._Success `shouldBe` Wkt.emptyLine+ it "Parse not points" $+ Wkt.parseString Wkt.lineString "linestring (abc)" `shouldSatisfy` (Maybe.isJust . flip (^?) Trifecta._Failure)+ it "Parse something" $+ Wkt.parseString Wkt.lineString "linestring (1.0 2.0, 1.0 2.5, 1.0 3.0)" ^?! Trifecta._Success `shouldBe` exampleLine+ it "Parse spaces" $+ Wkt.parseString Wkt.lineString "linestring ( 1.0 2.0,1.0 2.5, 1.0 3.0)" ^?! Trifecta._Success `shouldBe` exampleLine++testMultiLines :: Spec+testMultiLines =+ describe "simple multilines" $ do+ it "Parse incomplete" $+ Wkt.parseString Wkt.multiLineString "multilinestring" `shouldSatisfy` (Maybe.isJust . flip (^?) Trifecta._Failure)+ it "Parse empty" $+ Wkt.parseString Wkt.multiLineString "multilinestring empty" ^?! Trifecta._Success `shouldBe` Wkt.emptyMultiLine+ it "Parse not points" $+ Wkt.parseString Wkt.multiLineString "multilinestring (abc)" `shouldSatisfy` (Maybe.isJust . flip (^?) Trifecta._Failure)+ it "Parse something" $+ Wkt.parseString Wkt.multiLineString "multilinestring ((1.0 2.0, 1.0 2.5, 1.0 3.0))" ^?! Trifecta._Success `shouldBe` exampleMultiLine+ it "Parse spaces" $+ Wkt.parseString Wkt.multiLineString "multilinestring ( ( 1.0 2.0,1.0 2.5, 1.0 3.0) )" ^?! Trifecta._Success `shouldBe` exampleMultiLine++exampleLine :: Geospatial.GeoLine+exampleLine = Geospatial.GeoLine exampleLineString++exampleMultiLine :: Geospatial.GeoMultiLine+exampleMultiLine = Geospatial.GeoMultiLine (Vector.singleton exampleLineString)++exampleLineString :: LineString.LineString Geospatial.GeoPositionWithoutCRS+exampleLineString = LineString.makeLineString (Geospatial.GeoPointXY (Geospatial.PointXY 1.0 2.0)) (Geospatial.GeoPointXY (Geospatial.PointXY 1.0 2.5)) (VectorStorable.singleton (Geospatial.GeoPointXY (Geospatial.PointXY 1.0 3.0)))
+ test/Data/WktPointSpec.hs view
@@ -0,0 +1,60 @@+{-# LANGUAGE OverloadedStrings #-}++module Data.WktPointSpec where++import Control.Lens ((^?), (^?!))+import qualified Data.Geospatial as Geospatial+import qualified Data.Maybe as Maybe+import qualified Data.Vector as Vector+import Test.Hspec (Spec, describe, expectationFailure, it,+ shouldBe, shouldSatisfy)+import qualified Text.Trifecta as Trifecta++import qualified Data.Wkt as Wkt++spec :: Spec+spec = do+ testPoints+ testMultiPoints++testPoints :: Spec+testPoints =+ describe "simple points" $ do+ it "Parse incomplete" $+ Wkt.parseString Wkt.point "point" `shouldSatisfy` (Maybe.isJust . flip (^?) Trifecta._Failure)+ it "Parse empty" $+ Wkt.parseString Wkt.point "point empty" ^?! Trifecta._Success `shouldBe` Wkt.emptyPoint+ it "Parse not points" $+ Wkt.parseString Wkt.point "point (abc)" `shouldSatisfy` (Maybe.isJust . flip (^?) Trifecta._Failure)+ it "Parse something" $ do+ --Wkt.parseString Wkt.point "point (1.0 2.0)" ^?! Trifecta._Success `shouldBe` examplePoint+ let x = Wkt.parseString Wkt.point "point (1.0 2.0)"+ case x of+ Trifecta.Success a -> a `shouldBe` examplePoint+ Trifecta.Failure f -> expectationFailure (show f)+ it "Parse spaces" $+ Wkt.parseString Wkt.point "point( 1.0 2.0 )" ^?! Trifecta._Success `shouldBe` examplePoint++testMultiPoints :: Spec+testMultiPoints =+ describe "simple multipoints" $ do+ it "Parse incomplete" $+ Wkt.parseString Wkt.multiPoint "multipoint" `shouldSatisfy` (Maybe.isJust . flip (^?) Trifecta._Failure)+ it "Parse empty" $+ Wkt.parseString Wkt.multiPoint "multipoint empty" ^?! Trifecta._Success `shouldBe` Wkt.emptyMultiPoint+ it "Parse not points" $+ Wkt.parseString Wkt.multiPoint "multipoint (abc)" `shouldSatisfy` (Maybe.isJust . flip (^?) Trifecta._Failure)+ it "Parse unbracketed" $+ Wkt.parseString Wkt.multiPoint "multipoint (1.0 2.0, 2.0 2.0)" ^?! Trifecta._Success `shouldBe` exampleMultiPoint+ it "Parse unbracketed spaces" $+ Wkt.parseString Wkt.multiPoint "multipoint( 1.0 2.0,2.0 2.0 )" ^?! Trifecta._Success `shouldBe` exampleMultiPoint+ it "Parse bracketed" $+ Wkt.parseString Wkt.multiPoint "multipoint ((1.0 2.0), (2.0 2.0))" ^?! Trifecta._Success `shouldBe` exampleMultiPoint+ it "Parse bracketed spaces" $+ Wkt.parseString Wkt.multiPoint "multipoint( ( 1.0 2.0) ,( 2.0 2.0) )" ^?! Trifecta._Success `shouldBe` exampleMultiPoint++examplePoint :: Geospatial.GeoPoint+examplePoint = Geospatial.GeoPoint (Geospatial.GeoPointXY (Geospatial.PointXY 1.0 2.0))++exampleMultiPoint :: Geospatial.GeoMultiPoint+exampleMultiPoint = Geospatial.GeoMultiPoint (Vector.fromList [Geospatial.GeoPointXY (Geospatial.PointXY 1.0 2.0), Geospatial.GeoPointXY (Geospatial.PointXY 2.0 2.0)])
+ test/Data/WktPolygonSpec.hs view
@@ -0,0 +1,64 @@+{-# LANGUAGE OverloadedStrings #-}++module Data.WktPolygonSpec where++import Control.Lens ((^?!))+import qualified Data.Geospatial as Geospatial+import qualified Data.LinearRing as LinearRing+import qualified Data.Vector as Vector+import qualified Data.Vector.Storable as VectorStorable+import Test.Hspec (Spec, describe, it, shouldBe)+import qualified Text.Trifecta as Trifecta++import qualified Data.Wkt as Wkt++spec :: Spec+spec = do+ testPolygons+ testMultiPolygons++testPolygons :: Spec+testPolygons =+ describe "simple polygons" $ do+ it "Parse empty" $+ Wkt.parseString Wkt.polygon "polygon empty" ^?! Trifecta._Success `shouldBe` Wkt.emptyPolygon+ it "Parse something" $+ Wkt.parseString Wkt.polygon "polygon ((4.0 0.0, 0.0 4.0, -4.0 0.0, 0.0 -4.0, 4.0 0.0))" ^?! Trifecta._Success `shouldBe` examplePolygon+ it "Parse spaces" $+ Wkt.parseString Wkt.polygon "polygon ( (4.0 0.0, 0.0 4.0, -4.0 0.0 , 0.0 -4.0 , 4.0 0.0))" ^?! Trifecta._Success `shouldBe` examplePolygon+ it "Parse something with hole" $+ Wkt.parseString Wkt.polygon "polygon ((4.0 0.0, 0.0 4.0, -4.0 0.0, 0.0 -4.0, 4.0 0.0),(2.0 0.0, 0.0 2.0, -2.0 0.0, 0.0 -2.0, 2.0 0.0))" ^?! Trifecta._Success `shouldBe` examplePolygonWithHole+ it "Parse something with hole spaces" $+ Wkt.parseString Wkt.polygon "polygon ( (4.0 0.0, 0.0 4.0, -4.0 0.0 , 0.0 -4.0 , 4.0 0.0),( 2.0 0.0, 0.0 2.0 , -2.0 0.0, 0.0 -2.0, 2.0 0.0))" ^?! Trifecta._Success `shouldBe` examplePolygonWithHole++testMultiPolygons :: Spec+testMultiPolygons =+ describe "simple multipolygons" $ do+ it "Parse empty" $+ Wkt.parseString Wkt.multiPolygon "multipolygon empty" ^?! Trifecta._Success `shouldBe` Wkt.emptyMultiPolygon+ it "Parse something" $+ Wkt.parseString Wkt.multiPolygon "multipolygon (((4.0 0.0, 0.0 4.0, -4.0 0.0, 0.0 -4.0, 4.0 0.0)))" ^?! Trifecta._Success `shouldBe` exampleMultiPolygon+ it "Parse something with hole" $+ Wkt.parseString Wkt.multiPolygon "multipolygon (( (4.0 0.0, 0.0 4.0, -4.0 0.0 , 0.0 -4.0 , 4.0 0.0),( 2.0 0.0, 0.0 2.0 , -2.0 0.0, 0.0 -2.0, 2.0 0.0)))" ^?! Trifecta._Success `shouldBe` exampleMultiPolygonWithHole++examplePolygon :: Geospatial.GeoPolygon+examplePolygon =+ Geospatial.GeoPolygon (Vector.singleton linearRingSingle)++examplePolygonWithHole :: Geospatial.GeoPolygon+examplePolygonWithHole = Geospatial.GeoPolygon linearRingDouble++linearRingSingle :: LinearRing.LinearRing Geospatial.GeoPositionWithoutCRS+linearRingSingle = LinearRing.makeLinearRing (Geospatial.GeoPointXY (Geospatial.PointXY 4.0 0.0)) (Geospatial.GeoPointXY (Geospatial.PointXY 0.0 4.0)) (Geospatial.GeoPointXY (Geospatial.PointXY (-4.0) 0.0)) (VectorStorable.fromList [Geospatial.GeoPointXY (Geospatial.PointXY 0.0 (-4.0))])++linearRingDouble :: Vector.Vector (LinearRing.LinearRing Geospatial.GeoPositionWithoutCRS)+linearRingDouble = Vector.fromList+ [ linearRingSingle+ , LinearRing.makeLinearRing (Geospatial.GeoPointXY (Geospatial.PointXY 2.0 0.0)) (Geospatial.GeoPointXY (Geospatial.PointXY 0.0 2.0)) (Geospatial.GeoPointXY (Geospatial.PointXY (-2.0) 0.0)) (VectorStorable.fromList [ Geospatial.GeoPointXY (Geospatial.PointXY 0.0 (-2.0))])+ ]++exampleMultiPolygon :: Geospatial.GeoMultiPolygon+exampleMultiPolygon = Geospatial.GeoMultiPolygon (Vector.singleton (Vector.singleton linearRingSingle))++exampleMultiPolygonWithHole :: Geospatial.GeoMultiPolygon+exampleMultiPolygonWithHole = Geospatial.GeoMultiPolygon (Vector.singleton linearRingDouble)
+ test/Spec.hs view
@@ -0,0 +1,1 @@+{-# OPTIONS_GHC -F -pgmF hspec-discover #-}
+ wkt-geom.cabal view
@@ -0,0 +1,84 @@+name: wkt-geom+version: 0.0.1+synopsis: A parser of WKT, WKB and eWKB.+description: Well Known Text (WKT), Well Known Binary and the Postgresql extension Extended Well Know Binary (eWKB) are vector geometry representations.+ .+ The text or binary representations are parsed and turned into their GeoJSON representations. The binary representations use vectors throughout+ whereas the text representation still uses a list and then is converted to a vector representation.+homepage: https://github.com/indicatrix/wkt-geom#readme+license: Apache-2.0+license-file: LICENSE+author: Andrew Newmnan, Dominic Endrei+maintainer: andrewfnewman@gmail.com+copyright: 2017-2018 Wkt-Geom Project+category: Data+build-type: Simple+extra-source-files: README.md+cabal-version: >=1.10++library+ hs-source-dirs: src+ exposed-modules: Data.Ewkb+ , Data.Wkb+ , Data.Wkt+ , Data.Internal.Ewkb.Geometry + , Data.Internal.Wkb.Endian+ , Data.Internal.Wkb.Geometry+ , Data.Internal.Wkb.GeometryCollection+ , Data.Internal.Wkb.Geospatial+ , Data.Internal.Wkb.Line+ , Data.Internal.Wkb.Point+ , Data.Internal.Wkb.Polygon+ , Data.Internal.Wkt.Common+ , Data.Internal.Wkt.Line+ , Data.Internal.Wkt.Point+ , Data.Internal.Wkt.Polygon+ , Data.Internal.Wkt.GeometryCollection+ ++ build-depends: base < 5 && >= 4.8 + , binary >= 0.8+ , bytestring >= 0.10+ , geojson >= 2.0.0+ , scientific >= 0.3.6+ , trifecta >= 1.7+ , utf8-string >= 1+ , vector >= 0.10++ ghc-options: -Wall+ default-language: Haskell2010++test-suite wkt-geom-test+ type: exitcode-stdio-1.0+ hs-source-dirs: test+ other-modules: Data.Internal.Ewkb.GeometrySpec+ , Data.Internal.Wkb.EndianSpec+ , Data.Internal.Wkb.GeometrySpec+ , Data.WkbGeometryCollectionSpec+ , Data.WkbLineSpec+ , Data.WkbPointSpec+ , Data.WkbPolygonSpec+ , Data.WktGeometryCollectionSpec+ , Data.WktLineSpec+ , Data.WktPointSpec+ , Data.WktPolygonSpec + , Data.SpecHelper + main-is: Spec.hs+ build-depends: base+ , binary >= 0.8+ , ansi-wl-pprint >= 0.6+ , bytestring >= 0.10+ , geojson >= 2.0.0+ , hspec+ , lens+ , QuickCheck+ , scientific >= 0.3.6+ , trifecta >= 1.7+ , vector >= 0.10 + , wkt-geom+ ghc-options: -Wall+ default-language: Haskell2010++source-repository head+ type: git+ location: https://github.com/githubuser/wkt-geom