mapquest-api 0.1.0.0 → 0.2.0.0
raw patch · 5 files changed
+139/−124 lines, 5 files
Files
- mapquest-api.cabal +3/−3
- src/Web/API/MapQuest.hs +24/−0
- src/Web/API/MapQuest/Geocoding.hs +112/−0
- src/Web/API/Mapquest.hs +0/−15
- src/Web/API/Mapquest/Geocoding.hs +0/−106
mapquest-api.cabal view
@@ -1,5 +1,5 @@ name: mapquest-api-version: 0.1.0.0+version: 0.2.0.0 synopsis: Bindings to the MapQuest API description: This library provides a high-level interface to the MapQuest API. Currently only the "geocoding" API (street address to coordinates) is provided, but the functionality is straightforward to extend. homepage: https://github.com/ocramz/mapquest-api@@ -18,8 +18,8 @@ default-language: Haskell2010 ghc-options: -Wall hs-source-dirs: src- exposed-modules: Web.API.Mapquest- Web.API.Mapquest.Geocoding+ exposed-modules: Web.API.MapQuest+ other-modules: Web.API.MapQuest.Geocoding build-depends: base >= 4.7 && < 5 , req , bytestring
+ src/Web/API/MapQuest.hs view
@@ -0,0 +1,24 @@+{-|+Module : Web.API.MapQuest+Description : MapQuest+Copyright : (c) Marco Zocca, 2018+License : GPL-3+Maintainer : zocca.marco gmail+Stability : experimental+Portability : POSIX+-}+module Web.API.MapQuest (+ -- * Geocoding+ G.request,+ -- ** Parameters+ G.GeoQuery(..),+ -- ** Output+ G.Coords(..)+ ) where++import qualified Web.API.MapQuest.Geocoding as G+++++
+ src/Web/API/MapQuest/Geocoding.hs view
@@ -0,0 +1,112 @@+{-# language OverloadedStrings, DataKinds, DeriveGeneric #-}+{-|+Module : Web.API.MapQuest.Geocoding+Description : Geocoding interface+Copyright : (c) Marco Zocca, 2018+License : GPL-3+Maintainer : zocca.marco gmail+Stability : experimental+Portability : POSIX+-}+module Web.API.MapQuest.Geocoding (request, GeoQuery(..), Coords(..))where++import Data.List (intersperse)+import Data.Monoid ((<>))++import Network.HTTP.Req+import qualified Data.Text as T+import qualified Data.ByteString.Lazy as LBS++import GHC.Generics++import Control.Monad.Catch++import Data.Aeson+import Data.Aeson.Types (Parser, parseMaybe)++-- https://developer.mapquest.com/documentation/geocoding-api/address/get/++apiRootPath :: Url 'Http+apiRootPath = http "www.mapquestapi.com" /: "geocoding" /: "v1" /: "address"+++-- example request :+-- GET http://www.mapquestapi.com/geocoding/v1/address?key=KEY&location=Washington,DC++instance MonadHttp IO where+ handleHttpException = throwM++-- | Call the MapQuest Geocoding API with a given address and extract the coordinates from the parsed result.+--+-- Example usage :+-- assuming the user has bound /key/ to hold the API key string, the following can be run in a GHCi shell :+--+-- >>> request key (GQ "Via Irnerio" "Bologna" "Italy")+-- Just (Coords {lat = 44.49897, long = 11.34503})+--+-- >>> request key (GQFree "Hong Kong")+-- Just (Coords {lat = 22.264412, long = 114.16706})++request ::+ T.Text -- ^ API key (available for free on <https://developer.mapquest.com>)+ -> GeoQuery -- ^ Query address+ -> IO (Maybe (Coords Float))+request apikey q = do+ r <- req GET apiRootPath NoReqBody lbsResponse opts'+ return $ decoder1 $ responseBody r where+ opts' = + ("key" =: apikey) <>+ ("outFormat" =: ("json" :: T.Text)) <>+ ("location" =: renderGeoQuery q)++++decoder1 :: LBS.ByteString -> Maybe (Coords Float)+decoder1 dat = do+ r <- decode dat+ flip parseMaybe r $ \obj -> do + locp <- decodeLocation obj+ decodeLatLong locp++decodeLocation :: FromJSON a => Object -> Parser a+decodeLocation obj = do+ (res0 : _) <- obj .: "results"+ (loc0 : _) <- res0 .: "locations"+ return loc0++decodeLatLong :: Object -> Parser (Coords Float)+decodeLatLong loc = do+ ll <- loc .: "latLng"+ Coords <$> ll .: "lat" <*> ll .: "lng"+ +-- | Coordinates+data Coords a = Coords {+ lat :: a -- ^ Latitude+ , long :: a -- ^ Longitude+ } deriving (Eq, Show, Generic)++-- instance Functor Coords where+-- fmap f (Coords x y) = Coords (f x) (f y)++-- instance FromJSON a => FromJSON (Coords a)+++-- | Geocoding query parameters+data GeoQuery = GQ {+ gqStreet :: T.Text -- ^ Street address (e.g. \"Via Irnerio\")+ , gqCity :: T.Text -- ^ City (e.g. \"Bologna\")+ , gqCountry :: T.Text -- ^ Country (e.g. \"Italy\")+ }+ | GQFree T.Text -- ^ Free-text query (must be a valid address or location)+ deriving (Eq, Show)++renderGeoQuery :: GeoQuery -> T.Text+renderGeoQuery q = case q of+ (GQ addr city country) -> T.concat $ intersperse ", " [addr, city, country]+ (GQFree t) -> t ++-- options :: Foldable t => t (T.Text, T.Text) -> Option 'Http+-- options = foldr (\(k, v) acc -> (k =: v) <> acc ) mempty+++
− src/Web/API/Mapquest.hs
@@ -1,15 +0,0 @@-module Web.API.Mapquest (- -- * Geocoding- G.request,- -- ** Parameters- G.GeoQuery(..),- -- ** Output- G.Coords(..)- ) where--import qualified Web.API.Mapquest.Geocoding as G-----
− src/Web/API/Mapquest/Geocoding.hs
@@ -1,106 +0,0 @@-{-# language OverloadedStrings, DataKinds, DeriveGeneric #-}-module Web.API.Mapquest.Geocoding (request, GeoQuery(..), Coords(..))where--import Data.List (intersperse)-import Data.Monoid (mempty, (<>))--import Network.HTTP.Req-import qualified Data.Text as T-import qualified Data.Text.Encoding as T (encodeUtf8, decodeUtf8)-import qualified Data.ByteString.Lazy as LBS--import GHC.Generics--import Control.Monad.Catch--import Data.Aeson-import Data.Aeson.Types (Parser(..), parseMaybe, parseEither)---- https://developer.mapquest.com/documentation/geocoding-api/address/get/--apiRootPath :: Url 'Http-apiRootPath = http "www.mapquestapi.com" /: "geocoding" /: "v1" /: "address"----- example request :--- GET http://www.mapquestapi.com/geocoding/v1/address?key=KEY&location=Washington,DC--instance MonadHttp IO where- handleHttpException = throwM---- | Call the MapQuest Geocoding API with a given address and extract the coordinates from the parsed result.------ Example usage :--- assuming the user has bound /key/ to hold the API key string, the following can be run in a GHCi shell :------ >>> request key (GQ "Via Irnerio" "Bologna" "Italy")--- Just (Coords {lat = 44.49897, long = 11.34503})------ >>> request key (GQFree "Hong Kong")--- Just (Coords {lat = 22.264412, long = 114.16706})--request ::- T.Text -- ^ API key (available for free on mapquestapi.com)- -> GeoQuery -- ^ Query address- -> IO (Maybe (Coords Float))-request apikey q = do- r <- req GET apiRootPath NoReqBody lbsResponse opts'- return $ decoder1 $ responseBody r where- opts' = - ("key" =: apikey) <>- ("outFormat" =: ("json" :: T.Text)) <>- ("location" =: renderGeoQuery q)----decoder1 :: LBS.ByteString -> Maybe (Coords Float)-decoder1 dat = do- r <- decode dat- flip parseMaybe r $ \obj -> do - locp <- decodeLocation obj- decodeLatLong locp--decodeLocation :: FromJSON a => Object -> Parser a-decodeLocation obj = do- (res0 : _) <- obj .: "results"- (loc0 : _) <- res0 .: "locations"- return loc0--decodeLatLong :: Object -> Parser (Coords Float)-decodeLatLong loc = do- ll <- loc .: "latLng"- Coords <$> ll .: "lat" <*> ll .: "lng"- --- | Coordinates-data Coords a = Coords {- lat :: a -- ^ Latitude- , long :: a -- ^ Longitude- } deriving (Eq, Show, Generic)---- instance Functor Coords where--- fmap f (Coords x y) = Coords (f x) (f y)---- instance FromJSON a => FromJSON (Coords a)--mkQuery :: T.Text -> T.Text -> T.Text -> GeoQuery-mkQuery = GQ---- | Geocoding query -data GeoQuery = GQ {- gqStreet :: T.Text -- ^ Street address (e.g. \"Via Irnerio\")- , gqCity :: T.Text -- ^ City (e.g. \"Bologna\")- , gqCountry :: T.Text -- ^ Country (e.g. \"Italy\")- }- | GQFree T.Text -- ^ Free-text query (must be a valid address or location)- deriving (Eq, Show)--renderGeoQuery :: GeoQuery -> T.Text-renderGeoQuery q = case q of- (GQ addr city country) -> T.concat $ intersperse ", " [addr, city, country]- (GQFree t) -> t ---- options :: Foldable t => t (T.Text, T.Text) -> Option 'Http--- options = foldr (\(k, v) acc -> (k =: v) <> acc ) mempty---