Aeson parsing of GeoJSON geometries

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
2 messages Options
Reply | Threaded
Open this post in threaded view
|

Aeson parsing of GeoJSON geometries

mike thomas
Hi all.

Having returned to commercial software development after a 10 year break in environmental regulation and travel, it was not long before I was using my favorite language Haskell, fortunately, as part of my job this time.

Haskell has changed in many ways since last I used it, and I am having an internal (lack of) knowledge collision with the problem below:

As set out in the example below, given a polygon as GeoJSON from a Postgis enabled Postgres datasbase, I want to get to a list of Double tuples: [(Double,Double)], using Aeson, to draw some polygons.  I am stopped at the point of dismembering the nested coordinate arrays.

With Stackage nightly-2016-09-01 ghci, and the series of functions defined at the end of this message, I get a runtime type error regarding Vectors, which baffles me.

Question 1. Why does Aeson nest the arrays using list syntax around the Array type constructor arguments?

Question 2. How do I best convert this part of the AST:

"(Array [Array [Array [Number 4.5305923601,Number 50.6585120423],Array [Number 4.5307511543,Number 50.657719833],Array [Number 4.5310580333,Number 50.657539732],Array [Number 4.5309023972,Number 50.6584422261],Array [Number 4.5308797482,Number 50.6586166629],Array [Number 4.5305923601,Number 50.6585120423]]])"

to a list of Double tuples?

I've tried several variations of the function dropOuterArray to try and resolve this run-time error, with no success.

All help welcome, and thanks for your time

Mike.

========= GHCI Session With Error ======================

p = parseGeoJSONGeometry "{\"type\":\"Polygon\",\"coordinates\":[[[4.5305923601,50.6585120423],[4.5307511543,50.657719833],[4.5310580333,50.657539732],[4.5309023972,50.6584422261],[4.5308797482,50.6586166629],[4.5305923601,50.6585120423]]]}"

p (Just (String "Polygon"),Just (Array [Array [Array [Number 4.5305923601,Number 50.6585120423],Array [Number 4.5307511543,Number 50.657719833],Array [Number 4.5310580333,Number 50.657539732],Array [Number 4.5309023972,Number 50.6584422261],Array [Number 4.5308797482,Number 50.6586166629],Array [Number 4.5305923601,Number 50.6585120423]]]))

p1 = dropOuterArrayJ (snd p)

p1 [Array [Array [Number 4.5305923601,Number 50.6585120423],Array [Number 4.5307511543,Number 50.657719833],Array [Number 4.5310580333,Number 50.657539732],Array [Number 4.5309023972,Number 50.6584422261],Array [Number 4.5308797482,Number 50.6586166629],Array [Number 4.5305923601,Number 50.6585120423]]]

p2 = dropOuterArray p1


:151:21: error: • Couldn't match type ‘Data.Vector.Vector Value’ with ‘Value’ Expected type: Value Actual type: Array • In the first argument of ‘dropOuterArray’, namely ‘p1’ In the expression: dropOuterArray p1 In an equation for ‘p2’: p2 = dropOuterArray p1

============ Code ====================

{-# LANGUAGE OverloadedStrings, DeriveGeneric, DeriveAnyClass, ScopedTypeVariables, LambdaCase, FlexibleContexts #-}

module GeoJSONGeometry (
    parseGeoJSONGeometry
    ,Coordinates
    ,dropOuterArrayJ
    ,dropOuterArray
    ,Pt
    ,gTypeString
) where

import Data.Aeson
import Data.Aeson.Types --(parse, parseMaybe, parseEither, Value(..))
import GHC.Generics
import Data.ByteString.Lazy.Char8
import Data.HashMap.Strict
import qualified Data.Vector as V
import Data.Either.Extra (fromRight)
import Data.Maybe (fromJust)
import Control.Applicative

import Codec.Picture
import Graphics.Rasterific
import Graphics.Rasterific.Texture

import GHC.Generics


parseGeoJSONGeometry gjg =
 let
    eresult = (eitherDecode (pack gjg)) :: Either String Object -- Value -- Object
    result = (fromRight eresult)
    gType = Data.HashMap.Strict.lookup "type" result
    gCoords = Data.HashMap.Strict.lookup "coordinates" result
 in
    (gType, gCoords)

gTypeString (Just (Data.Aeson.Types.String s)) = s
  
dropOuterArrayJ (Just (Array u)) = u


dropOuterArray (Array u) = u



_______________________________________________
Haskell-Cafe mailing list
To (un)subscribe, modify options or view archives go to:
http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
Only members subscribed via the mailman list are allowed to post.
Reply | Threaded
Open this post in threaded view
|

Re: Aeson parsing of GeoJSON geometries

Adam Bergmark-2


On Thu, Sep 8, 2016 at 1:06 AM, mike thomas <[hidden email]> wrote:
Hi all.

Having returned to commercial software development after a 10 year break in environmental regulation and travel, it was not long before I was using my favorite language Haskell, fortunately, as part of my job this time.

Haskell has changed in many ways since last I used it, and I am having an internal (lack of) knowledge collision with the problem below:

As set out in the example below, given a polygon as GeoJSON from a Postgis enabled Postgres datasbase, I want to get to a list of Double tuples: [(Double,Double)], using Aeson, to draw some polygons.  I am stopped at the point of dismembering the nested coordinate arrays.

With Stackage nightly-2016-09-01 ghci, and the series of functions defined at the end of this message, I get a runtime type error regarding Vectors, which baffles me.

Question 1. Why does Aeson nest the arrays using list syntax around the Array type constructor arguments?

This is just the Show instance for Values, you can `encode' to print the actual json representation.
 

Question 2. How do I best convert this part of the AST:

"(Array [Array [Array [Number 4.5305923601,Number 50.6585120423],Array [Number 4.5307511543,Number 50.657719833],Array [Number 4.5310580333,Number 50.657539732],Array [Number 4.5309023972,Number 50.6584422261],Array [Number 4.5308797482,Number 50.6586166629],Array [Number 4.5305923601,Number 50.6585120423]]])"

to a list of Double tuples?

I usually try to avoid doing custom parsing, it's much easier to use an existing instance when you can.

Try this:

#!/usr/bin/env stack
-- stack --resolver nightly-2016-09-01 --install-ghc runghc --package aeson
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE OverloadedStrings #-}
module Main (main) where

import GHC.Generics
import Data.Aeson

data Geo = Geo { coordinates :: [[(Double, Double)]] } deriving (Generic, Show)
instance ToJSON Geo
instance FromJSON Geo

main :: IO ()
main = do
  let ex1 = eitherDecode "[[1,2]]" :: Either String Value
  print ex1
  print $ fmap encode ex1
  let ex2 = "{\"type\":\"Polygon\",\"coordinates\":[[[4.5305923601,50.6585120423],[4.5307511543,50.657719833],[4.5310580333,50.657539732],[4.5309023972,50.6584422261],[4.5308797482,50.6586166629],[4.5305923601,50.6585120423]]]}"
  print (eitherDecode ex2 :: Either String Geo)

 

I've tried several variations of the function dropOuterArray to try and resolve this run-time error, with no success.

All help welcome, and thanks for your time

Mike.

========= GHCI Session With Error ======================

p = parseGeoJSONGeometry "{\"type\":\"Polygon\",\"coordinates\":[[[4.5305923601,50.6585120423],[4.5307511543,50.657719833],[4.5310580333,50.657539732],[4.5309023972,50.6584422261],[4.5308797482,50.6586166629],[4.5305923601,50.6585120423]]]}"

p (Just (String "Polygon"),Just (Array [Array [Array [Number 4.5305923601,Number 50.6585120423],Array [Number 4.5307511543,Number 50.657719833],Array [Number 4.5310580333,Number 50.657539732],Array [Number 4.5309023972,Number 50.6584422261],Array [Number 4.5308797482,Number 50.6586166629],Array [Number 4.5305923601,Number 50.6585120423]]]))

p1 = dropOuterArrayJ (snd p)

p1 [Array [Array [Number 4.5305923601,Number 50.6585120423],Array [Number 4.5307511543,Number 50.657719833],Array [Number 4.5310580333,Number 50.657539732],Array [Number 4.5309023972,Number 50.6584422261],Array [Number 4.5308797482,Number 50.6586166629],Array [Number 4.5305923601,Number 50.6585120423]]]

p2 = dropOuterArray p1


:151:21: error: • Couldn't match type ‘Data.Vector.Vector Value’ with ‘Value’ Expected type: Value Actual type: Array • In the first argument of ‘dropOuterArray’, namely ‘p1’ In the expression: dropOuterArray p1 In an equation for ‘p2’: p2 = dropOuterArray p1

============ Code ====================

{-# LANGUAGE OverloadedStrings, DeriveGeneric, DeriveAnyClass, ScopedTypeVariables, LambdaCase, FlexibleContexts #-}

module GeoJSONGeometry (
    parseGeoJSONGeometry
    ,Coordinates
    ,dropOuterArrayJ
    ,dropOuterArray
    ,Pt
    ,gTypeString
) where

import Data.Aeson
import Data.Aeson.Types --(parse, parseMaybe, parseEither, Value(..))
import GHC.Generics
import Data.ByteString.Lazy.Char8
import Data.HashMap.Strict
import qualified Data.Vector as V
import Data.Either.Extra (fromRight)
import Data.Maybe (fromJust)
import Control.Applicative

import Codec.Picture
import Graphics.Rasterific
import Graphics.Rasterific.Texture

import GHC.Generics


parseGeoJSONGeometry gjg =
 let
    eresult = (eitherDecode (pack gjg)) :: Either String Object -- Value -- Object
    result = (fromRight eresult)
    gType = Data.HashMap.Strict.lookup "type" result
    gCoords = Data.HashMap.Strict.lookup "coordinates" result
 in
    (gType, gCoords)

gTypeString (Just (Data.Aeson.Types.String s)) = s
  
dropOuterArrayJ (Just (Array u)) = u


dropOuterArray (Array u) = u



_______________________________________________
Haskell-Cafe mailing list
To (un)subscribe, modify options or view archives go to:
http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
Only members subscribed via the mailman list are allowed to post.


_______________________________________________
Haskell-Cafe mailing list
To (un)subscribe, modify options or view archives go to:
http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
Only members subscribed via the mailman list are allowed to post.