-
-
Notifications
You must be signed in to change notification settings - Fork 7
/
Copy pathgoogle-maps-geocode.el
146 lines (121 loc) · 5.11 KB
/
google-maps-geocode.el
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
;;; google-maps-geocode.el --- Geocode address using Google Maps service
;; Copyright (C) 2010 Julien Danjou
;; Author: Julien Danjou <[email protected]>
;; Keywords: comm
;; This file is NOT part of GNU Emacs.
;; GNU Emacs is free software: you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;; GNU Emacs is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
;;; Commentary:
;; Communicate with the Google Maps Geocoding API.
;;; Code:
(require 'cl-lib)
(require 'json)
(require 'google-maps-base)
(defconst google-maps-geocode-uri
"https://maps.google.com/maps/api/geocode/json"
"Google Maps Geocoding API server.")
(defun google-maps-geocode-build-url (plist)
"Built a Google Maps Geocode API request URL from PLIST."
(concat
google-maps-geocode-uri "?key="
google-maps-api-key "&"
(google-maps-urlencode-plist
plist
'((address . url-hexify-string)
;; Sensor is MANDATORY
(sensor . (lambda (v)
(if v "true" "false")))
(latlng)
(region)
(bounds . (lambda (v)
(mapconcat 'identity v "|")))
(language)))))
(defun google-maps-geocode-request->status (data)
"Return status of a `google-maps-geocode' request.
It should returned `ok' if everything went well."
(intern (downcase (cdr (assoc 'status data)))))
(defun google-maps-geocode-request-is-ok (data)
"Return t if the geocoding via `google-maps-geocode' went fine."
(eq (google-maps-geocode-request->status data) 'ok))
(defun google-maps-geocode-request->results (data)
"Return result list of a `google-maps-geocode' request."
(cdr (assoc 'results data)))
(defun google-maps-geocode-request (&rest params)
"Request geocoding of a location and return the request result.
Request status can be retrieved with
`google-maps-geocode-request->status'. Request results data can
be retrieve ed with `google-maps-geocode-request->results'.
Valid params are:
:address The address to geocode.
:sensor Boolean indicating if this call is used for a sensor
device.
:latlng Coordinates.
:region Region.
:bounds Bounding box.
:language Language to use in returned data."
(json-read-from-string
(google-maps-retrieve-data
(google-maps-geocode-build-url
(google-maps-build-plist params)))))
(defun google-maps-geocode-results->one-result-picked-by-user (results)
"Make the user choose a result from RESULTS, and return it."
(let ((location
;; `location' will contains what the user choosed as location.
(completing-read
"Precise location: "
(mapcar
(lambda (x)
(cdr (assoc 'formatted_address x)))
results)
nil t)))
;; Find entry with that location
(cl-find-if
`(lambda (entry)
(string= (cdr (assoc 'formatted_address entry))
,location))
results)))
(defun google-maps-geocode-results->one-result (results)
"Converts geocoding results list to one result only.
If there is several results, the user is asked to pick one via
`google-maps-geocode-results->one-result-picked-by-user'."
(pcase (length results)
(0 nil)
(1 (elt results 0))
(_ (google-maps-geocode-results->one-result-picked-by-user results))))
(defun google-maps-geocode-location (location)
(let* ((req (google-maps-geocode-request :address location))
(status (google-maps-geocode-request->status req)))
(unless (eq status 'ok)
(error (format "Unable to geocode %s: %s" location status)))
(google-maps-geocode-results->one-result
(google-maps-geocode-request->results req))))
(defun google-maps-geocode-location->coordinates (location)
"Return a list containing latitude and longitude."
(let ((geocode-location (google-maps-geocode-location location))
latitude longitude)
(if (null (assoc 'geometry geocode-location))
(error (format "No geometry information for location: %s" location)))
(setq latitude (cdr (assoc 'lat (assoc 'location (assoc 'geometry geocode-location)))))
(setq longitude (cdr (assoc 'lng (assoc 'location (assoc 'geometry geocode-location)))))
(if (or (null latitude) (null longitude))
(error (format "Null location coordinates: %s,%s" latitude longitude)))
(list latitude longitude)))
;;;###autoload
(defun google-maps-geocode-replace-region (beg end)
"Geocode region and replace it with a more accurate result."
(interactive "r")
(let ((location (cdr (assoc 'formatted_address
(google-maps-geocode-location
(buffer-substring beg end))))))
(delete-region beg end)
(insert location)))
(provide 'google-maps-geocode)
;;; google-maps-geocode.el ends here