reverse-geocoding (empty) → 0.1.0.0
raw patch · 6 files changed
+201/−0 lines, 6 filesdep +aesondep +basedep +iso3166-country-codessetup-changed
Dependencies added: aeson, base, iso3166-country-codes, lens, lens-aeson, text, wreq
Files
- LICENSE +30/−0
- Setup.hs +2/−0
- reverse-geocoding.cabal +33/−0
- src/Data/Geolocation/Reverse.hs +44/−0
- src/Data/Geolocation/Reverse/Providers.hs +50/−0
- src/Data/Geolocation/Reverse/Types.hs +42/−0
+ LICENSE view
@@ -0,0 +1,30 @@+Copyright (c) 2015, João Cristóvão++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++ * Redistributions of source code must retain the above copyright+ notice, this list of conditions and the following disclaimer.++ * Redistributions in binary form must reproduce the above+ copyright notice, this list of conditions and the following+ disclaimer in the documentation and/or other materials provided+ with the distribution.++ * Neither the name of João Cristóvão nor the names of other+ contributors may be used to endorse or promote products derived+ from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ reverse-geocoding.cabal view
@@ -0,0 +1,33 @@+name: reverse-geocoding+version: 0.1.0.0+synopsis: Simple reverse geocoding using OpenStreeMap+description: Simple reverse geocoding using OpenStreeMap+homepage: https://github.com/jcristovao/reverse-geocoding+license: BSD3+license-file: LICENSE+author: João Cristóvão+maintainer: jmacristovao@gmail.com+-- copyright: +category: Data+build-type: Simple+cabal-version: >=1.10++library+ exposed-modules: Data.Geolocation.Reverse.Types+ Data.Geolocation.Reverse.Providers+ Data.Geolocation.Reverse+ -- other-modules: + other-extensions: OverloadedStrings+ build-depends: base >= 4.6 && < 4.8+ , lens >= 4.4 && < 4.5+ , lens-aeson >= 1.0 && < 1.1+ , wreq >= 0.2 && < 0.3+ , aeson >= 0.8 && < 0.9+ , text >= 1.2 && < 1.3+ , iso3166-country-codes >= 0.20140203.7+ hs-source-dirs: src+ default-language: Haskell2010++source-repository head+ type: git+ location: https://github.com/jcristovao/reverse-geocoding
+ src/Data/Geolocation/Reverse.hs view
@@ -0,0 +1,44 @@+{-# LANGUAGE OverloadedStrings #-}+-- | This very simple module returns a Maybe Location info for a set+-- of coordinates.+--+-- Right now only one provider is given (OpenStreeMap), and the Suburb/Street+-- parsing could be improved, but this is enough for our needs.+--+-- Feel free to contribute!+module Data.Geolocation.Reverse where++import Control.Lens+import Network.Wreq+import Data.Aeson+import Data.Aeson.Types+import Data.Aeson.Lens (key)++import Data.Geolocation.Reverse.Types+import Data.Geolocation.Reverse.Providers++-- | Default reverse geo provider+defaultReverseGeoProvider :: ReverseGeoProvider+defaultReverseGeoProvider = openStreetMap++-- | Given a Latitude and Longitude, return a Location Info.+getLocationInfo+ :: Latitude+ -> Longitude+ -> ReverseGeoProvider+ -> IO (Maybe ParsedLocationInfo)+getLocationInfo lat lon (jsonkey,url,parser) =+ case url lat lon of+ Nothing -> return Nothing+ Just ga -> do+ r <- get ga+ -- addr :: Maybe Value+ let addr = r ^? responseBody . key jsonkey+ case addr of+ Just (Object o) -> return . flip parseMaybe o . return+ $ parser o+ _ -> return Nothing++-- | Get location info from default provider (right now Open Stree Map)+getLocationInfoDef :: Latitude -> Longitude -> IO (Maybe ParsedLocationInfo)+getLocationInfoDef lat lon = getLocationInfo lat lon defaultReverseGeoProvider
+ src/Data/Geolocation/Reverse/Providers.hs view
@@ -0,0 +1,50 @@+{-# LANGUAGE OverloadedStrings #-}+-- | Here we define providers for the Reverse Geocoding feature,+-- namely how to format the url, the key under which the results are returned,+-- and how to parse the retrived data.+--+-- Currently only Open Stree Map is provided, feel free to add others.+--+module Data.Geolocation.Reverse.Providers+ ( ReverseGeoJsonKey+ , ReverseGeoParser+ , ReverseGeoProvider+ , openStreetMap++ ) where++import Control.Applicative++import Data.Monoid+import Data.Aeson+import Data.Aeson.Types+import qualified Data.Text as T++import Data.Geolocation.Reverse.Types++type ReverseGeoJsonKey = T.Text+type ReverseGeoUrl = Latitude -> Longitude -> Maybe String+type ReverseGeoParser = Object -> Parser ParsedLocationInfo+type ReverseGeoProvider = (ReverseGeoJsonKey, ReverseGeoUrl, ReverseGeoParser)+++openStreetMap :: ReverseGeoProvider+openStreetMap = ("address", openStreetMapUrl, openStreetMapParser)++openStreetMapUrl :: Latitude -> Longitude -> Maybe String+openStreetMapUrl (Latitude mlat) (Longitude mlon) = do+ lat <- mlat+ lon <- mlon+ return $ "http://nominatim.openstreetmap.org/reverse"+ <> "?format=json"+ <> "&zoom=18"+ <> "&lat=" <> show lat+ <> "&lon=" <> show lon+++openStreetMapParser o =+ ParsedLocationInfo <$> o .: "country_code"+ <*> o .: "city"+ <*> o .:? "suburb"+ <*> ((o .:? "road") <|> (o .:? "street"))+ <*> o .:? "postcode"
+ src/Data/Geolocation/Reverse/Types.hs view
@@ -0,0 +1,42 @@+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# OPTIONS_GHC -fno-warn-orphans #-}+module Data.Geolocation.Reverse.Types where++import Data.Aeson+import Control.Monad (mzero)+import qualified Data.Text as T+import Data.ISO3166_CountryCodes++-- | /Input types/++-- | Latitude : newtype over double+newtype Latitude = Latitude (Maybe Double)+ deriving (Eq,Ord,Show,Read,FromJSON,ToJSON)++-- | Longitude : newtype over double+newtype Longitude = Longitude (Maybe Double)+ deriving (Eq,Ord,Show,Read,FromJSON,ToJSON)++-- | /Output types/+newtype City = City T.Text deriving (Eq,Ord,Show,Read,FromJSON,ToJSON)+newtype Suburb = Suburb T.Text deriving (Eq,Ord,Show,Read,FromJSON,ToJSON)+newtype Street = Street T.Text deriving (Eq,Ord,Show,Read,FromJSON,ToJSON)+newtype Postcode= Postcode T.Text deriving (Eq,Ord,Show,Read,FromJSON,ToJSON)++-- Booo: orphan instances+instance ToJSON CountryCode+instance FromJSON CountryCode where+ parseJSON (String t) = return . read . T.unpack . T.toUpper $ t+ parseJSON _ = mzero+++-- | Parsed Location Info: Country code and city are mandatory, all other info is optional+data ParsedLocationInfo+ = ParsedLocationInfo+ { parsedCountry :: CountryCode+ , parsedCity :: City+ , parsedSuburb :: Maybe Suburb+ , parsedStreet :: Maybe Street+ , parsedPostCode :: Maybe Postcode+ } deriving (Eq,Ord,Show)