]> git.mxchange.org Git - quix0rs-gnu-social.git/blob - plugins/GeonamesPlugin.php
make an URL for a geonames location
[quix0rs-gnu-social.git] / plugins / GeonamesPlugin.php
1 <?php
2 /**
3  * StatusNet, the distributed open-source microblogging tool
4  *
5  * Plugin to convert string locations to Geonames IDs and vice versa
6  *
7  * PHP version 5
8  *
9  * LICENCE: This program is free software: you can redistribute it and/or modify
10  * it under the terms of the GNU Affero General Public License as published by
11  * the Free Software Foundation, either version 3 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU Affero General Public License for more details.
18  *
19  * You should have received a copy of the GNU Affero General Public License
20  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
21  *
22  * @category  Action
23  * @package   StatusNet
24  * @author    Evan Prodromou <evan@status.net>
25  * @copyright 2009 StatusNet Inc.
26  * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
27  * @link      http://status.net/
28  */
29
30 if (!defined('STATUSNET')) {
31     exit(1);
32 }
33
34 /**
35  * Plugin to convert string locations to Geonames IDs and vice versa
36  *
37  * This handles most of the events that Location class emits. It uses
38  * the geonames.org Web service to convert names like 'Montreal, Quebec, Canada'
39  * into IDs and lat/lon pairs.
40  *
41  * @category Plugin
42  * @package  StatusNet
43  * @author   Evan Prodromou <evan@status.net>
44  * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
45  * @link     http://status.net/
46  *
47  * @seeAlso  Location
48  */
49
50 class GeonamesPlugin extends Plugin
51 {
52     const NAMESPACE = 1;
53
54     /**
55      * convert a name into a Location object
56      *
57      * @param string   $name      Name to convert
58      * @param string   $language  ISO code for anguage the name is in
59      * @param Location &$location Location object (may be null)
60      *
61      * @return boolean whether to continue (results in $location)
62      */
63
64     function onLocationFromName($name, $language, &$location)
65     {
66         $client = HTTPClient::start();
67
68         // XXX: break down a name by commas, narrow by each
69
70         $str = http_build_query(array('maxRows' => 1,
71                                       'q' => $name,
72                                       'lang' => $language,
73                                       'type' => 'json'));
74
75         $result = $client->get('http://ws.geonames.org/search?'.$str);
76
77         if ($result->code == "200") {
78             $rj = json_decode($result->body);
79             if (count($rj->geonames) > 0) {
80                 $n = $rj->geonames[0];
81
82                 $location = new Location();
83
84                 $location->lat              = $n->lat;
85                 $location->lon              = $n->lng;
86                 $location->names[$language] = $n->name;
87                 $location->location_id      = $n->geonameId;
88                 $location->location_ns      = self::NAMESPACE;
89
90                 // handled, don't continue processing!
91                 return false;
92             }
93         }
94
95         // Continue processing; we don't have the answer
96         return true;
97     }
98
99     /**
100      * convert an id into a Location object
101      *
102      * @param string   $id        Name to convert
103      * @param string   $ns        Name to convert
104      * @param string   $language  ISO code for language for results
105      * @param Location &$location Location object (may be null)
106      *
107      * @return boolean whether to continue (results in $location)
108      */
109
110     function onLocationFromId($id, $ns, $language, &$location)
111     {
112         if ($ns != self::NAMESPACE) {
113             // It's not one of our IDs... keep processing
114             return true;
115         }
116
117         $client = HTTPClient::start();
118
119         $str = http_build_query(array('geonameId' => $id,
120                                       'lang' => $language));
121
122         $result = $client->get('http://ws.geonames.org/hierarchyJSON?'.$str);
123
124         if ($result->code == "200") {
125
126             $rj = json_decode($result->body);
127
128             if (count($rj->geonames) > 0) {
129
130                 $parts = array();
131
132                 foreach ($rj->geonames as $level) {
133                     if (in_array($level->fcode, array('PCLI', 'ADM1', 'PPL'))) {
134                         $parts[] = $level->name;
135                     }
136                 }
137
138                 $last = $rj->geonames[count($rj->geonames)-1];
139
140                 if (!in_array($level->fcode, array('PCLI', 'ADM1', 'PPL'))) {
141                     $parts[] = $last->name;
142                 }
143
144                 $location = new Location();
145
146                 $location->location_id      = $last->geonameId;
147                 $location->location_ns      = self::NAMESPACE;
148                 $location->lat              = $last->lat;
149                 $location->lon              = $last->lng;
150                 $location->names[$language] = implode(', ', array_reverse($parts));
151             }
152         }
153
154         // We're responsible for this NAMESPACE; nobody else
155         // can resolve it
156
157         return false;
158     }
159
160     /**
161      * convert a lat/lon pair into a Location object
162      *
163      * Given a lat/lon, we try to find a Location that's around
164      * it or nearby. We prefer populated places (cities, towns, villages).
165      *
166      * @param string   $lat       Latitude
167      * @param string   $lon       Longitude
168      * @param string   $language  ISO code for language for results
169      * @param Location &$location Location object (may be null)
170      *
171      * @return boolean whether to continue (results in $location)
172      */
173
174     function onLocationFromLatLon($lat, $lon, $language, &$location)
175     {
176         $client = HTTPClient::start();
177
178         $str = http_build_query(array('lat' => $lat,
179                                       'lng' => $lon,
180                                       'lang' => $language));
181
182         $result =
183           $client->get('http://ws.geonames.org/findNearbyPlaceNameJSON?'.$str);
184
185         if ($result->code == "200") {
186
187             $rj = json_decode($result->body);
188
189             if (count($rj->geonames) > 0) {
190
191                 $n = $rj->geonames[0];
192
193                 $parts = array();
194
195                 $location = new Location();
196
197                 $parts[] = $n->name;
198
199                 if (!empty($n->adminName1)) {
200                     $parts[] = $n->adminName1;
201                 }
202
203                 if (!empty($n->countryName)) {
204                     $parts[] = $n->countryName;
205                 }
206
207                 $location->location_id = $n->geonameId;
208                 $location->location_ns = self::NAMESPACE;
209                 $location->lat         = $lat;
210                 $location->lon         = $lon;
211
212                 $location->names[$language] = implode(', ', $parts);
213
214                 // Success! We handled it, so no further processing
215
216                 return false;
217             }
218         }
219
220         // For some reason we don't know, so pass.
221
222         return true;
223     }
224
225     /**
226      * Human-readable name for a location
227      *
228      * Given a location, we try to retrieve a human-readable name
229      * in the target language.
230      *
231      * @param Location $location Location to get the name for
232      * @param string   $language ISO code for language to find name in
233      * @param string   &$name    Place to put the name
234      *
235      * @return boolean whether to continue
236      */
237
238     function onLocationNameLanguage($location, $language, &$name)
239     {
240         if ($location->location_ns != self::NAMESPACE) {
241             // It's not one of our IDs... keep processing
242             return true;
243         }
244
245         $client = HTTPClient::start();
246
247         $str = http_build_query(array('geonameId' => $id,
248                                       'lang' => $language));
249
250         $result = $client->get('http://ws.geonames.org/hierarchyJSON?'.$str);
251
252         if ($result->code == "200") {
253
254             $rj = json_decode($result->body);
255
256             if (count($rj->geonames) > 0) {
257
258                 $parts = array();
259
260                 foreach ($rj->geonames as $level) {
261                     if (in_array($level->fcode, array('PCLI', 'ADM1', 'PPL'))) {
262                         $parts[] = $level->name;
263                     }
264                 }
265
266                 $last = $rj->geonames[count($rj->geonames)-1];
267
268                 if (!in_array($level->fcode, array('PCLI', 'ADM1', 'PPL'))) {
269                     $parts[] = $last->name;
270                 }
271
272                 if (count($parts)) {
273                     $name = implode(', ', array_reverse($parts));
274                     return false;
275                 }
276             }
277         }
278
279         return true;
280     }
281
282     /**
283      * Human-readable name for a location
284      *
285      * Given a location, we try to retrieve a geonames.org URL.
286      *
287      * @param Location $location Location to get the url for
288      * @param string   &$url     Place to put the url
289      *
290      * @return boolean whether to continue
291      */
292
293     function onLocationUrl($location, &$url)
294     {
295         if ($location->location_ns != self::NAMESPACE) {
296             // It's not one of our IDs... keep processing
297             return true;
298         }
299
300         $url = 'http://www.geonames.org/' . $location->location_id;
301
302         // it's been filled, so don't process further.
303         return false;
304     }
305 }