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 }