/[drupal]/contributions/modules/geonames_cck/geoapi.module
ViewVC logotype

Contents of /contributions/modules/geonames_cck/geoapi.module

Parent Directory Parent Directory | Revision Log Revision Log | View Revision Graph Revision Graph


Revision 1.6 - (show annotations) (download) (as text)
Wed Oct 28 14:16:55 2009 UTC (4 weeks, 2 days ago) by thegreenman
Branch: MAIN
CVS Tags: HEAD
Changes since 1.5: +478 -376 lines
File MIME type: text/x-php
Some new updates. There are going to be a few bits of School of Everything only code in here.
But it should get things going.
1 <?php
2 /* $Id: geoapi.module,v 1.4 2008/08/04 16:34:26 thegreenman Exp $ */
3 /**
4 * @file
5 * An api for easy management of CCK geo location information.
6 *
7 * This module provides some code and web level api functions.
8 *
9 *
10 */
11
12 /* ======================= API ===========================*/
13
14 /**
15 * Get the lat and lng for a location.
16 * Call the geonames api to find a matching place
17 *
18 * @param $query
19 * The name of a place to search for
20 * @param $country
21 * Limit searches to a specific country
22 */
23 function geoapi_convert_location_to_geo($query, $country = 'uk') {
24 // we start by just searching on the name
25 // if we get no results, we broaden the search a bit
26 $query = array(
27 'query' => $query,
28 'country' => $country,
29 'featureclass' => 'p',
30 'style' => 'FULL',
31 'maxrows' => 15,
32 );
33
34 $location = geonames_query('search', $query);
35 return $location;
36 }
37
38 /**
39 * Set the value of the location field to the specified geoname id.
40 *
41 * @param $nid #
42 * Node id to update
43 * @param $field
44 * The name of the cck location field to update
45 * @param $index
46 * For multivalue fields, the index of the value to update.
47 * Set to -1 to append a new value.
48 * @param $geonameid
49 * The geonames id of the location we are using
50 */
51 function geoapi_insert_geoname_location($nid, $field, $index, $geonameid) {
52 if (node_access('update', node_load($nid)) && is_numeric($nid) && $field && is_numeric($geonameid)) {
53 // get the name of the primary location field
54 // look up geoname id using heirarchy
55 $location = _geoapi_get_data_for_geonameid($geonameid);
56 $location = _geoapi_convert_geonames_result_to_cck_value($location);
57 _geoapi_set_field_value($nid, $field, $index, $location);
58 }
59 else {
60 drupal_access_denied();
61 }
62 }
63
64 /**
65 * Delete a location entry from a node
66 *
67 * @param unknown_type $nid
68 * @param unknown_type $field The cck field to update
69 * @param unknown_type $counter Which value in the field to delete
70 */
71 function geoapi_delete_geoname_location($nid, $field, $index = 0 ) {
72 //TODO: allow the deletion of location info via web
73 }
74
75
76 /**
77 * Create a list of country codes for a dropdown box
78 * If a code is specified, will return the name of the country with that code
79 *
80 * @param $code
81 * If specified, return the name of the country with the matching code
82 * @return mixed
83 * If no code is specified, an array of countries and their codes is returned.
84 * If a code is provided, the name of that country will be returned as a string
85 */
86 function geoapi_countries($code = null, $include_empty = true) {
87 if (!is_null($code)) {
88 return countries_api_get_name($code);
89 }
90 else {
91 $countries_raw = countries_api_get_list();
92 if ($include_empty) {
93 $countries = array('' => t('Select a country'));
94 }
95 foreach ($countries_raw as $country) {
96 $countries[$country['iso2']] = $country['name'];
97 }
98 }
99 return $countries;
100 }
101
102 /**
103 * Returns a list of countries trimmed to a specified character length.
104 *
105 * @param unknown_type $code
106 */
107 function geoapi_countries_trim($len = 30) {
108 $countries = geoapi_countries();
109 foreach ($countries as $code => $name) {
110 $trimmed[$code] = trim(truncate_utf8($name, $len, FALSE));
111 }
112 return $trimmed;
113 }
114
115 /**
116 * Alter the lat and long data for an existing geonames_cck field
117 *
118 * @param unknown_type $nid
119 * @param unknown_type $field
120 * @param unknown_type $index
121 * @param unknown_type $lat
122 * @param unknown_type $lng
123 */
124 function geoapi_alter_geoname_latlng($nid, $field, $index, $lat, $lng) {
125 $node = node_load($nid);
126 if (node_access('update', $node) && $field) {
127 if ($location = $node->${field}[$index]) {
128 $location['lat']=$lat;
129 $location['lng']=$lng;
130 _geoapi_set_field_value($nid, $field, $index, $location);
131 }
132 }
133 else {
134
135 }
136 }
137
138
139 /**
140 * Alters a views query object with proximity sort data
141 *
142 * @param unknown_type $query
143 * @param unknown_type $field
144 * @param unknown_type $geodata
145 */
146 function geoapi_proximity_nodes($field, $geodata, $node_types, $distance = 20, $limit = null) {
147 include_once(drupal_get_path('module', 'geonames_cck') .'/geonames_cck_earth.inc');
148 $db_info = content_database_info($field);
149 $table = $db_info['table'];
150 $latcol = $db_info['columns']['lat']['column'];
151 $lngcol = $db_info['columns']['lng']['column'];
152 $lat = $geodata['lat'];
153 $lng = $geodata['lng'];
154 if (!$lat || !$lng) return;
155 $divisor = 1609.347;
156 $latrange = earth_latitude_range($lng, $lat, ($distance * $divisor));
157 $lngrange = earth_longitude_range($lng, $lat, ($distance * $divisor));
158 $orderby = ' ORDER BY '.earth_distancesqlared_sql($lng, $lat, 'geo' , $latcol, $lngcol). ' ASC, RAND()'; // only randomises nodes with same distance
159 $types = implode("','", (array) $node_types);
160 if ($limit > 0) {
161 $limit = " LIMIT $limit";
162 }
163 $sql = "SELECT n.nid FROM {$table} geo
164 INNER JOIN {node} n ON (geo.nid = n.nid)
165 WHERE geo.$latcol > %f AND geo.$latcol < %f AND geo.$lngcol > %f AND geo.$lngcol < %f
166 AND n.type IN ('%s')
167 $orderby
168 $limit";
169 $result = db_query($sql, array($latrange[0], $latrange[1], $lngrange[0], $lngrange[1], $types));
170 while ($row = db_fetch_array($result)) {
171 $nodes[$row['nid']] = $row;
172 }
173 return $nodes;
174 }
175
176 // ============== Internals ============== //
177
178
179
180 /**
181 * Get the location details for an id.
182 * We are using heirarchy search for this, but it may not be the most efficient.
183 * The geonames module handles caching, so we dont bother
184 */
185 function _geoapi_get_data_for_geonameid($geonameid) {
186 static $geodata = array();
187 if ($geonameid) {
188 if (!$location = $geodata[$geonameid]) {
189 $query = array(
190 'geonameid' => $geonameid,
191 'style' => 'FULL',
192 );
193 $result = geonames_query('hierarchy', $query);
194 if (is_array($result -> results)) {
195 $location = array_pop($result -> results);
196 }
197 $geodata[$geonameid] = $location;
198 }
199 }
200 return $location;
201 }
202
203
204 /**
205 * Set a cck field value in the database.
206 * @param nid
207 * @param field
208 * @param index Set to -1 to append values to multivalue fields
209 */
210 function _geoapi_set_field_value($nid, $field, $index = -1, $value) {
211 $field_data = content_fields($field);
212
213 $node = node_load($nid, NULL, true);
214 // load the current values
215 $values = content_field('load', $node, $field_data, $node -> {$field}, NULL, NULL);
216 if ($index < 0) {
217 $values[$field][] = $value;
218 }
219 else {
220 $values[$field][$index] = $value;
221 }
222 content_field('update', $node, $field_data, $values[$field], NULL, NULL);
223 // we have gone underneath the normal node interface, so have to clear our node cache
224 cache_clear_all('content:'. $node->nid .':'. $node -> vid, 'cache_content');
225 if (function_exists('search_wipe')) search_wipe($node->nid, 'node', true); // re-index this node for search
226 module_invoke_all('geoapi', 'update', $node, $field, $index, $value);
227
228 // We can't easily call nodeapi here - the field updates get lost
229 //node_invoke_nodeapi($node, 'update');
230 }
231
232
233 /**
234 * Convert the array structure returned by a geonames call to an array that can be written to a cck field.
235 *
236 */
237 function _geoapi_convert_geonames_result_to_cck_value($result) {
238 $map = array(
239 // cck field => result field
240 'name' => 'name',
241 'lat' => 'lat',
242 'lng' => 'lng',
243 'country' => 'countrycode',
244 'geonameId' => 'geonameid',
245 'fcode' => 'fcode',
246 'adminName1' => 'adminname1',
247 'adminName2' => 'adminname2',
248 'adminCode1' => 'admincode1',
249 'adminCode2' => 'admincode2',
250 );
251 foreach ($map as $cck => $geonames) {
252 $value[$cck] = $result[$geonames];
253 }
254 return $value;
255 }
256
257 /**
258 * Some geolocation stuff. expand it sometime.
259 */
260
261 /**
262 * if there is no existing country information, set the default
263 *
264 */
265 function geoapi_get_default_country() {
266 //TODO: Make other modules possible
267 //$geolocation_module = 'geoapi_iplocator_maxmind';
268 // we dont want to set any default on the content type admin pages. This causes bad defaults.
269 if (!( arg(0) == 'admin' && arg(1) == 'content' && arg(2) == 'types') ) {
270 // force a test ip if one exists in the request
271 //if (!$ip = $_REQUEST['TEST_IP_ADDRESS']) {
272 // $ip = $_SERVER['REMOTE_ADDR'];
273 //}
274 //$location = module_invoke($geolocation_module, 'goapi_iplocation', $ip);
275 if (function_exists('soe_explore_get_best_user_country')) {
276 $location['country'] = soe_explore_get_best_user_country();
277 }
278 return $location['country'];
279 }
280 }
281
282 /**
283 * Detect the default city
284 *
285 * @return unknown
286 */
287 function geoapi_get_default_city() {
288 //TODO: Make other modules possible
289 $geolocation_module = 'geoapi_iplocator_maxmind';
290 // we dont want to set any default on the content type admin pages. This causes bad defaults.
291 if (!( arg(0) == 'admin' && arg(1) == 'content' && arg(2) == 'types') ) {
292 if (!$ip = $_REQUEST['TEST_IP_ADDRESS']) {
293 $ip = $_SERVER['REMOTE_ADDR'];
294 }
295 $location = module_invoke($geolocation_module, 'goapi_iplocation', $ip);
296 // TODO: Make regions work for other locations
297 // This format fails for geonames searches, temporarly disabled
298 if (0 && $location['country'] == 'US') {
299 $city = $location['city'] .', '. $location['region'];
300 }
301 else {
302 $city = $location['city'];
303 }
304 return $city;
305 }
306 }
307
308
309 // ============== REST INTERFACE
310 /**
311 * This is a simple implementation of the service using hook_menu.
312 */
313
314 /**
315 * implementation of hook_menu
316 */
317 function geoapi_menu($may_cache) {
318 if ($may_cache) {
319 $items[] = array(
320 'path' => 'services/location/geonames',
321 'callback' => '_geoapi_service_handler',
322 'access' => true,
323 'type' => MENU_CALLBACK,
324 );
325 }
326 return $items;
327 }
328
329 /**
330 * Handle the REST calls to the api
331 */
332 function _geoapi_service_handler($action, $nid, $field, $index, $a1, $a2 = null, $a3 = null) {
333 switch ($action) {
334 case 'insert':
335 geoapi_insert_geoname_location($nid, $field, $index, $a1);
336 drupal_goto('node/'. $nid, "geoapi-insert");
337 break;
338 case 'latlng':
339 geoapi_alter_geoname_latlng($nid, $field, $index, $a1, $a2, $a3);
340 if (!$a3 == 'background') drupal_goto('node/'. $nid);
341 break;
342 }
343 }
344
345
346 //====================== ALIASES
347 /**
348 * We can maintain aliases for geonameID's to make URLs nicer
349 *
350 *
351 */
352
353
354
355 /**
356 * returns the tid of an alias if it exists.
357 * @param $alias A processed string containing the exact alias to search for
358 */
359 function geoapi_get_geonameid_for_alias($alias, $clean = false, $reset = false) {
360 if (!$alias or is_numeric($alias)) {
361 return $alias;
362 }
363 static $aliases = array();
364 if ($reset) $aliases = array();
365 if ($clean) $alias = drupal_strtolower(pathauto_cleanstring($alias));
366 if (!$id = $aliases[$alias]) {
367 $id = db_result(db_query("select geonameid from {geonamesalias} where alias = '%s'", $alias));
368 $aliases[$alias] = $id;
369 }
370 return $id;
371 }
372
373
374 /**
375 * Returns an alias for this term, creating a new alias if needed.
376 *
377 * @param unknown_type $tid
378 */
379 function geoapi_get_alias_for_geonameid($id) {
380 if ($id) {
381 $alias = _geoapi_load_alias_for_geonameid($id);
382 if (!$alias) {
383 if ( strlen($id) == 2 && is_string(geoapi_countries($id))) {
384 // we have a country code
385 $alias = $id;
386 }
387 else if (is_numeric($id)) {
388 // check to see if we should create an alias
389 $geodata = _geoapi_get_data_for_geonameid($id);
390 if ($geodata['name']) {
391 $alias = geoapi_create_alias($geodata);
392 }
393 }
394 }
395 }
396 return $alias;
397 }
398
399 /**
400 * Load the alias for term.
401 *
402 * @param unknown_type $tid
403 * @param unknown_type $reset
404 * @return unknown
405 */
406 function _geoapi_load_alias_for_geonameid($id, $reset = false) {
407 static $aliases = array();
408 if ($reset) $aliases = array();
409 if (!$alias = $aliases[$id]) {
410 $alias = db_result(db_query('select alias from {geonamesalias} where geonameid = %d', $id));
411 $aliases[$id] = $alias;
412 }
413 return $alias;
414 }
415
416
417 /**
418 * processes a string, removing unwanted characters.
419 * Based on pathauto
420 *
421 */
422 function geoapi_create_alias($geodata){
423 if (!function_exists('pathauto_cleanstring')) {
424 $pathauto_path = drupal_get_path('module', 'pathauto');
425 require_once("$pathauto_path/pathauto.inc");
426 }
427 $alias = pathauto_cleanstring($geodata['name']);
428 $alias = drupal_strtolower($alias);
429 $maxlength = 50;
430 $alias = drupal_substr($alias, 0, $maxlength);
431 // If the alias already exists, generate a new variant
432 $separator = variable_get('pathauto_separator', '-');
433
434 if (geoapi_get_geonameid_for_alias($alias)) {
435 for ($i = 0; geoapi_get_geonameid_for_alias($alias . $separator . $i); $i++) {
436 }
437 // Make room for the sequence number
438 $alias = drupal_substr($alias, 0, $maxlength - 1 -($i/10 + 1));
439 $alias = $alias . $separator . $i;
440 }
441 db_query("insert into {geonamesalias} (geonameid, alias) values (%d, '%s')", $geodata['geonameid'], $alias);
442 return $alias;
443 }
444
445
446
447
448 // ========== COUNTRIES
449
450 /**
451 * Get the location details for an id.
452 * We are using heirarchy search for this, but it may not be the most efficient.
453 * The geonames module handles caching, so we dont bother
454 */
455 function _geoapi_get_country_data($country) {
456 static $geodata = array();
457 if ($country) {
458 if (!$data = $geodata[$country]) {
459 $query = array('country' => $country);
460 $result = geonames_query('countryinfo', $query);
461 if (is_array($result -> results)) {
462 $data = array_pop($result -> results);
463 }
464 $geodata[$country] = $data;
465 }
466 }
467 return $data;
468 }
469
470
471 // ========== KML
472
473 function geoapi_send_kml($kml) {
474 drupal_set_header('Content-Type: text/xml; charset=utf-8');
475 print $kml;
476 module_invoke_all('exit');
477 exit;
478 }

  ViewVC Help
Powered by ViewVC 1.1.2