pyc-website

main website for pyc inc.

git clone https://9o.is/git/pyc-website.git

Geocode.scala

(2712B)


      1 package inc.pyc
      2 package lib
      3 
      4 import dispatch._, Defaults._
      5 import net.liftweb._
      6 import util.Helpers._
      7 import json._
      8 
      9 /**
     10  * Google Geocoding API -- Reverse Address Lookup
     11  * https://developers.google.com/maps/documentation/geocoding/
     12  */
     13 object Geocode {
     14   
     15   private val endPoint = "http://maps.googleapis.com/maps/api/geocode/json"
     16     
     17   private val defaultParams = List(("language", "en"))
     18   
     19   def geolocation(address: String, params: List[(String, String)] = defaultParams): Future[Either[String, GeoPoint]] = {
     20     for(lookup <- lookupAddress(address, params))
     21       yield for {
     22         response <- lookup.right
     23         result <- getResult(response).right
     24       } yield result.geometry.location
     25   }
     26   
     27   /**
     28    * Lookup address information
     29    */
     30   def lookupAddress(address: String, params: List[(String, String)] = defaultParams): Future[Either[String, Response]] = {
     31     implicit val formats = DefaultFormats
     32     val req = url(endPoint) <<? (("address", address) :: ("address", address) :: params)
     33     
     34     def extract(json: String): Either[String, Response] = 
     35       tryo(parse(json).extract[Response]) toRight {
     36         "Can't extract response from Google Geocode API"
     37       }
     38     
     39     def checkStatus(response: Response): Either[String, Response] = {
     40       if(response.status == "OK") Right(response)
     41       else Left(response.status)
     42     }
     43     
     44     for (jsonEither <- request(req)) 
     45       yield for {
     46         json <- jsonEither.right 
     47         value <- extract(json).right
     48         okValue <- checkStatus(value).right
     49       } yield okValue
     50   }
     51   
     52   /**
     53    * Given a list of results by Google's API, this just gets the first one in the list.
     54    */
     55   private def getResult(response: Response): Either[String, Result] = 
     56     response.results.headOption.toRight("No results found when retrieving Google's returned results.")
     57 
     58   /**
     59    * Request and get a json response.
     60    */
     61   private def request(req: Req): Future[Either[String, String]] = {
     62     val res = Http(req OK as.String).either
     63     
     64     for (e <- (res).left) 
     65       yield s"Can't connect to Google Geocode API: ${e.getMessage}"
     66   }
     67 
     68   case class Response(
     69     status: String,
     70     results: List[Result])
     71 
     72   case class Result(
     73     types: List[String],
     74     formatted_address: String,
     75     address_components: List[AddressComponent],
     76     geometry: Geometry,
     77     partial_match: Option[Boolean])
     78 
     79   case class AddressComponent(
     80     long_name: String,
     81     short_name: String,
     82     types: List[String])
     83 
     84   case class Geometry(
     85     location: GeoPoint,
     86     location_type: String,
     87     viewport: ViewPort,
     88     bounds: Option[ViewPort])
     89 
     90   case class ViewPort(
     91     southwest: GeoPoint,
     92     northeast: GeoPoint)
     93 
     94   case class GeoPoint(lat: Double, lng: Double)
     95 }