Fixes geolocalization.
[platal.git] / include / geoloc.inc.php
1 <?php
2 /***************************************************************************
3 * Copyright (C) 2003-2009 Polytechnique.org *
4 * http://opensource.polytechnique.org/ *
5 * *
6 * This program is free software; you can redistribute it and/or modify *
7 * it under the terms of the GNU General Public License as published by *
8 * the Free Software Foundation; either version 2 of the License, or *
9 * (at your option) any later version. *
10 * *
11 * This program is distributed in the hope that it will be useful, *
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
14 * GNU General Public License for more details. *
15 * *
16 * You should have received a copy of the GNU General Public License *
17 * along with this program; if not, write to the Free Software *
18 * Foundation, Inc., *
19 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
20 ***************************************************************************/
21
22 // {{{ geoloc_country($current, $avail_only = false)
23 /** donne la liste déroulante des pays
24 * @param $current pays actuellement selectionné
25 */
26 function geoloc_country($current, $avail_only = false)
27 {
28 if ($avail_only) {
29 $res = XDB::iterRow('SELECT g.a2, g.pays
30 FROM geoloc_pays AS g
31 INNER JOIN adresses AS a ON(a.country = g.a2)
32 GROUP BY g.a2
33 ORDER BY g.pays');
34 } else {
35 $res = XDB::iterRow('SELECT a2,pays FROM geoloc_pays ORDER BY pays');
36 }
37 $html = "";
38 while (list($my_id, $my_pays) = $res->next()) {
39 $html .= sprintf("<option value=\"%s\" %s>%s</option>\n",
40 $my_id, ($current==$my_id?"selected='selected'":""), $my_pays);
41 }
42 return $html;
43 }
44
45 // }}}
46 // {{{ geoloc_region($country, $current, $avail_only = false)
47 /** donne la liste deroulante des regions pour un pays
48 * @param $pays le pays dont on veut afficher les regions
49 * @param $current la region actuellement selectionnee
50 */
51 function geoloc_region($country, $current, $avail_only = false)
52 {
53 if ($avail_only) {
54 $res = XDB::iterRow('SELECT r.region, r.name
55 FROM geoloc_region AS r
56 INNER JOIN adresses AS a ON (a.country = r.a2 AND a.region = r.region)
57 WHERE r.a2 = {?}
58 GROUP BY r.region
59 ORDER BY r.name', $country);
60 } else {
61 $res = XDB::iterRow('SELECT region,name
62 FROM geoloc_region
63 WHERE a2 = {?}
64 ORDER BY name', $country);
65 }
66 $html = "<option value=\"\"></option>";
67 while (list($regid, $regname) = $res->next()) {
68 $html .= sprintf("<option value=\"%s\" %s>%s</option>\n",
69 $regid, ($current==$regid?"selected='selected'":""), $regname);
70 }
71 return $html;
72 }
73 // }}}
74 // {{{ get_cities_maps($array)
75 /* get all the maps id of the cities contained in an array */
76 function get_cities_maps($array)
77 {
78 global $globals;
79 implode("\n",$array);
80 $url = $globals->geoloc->webservice_url."findMaps.php?datatext=".urlencode(implode("\n", $array));
81 if (!($f = @fopen($url, 'r'))) return false;
82 $maps = array();
83 while (!feof($f))
84 {
85 $l = trim(fgets($f));
86 $tab = explode(';', $l);
87 $i = $tab[0];
88 unset($tab[0]);
89 $maps[$i] = $tab;
90 }
91 return $maps;
92 }
93 // }}}
94 // {{{ get_new_maps($url)
95 /** set new maps from url **/
96 function get_new_maps($url)
97 {
98 if (!($f = @fopen($url, 'r'))) {
99 return false;
100 }
101 XDB::query('TRUNCATE TABLE geoloc_maps');
102 $s = '';
103 while (!feof($f)) {
104 $l = fgetcsv($f, 1024, ';', '"');
105 foreach ($l as $i => $val) {
106 if ($val != 'NULL') {
107 $l[$i] = '\''.addslashes($val).'\'';
108 }
109 }
110 $s .= ',('.implode(',',$l).')';
111 }
112 XDB::execute('INSERT INTO geoloc_maps VALUES '.substr($s, 1));
113 return true;
114 }
115 // }}}
116 // {{{ geolocGoogle (array $address)
117 // retrieve the infos on a text address
118
119 function geolocGoogle (array &$address)
120 {
121 /* keys
122 * www.polytechnique.org:
123 * ABQIAAAAIlFNe1A494mwR9Zf4R3t0xRsw9kzQBeaENRP66lRw7Ru3uVJcRR73lY1tmAdYGqw-pyHTdynmicz0w
124 * www.polytechnique.net and dev.polytechnique.net:
125 * ABQIAAAAIlFNe1A494mwR9Zf4R3t0xT8SmDPc83znji5QwIVTgAvxgX5zRRMagHx_rmGeQF5SnCzmyqiSeSAxA
126 * dev.m4x.org:
127 * ABQIAAAAIlFNe1A494mwR9Zf4R3t0xQ31muaRX97DHXrOFfMwMMCxEnhaxQIPDe9Ct3D6ZvWuGiWllkGAP3IqA
128 *
129 * Documentation:
130 * http://code.google.com/intl/fr/apis/maps/documentation/geocoding/
131 * http://code.google.com/apis/maps/documentation/reference.html#GGeoAddressAccuracy */
132
133 $success = true;
134 $key = 'ABQIAAAAIlFNe1A494mwR9Zf4R3t0xQ31muaRX97DHXrOFfMwMMCxEnhaxQIPDe9Ct3D6ZvWuGiWllkGAP3IqA';
135 $webservice = "http://maps.google.com/maps/geo?";
136 $baseurl = $webservice . "&key=$key" . "&sensor=false&output=json&oe=utf8&gl=fr&hl=fr&q=";
137
138 $url = $baseurl . urlencode($address['text']);
139 if (!geolocalizeAddress($url, $gAddress)) {
140 $addressLines = explode("\n", $address['text']);
141 $nbLines = count($addressLines);
142 $currentState = array();
143 $success = false;
144 for ($i = 1; !$success && ($i < $nbLines); $i++) {
145 for ($j = 0; $j < $i; $j++) {
146 $currentState[$j] = 0;
147 }
148 while($j < $nbLines) {
149 $currentState[$j] = 1;
150 $j++;
151 }
152 do {
153 $partialAddress = "";
154 for ($j = 0; $j < $nbLines; $j++) {
155 if ($currentState[$j] == 1) {
156 $partialAddress .= $addressLines[$j] . " ";
157 }
158 }
159 $url = $baseurl . urlencode(trim($partialAddress));
160 $success = geolocalizeAddress($url, $gAddress);
161 } while (!$success && nextCurrentState($currentState, $nbLines));
162 }
163 if ($success) {
164 $extras = "";
165 for ($i = 0; $i < $nbLines; $i++) {
166 if ($currentState[$i] == 0) {
167 $extras .= $addressLines[$i] . ", ";
168 }
169 }
170 trim($extras, ", ");
171 $address['extras'] = $extras;
172 }
173 }
174 if ($success) {
175 fillAddress($address, $gAddress);
176 formatAddress($address);
177 }
178 return $success;
179 }
180
181 // }}}
182 // {{{ nextCurrentState(&$currentState, $nbLines)
183
184 function nextCurrentState(&$currentState, $nbLines)
185 {
186 $lastOne = 0;
187 $nbZeros = 2;
188 for ($i = 0; $i < $nbLines; $i++) {
189 if ($currentState[$i] == 1) {
190 $lastOne = $i;
191 $nbZeros = 2;
192 } else {
193 $nbZeros++;
194 }
195 }
196 if ($lastOne == 0) {
197 return false;
198 } elseif ($currentState[$lastOne - 1] == 0) {
199 $currentState[$lastOne - 1] = 1;
200 $currentState[$lastOne] = 0;
201 return true;
202 } else {
203 $lastZero = -1;
204 for ($j = 0; $j < $lastOne; $j++) {
205 if ($currentState[$j] == 0) {
206 $lastZero = $j;
207 }
208 }
209 if ($lastZero == -1) {
210 return false;
211 } else {
212 $currentState[$lastZero] = 1;
213 for ($k = $lastZero + 1; $k < $lastZero + $nbZeros; $k++) {
214 $currentState[$k] = 0;
215 }
216 for ($k = $lastZero + $nbZeros; $k < $nbLines; $k++) {
217 $currentState[$k] = 1;
218 }
219 return true;
220 }
221 }
222 }
223
224 // }}}
225 // {{{ geolocalizeAddress ($url, &$result)
226
227 function geolocalizeAddress ($url, &$result = array())
228 {
229 global $globals;
230
231 if ($globals->debug & DEBUG_BT) {
232 if (!isset(PlBacktrace::$bt['Geoloc'])) {
233 new PlBacktrace('Geoloc');
234 }
235 PlBacktrace::$bt['Geoloc']->start($url);
236 }
237
238 if ($f = file_get_contents($url, 'r')) {
239 $data = json_decode($f, true);
240 if ($globals->debug & DEBUG_BT) {
241 PlBacktrace::$bt['Geoloc']->stop(count($data), null, $data);
242 }
243 if ($data['Status']['code'] != 200) {
244 return false;
245 }
246 $nbResults = count($data['Placemark']);
247 $idAccuracy = 0;
248 if ($nbResults > 1) {
249 $bestAccuracy = $data['Placemark'][0]['AddressDetails']['Accuracy'];
250 for ($i = 1; $i < $nbResults; $i++) {
251 if ($data['Placemark'][$i]['AddressDetails']['Accuracy'] > $bestAccuracy) {
252 unset($data['Placemark'][$idAccuracy]);
253 $bestAccuracy = $data['Placemark'][$i]['AddressDetails']['Accuracy'];
254 $idAccuracy = $i;
255 } else {
256 unset($data['Placemark'][$i]);
257 }
258 }
259 }
260 $result = $data['Placemark'][$idAccuracy];
261 return true;
262 }
263 if ($globals->debug & DEBUG_BT) {
264 PlBacktrace::$bt['Geoloc']->stop(0, "Can't fetch result.");
265 }
266 return false;
267 }
268
269 // }}}
270 // {{{ fillAddress(array &$address, $gAddress)
271
272 function fillAddress(array &$address, array $gAddress)
273 {
274 // An address is Country -> AdministrativeArea -> SubAdministrativeArea -> Locality -> Thoroughfare
275 // with all the shortcuts possible
276
277 // postalText
278 $address['geoloc'] = str_replace(", ", "\n", $gAddress['address']);
279 if (isset($gAddress['AddressDetails']['Accuracy'])) {
280 $address['accuracy'] = $gAddress['AddressDetails']['Accuracy'];
281 }
282 $currentPosition = $gAddress['AddressDetails'];
283 if (isset($currentPosition['Country'])) {
284 $currentPosition = $currentPosition['Country'];
285 $address['countryId'] = $currentPosition['CountryNameCode'];
286 $address['country'] = $currentPosition['CountryName'];
287 }
288 if (isset($currentPosition['AdministrativeArea'])) {
289 $currentPosition = $currentPosition['AdministrativeArea'];
290 $address['administrativeAreaName'] = $currentPosition['AdministrativeAreaName'];
291 }
292 if (isset($currentPosition['SubAdministrativeArea'])) {
293 $currentPosition = $currentPosition['SubAdministrativeArea'];
294 $address['subAdministrativeAreaName'] = $currentPosition['SubAdministrativeAreaName'];
295 }
296 if (isset($currentPosition['Locality'])) {
297 $currentPosition = $currentPosition['Locality'];
298 $address['localityName'] = $currentPosition['LocalityName'];
299 }
300 if (isset($currentPosition['Thoroughfare'])) {
301 $address['thoroughfareName'] = $currentPosition['Thoroughfare']['ThoroughfareName'];
302 }
303 if (isset($currentPosition['PostalCode'])) {
304 $address['postalCode'] = $currentPosition['PostalCode']['PostalCodeNumber'];
305 }
306
307 // Coordinates
308 if (isset($gAddress['Point']['coordinates'][0])) {
309 $address['latitude'] = $gAddress['Point']['coordinates'][0];
310 }
311 if (isset($gAddress['Point']['coordinates'][1])) {
312 $address['longitude'] = $gAddress['Point']['coordinates'][1];
313 }
314 if (isset($gAddress['ExtendedData']['LatLonBox']['north'])) {
315 $address['north'] = $gAddress['ExtendedData']['LatLonBox']['north'];
316 }
317 if (isset($gAddress['ExtendedData']['LatLonBox']['south'])) {
318 $address['south'] = $gAddress['ExtendedData']['LatLonBox']['south'];
319 }
320 if (isset($gAddress['ExtendedData']['LatLonBox']['east'])) {
321 $address['east'] = $gAddress['ExtendedData']['LatLonBox']['east'];
322 }
323 if (isset($gAddress['ExtendedData']['LatLonBox']['west'])) {
324 $address['west'] = $gAddress['ExtendedData']['LatLonBox']['west'];
325 }
326 }
327
328 // }}}
329 // {{{ formatAddress(array &$address)
330
331 function formatAddress(array &$address)
332 {
333 $same = true;
334 $text = strtoupper(preg_replace(array("/[0-9,\"'#~:;_\- ]/", "/\r\n/"),
335 array("", "\n"), $address['text']));
336 $geoloc = strtoupper(preg_replace(array("/[0-9,\"'#~:;_\- ]/", "/\r\n/"),
337 array("", "\n"), $address['geoloc']));
338 if (isset($address['extras']) && $address['extras']) {
339 $address['geoloc'] = $address['extras'] . "\n" . $address['geoloc'];
340 $extras = strtoupper(preg_replace(array("/[0-9,\"'#~:;_\- ]/", "/\r\n/"),
341 array("", "\n"), $address['extras']));
342 $geoloc = $extras . $geoloc;
343 unset($address['extras']);
344 }
345
346 $arrayText = explode("\n", $text);
347 $arrayGeoloc = explode("\n", $geoloc);
348 $nbText = count($arrayText);
349 $nbGeoloc = count($arrayGeoloc);
350
351 if ((($nbText > $nbGeoloc) || ($nbText < $nbGeoloc - 1))
352 || (($nbText == $nbGeoloc - 1) && ($arrayText[$nbText - 1] == strtoupper($address['country'])))) {
353 $same = false;
354 } else {
355 foreach ($arrayText as $i => $lignText) {
356 if (levenshtein($lignText, trim($arrayGeoloc[$i])) > 3) {
357 $same = false;
358 }
359 }
360 }
361 if ($same) {
362 $address['text'] = $address['geoloc'];
363 unset($address['geoloc']);
364 }
365 }
366
367 // }}}
368 // {{{ cleanText(&$text)
369
370 function cleanText(&$text)
371 {
372 $lines = explode("\n", $text);
373 $n = count($lines);
374 $text = "";
375 for ($i = 0; $i < $n; $i++) {
376 if (trim($lines[$i])) {
377 $text .= trim($lines[$i]) . "\n";
378 }
379 }
380 $text = trim($text);
381 }
382
383 // }}}
384 // {{{ getAreaId(array &$address, $area)
385
386 function getAreaId(array &$address, $area)
387 {
388 if (isset($address[$area . 'Name'])) {
389 $res = XDB::query("SELECT id
390 FROM geoloc_" . $area . "
391 WHERE name = {?}",
392 $address[$area . 'Name']);
393 if ($res->numRows() == 0) {
394 $address[$area . 'Id'] = XDB::execute("INSERT INTO geoloc_" . $area . " (name, country)
395 VALUES ({?}, {?})",
396 $address[$area . 'Name'], $address['countryId']);
397 } else {
398 $address[$area . 'Id'] = $res->fetchOneCell();
399 }
400 }
401 }
402
403 // }}}
404 // {{{ get_address_text($adr)
405 /** make the text of an address that can be read by a mailman
406 * @param $adr an array with all the usual fields
407 */
408 function get_address_text($adr)
409 {
410 $t = "";
411 if (isset($adr['adr1']) && $adr['adr1']) $t.= $adr['adr1'];
412 if (isset($adr['adr2']) && $adr['adr2']) $t.= "\n".$adr['adr2'];
413 if (isset($adr['adr3']) && $adr['adr3']) $t.= "\n".$adr['adr3'];
414 $l = "";
415 if (isset($adr['display']) && $adr['display']) {
416 $keys = explode(' ', $adr['display']);
417 foreach ($keys as $key) {
418 if (isset($adr[$key])) {
419 $l .= " ".$adr[$key];
420 } else {
421 $l .= " ".$key;
422 }
423 }
424 if ($l) substr($l, 1);
425 } elseif ($adr['country'] == 'US' || $adr['country'] == 'CA' || $adr['country'] == 'GB') {
426 if ($adr['city']) $l .= $adr['city'].",\n";
427 if ($adr['region']) $l .= $adr['region']." ";
428 if ($adr['postcode']) $l .= $adr['postcode'];
429 } else {
430 if (isset($adr['postcode']) && $adr['postcode']) $l .= $adr['postcode']." ";
431 if (isset($adr['city']) && $adr['city']) $l .= $adr['city'];
432 }
433 if ($l) $t .= "\n".trim($l);
434 if ($adr['country'] != '00' && (!$adr['countrytxt'] || $adr['countrytxt'] == strtoupper($adr['countrytxt']))) {
435 $res = XDB::query("SELECT pays FROM geoloc_pays WHERE a2 = {?}", $adr['country']);
436 $adr['countrytxt'] = $res->fetchOneCell();
437 }
438 if (isset($adr['countrytxt']) && $adr['countrytxt']) {
439 $t .= "\n".$adr['countrytxt'];
440 }
441 return trim($t);
442 }
443 // }}}
444 // {{{ compare_addresses_text($a, $b)
445 /** compares if two address matches
446 * @param $a the raw text of an address
447 * @param $b the raw text of a complete valid address
448 */
449 function compare_addresses_text($a, $b)
450 {
451 $ta = strtoupper(preg_replace(array("/[0-9,\"'#~:;_\- ]/", "/\r\n/"), array("", "\n"), $a));
452 $tb = strtoupper(preg_replace(array("/[0-9,\"'#~:;_\- ]/", "/\r\n/"), array("", "\n"), $b));
453
454 $la = explode("\n", $ta);
455 $lb = explode("\n", $tb);
456
457 if (count($lb) > count($la) + 1) {
458 return false;
459 }
460 foreach ($la as $i => $l) {
461 if (levenshtein(trim($l), trim($lb[$i])) > 3) {
462 return false;
463 }
464 }
465 return true;
466 }
467
468 // }}}
469 // {{{ fixNumber($oldtext, &$new)
470
471 function fixNumber($oldtext, &$new)
472 {
473 $ThoroughfareName = $new['AddressDetails']['Country']['AdministrativeArea']['SubAdministrativeArea']['Locality']['Thoroughfare']['ThoroughfareName'];
474 $ThoroughfareName = trim(strtoupper(preg_replace(array("/[,\"'#~:;_\-]/", "/\r\n/"),
475 array("", "\n"), $ThoroughfareName)));
476 $oldarray = explode("\n", trim(strtoupper(preg_replace(array("/[,\"'#~:;_\-]/", "/\r\n/"),
477 array("", "\n"), $oldtext))));
478 $mindist = strlen($ThoroughfareName);
479 $minpos = 0;
480 foreach ($oldarray as $i => $oldline) {
481 if (($l = levenshtein(trim($oldline), $ThoroughfareName)) < $mindist) {
482 $mindist = $l;
483 $minpos = $i;
484 }
485 }
486 $nb = explode(" ", $oldarray[$minpos]);
487 $new['text'] = $nb[0] . " " . $new['text'];
488 }
489
490 // }}}
491 // {{{ localize_addresses($uid)
492 /* localize all the address of a user and modify the database
493 * if the new address match with the old one
494 * @param $uid the id of the user
495 */
496 function localize_addresses($uid)
497 {
498 $res = XDB::iterator("SELECT *
499 FROM adresses
500 WHERE uid = {?} and (cityid IS NULL OR cityid = 0)", $uid);
501 $erreur = Array();
502
503 while ($a = $res->next()) {
504 $new = get_address_infos($ta = get_address_text($a));
505 if (compare_addresses_text($ta, get_address_text($new))) {
506 XDB::execute("UPDATE adresses
507 SET adr1 = {?}, adr2 = {?}, adr3 = {?},
508 cityid = {?}, city = {?}, postcode = {?},
509 region = {?}, regiontxt = {?}, country = {?},
510 glat = {?}, glng = {?}
511 WHERE uid = {?} AND adrid = {?}",
512 $new['adr1'], $new['adr2'], $new['adr3'],
513 $new['cityid'], $new['city'], $new['postcode'],
514 $new['region'], $new['regiontxt'], $new['country'],
515 $new['precise_lat'], $new['precise_lon'],
516 $uid, $a['adrid']);
517 $new['store'] = true;
518 if (!$new['cityid']) {
519 $erreur[$a['adrid']] = $new;
520 }
521 } else {
522 $new['store'] = false;
523 $erreur[$a['adrid']] = $new;
524 }
525 }
526 return $erreur;
527 }
528 // }}}
529 // {{{ get_address_infos($txt)
530 /** retrieve the infos on a text address
531 * store on the fly the info of the city concerned
532 * @param $txt the raw text of an address
533 */
534
535 function get_address_infos($txt)
536 {
537 global $globals;
538
539 $url = $globals->geoloc->webservice_url."address.php?precise=1&txt=" . urlencode($txt);
540 if ($globals->debug & DEBUG_BT) {
541 if (!isset(PlBacktrace::$bt['Geoloc'])) {
542 new PlBacktrace('Geoloc');
543 }
544 PlBacktrace::$bt['Geoloc']->start($url);
545 }
546 $f = @fopen($url, 'r');
547 if ($f === false) {
548 if ($globals->debug & DEBUG_BT) {
549 PlBacktrace::$bt['Geoloc']->stop(0, 'Can\'t fetch result');
550 }
551 return false;
552 }
553 $keys = explode('|',fgets($f));
554 $vals = explode('|',fgets($f));
555 if ($globals->debug & DEBUG_BT) {
556 $data = array();
557 for ($i = 0 ; $i < count($keys) ; ++$i) {
558 $data[] = array($keys[$i], $vals[$i]);
559 }
560 PlBacktrace::$bt['Geoloc']->stop(count($keys), null, $data);
561 }
562 $infos = empty_address();
563 foreach ($keys as $i=>$key) {
564 if($vals[$i]) {
565 if ($key == 'sql') {
566 $infos[$key] = $vals[$i];
567 } else {
568 $val = strtr($vals[$i], array(chr(197).chr(147) => "&oelig;"));
569 $infos[$key] = $val;
570 }
571 }
572 }
573 if (empty($infos['country'])) {
574 $infos['country'] = '00';
575 }
576 if (isset($infos['sql']) && $infos['sql']) {
577 $sql = explode(', ', trim($infos['sql'], '()'));
578 if (count($sql) == 16) {
579 for ($i = 0 ; $i < 16 ; ++$i) {
580 $sql[$i] = stripslashes(trim($sql[$i], ' \''));
581 }
582 XDB::execute("REPLACE INTO geoloc_city
583 VALUES ({?}, {?}, {?}, {?}, {?}, {?},
584 {?}, {?}, {?}, {?}, {?}, {?},
585 {?}, {?}, {?}, {?})",
586 $sql[0], $sql[1], $sql[2], $sql[3], $sql[4], $sql[5],
587 $sql[6], $sql[7], $sql[8], $sql[9], $sql[10], $sql[11],
588 $sql[12], $sql[13], $sql[14], $sql[15]);
589 }
590 }
591 if (isset($infos['display']) && $infos['display'])
592 XDB::execute("UPDATE geoloc_pays
593 SET display = {?}
594 WHERE a2 = {?}", $infos['display'], $infos['country']);
595 if (isset($infos['cityid'])) {
596 fix_cities_not_on_map(1, $infos['cityid']);
597 if (floatval($infos['precise_lat']) && floatval($infos['precise_lon'])) {
598 $res = XDB::query("SELECT c.lat / 100000, c.lon / 100000
599 FROM geoloc_city AS c
600 WHERE c.id = {?}", $infos['cityid']);
601 if ($res->numRows()) {
602 list($glat, $glng) = $res->fetchOneRow();
603 $infos['precise_lat'] = $glat;
604 $infos['precise_lon'] = $glng;
605 }
606 }
607 }
608 return $infos;
609 }
610
611 // }}}
612 // {{{ synchro_city($id)
613 /** synchronise the local geoloc_city base to geoloc.org
614 * @param $id the id of the city to synchronize
615 */
616 function synchro_city($id)
617 {
618 global $globals;
619 $url = $globals->geoloc->webservice_url."cityFinder.php?method=id&id=".$id."&out=sql";
620 if (!($f = @fopen($url, 'r'))) {
621 return false;
622 }
623 $s = fgets($f);
624 if ($s) {
625 return XDB::execute("REPLACE INTO geoloc_city VALUES ".$s) > 0;
626 }
627 }
628 // }}}
629 // {{{ function fix_cities_not_on_map($limit)
630 function fix_cities_not_on_map($limit=false, $cityid=false)
631 {
632 $missing = XDB::query("SELECT c.id
633 FROM geoloc_city AS c
634 LEFT JOIN geoloc_city_in_maps AS m ON(c.id = m.city_id)
635 WHERE m.city_id IS NULL"
636 . ($cityid ? " AND c.id = '" . $cityid . "'" : "" )
637 . ($limit ? " LIMIT $limit" : "" ));
638 $maps = get_cities_maps($missing->fetchColumn());
639 if ($maps) {
640 $values = "";
641 foreach ($maps as $cityid => $maps_c) {
642 foreach ($maps_c as $map_id) {
643 $values .= ",($cityid, $map_id, '')";
644 }
645 }
646 if (strlen($values) > 1) {
647 XDB::execute("REPLACE INTO geoloc_city_in_maps
648 VALUES ".substr($values, 1));
649 }
650 } else {
651 return false;
652 }
653 return true;
654 }
655
656 function set_smallest_levels()
657 {
658 $maxlengths = XDB::iterRow("SELECT MAX(LENGTH(gm.path)), gcim.city_id
659 FROM geoloc_city_in_maps AS gcim
660 INNER JOIN geoloc_maps AS gm USING ( map_id )
661 GROUP BY gcim.city_id");
662 while (list($length, $id) = $maxlengths->next()) {
663 XDB::execute("UPDATE geoloc_city_in_maps AS gcim
664 INNER JOIN geoloc_maps AS gm USING(map_id)
665 SET gcim.infos = IF(LENGTH(gm.path) = {?}, 'smallest', '')
666 WHERE gcim.city_id = {?}", $length, $id);
667 }
668 return true;
669 }
670 // }}}
671
672 function geoloc_to_x($lon, $lat)
673 {
674 return deg2rad(1) * $lon *100;
675 }
676
677 function geoloc_to_y($lon, $lat)
678 {
679 if ($lat < -75) {
680 return latToY(-75);
681 }
682 if ($lat > 75) {
683 return latToY(75);
684 }
685 return -100 * log(tan(pi()/4 + deg2rad(1)/2*$lat));
686 }
687
688 function size_of_city($nb)
689 {
690 $s = round(log($nb + 1)*2,2);
691 if ($s < 1) {
692 return 1;
693 }
694 return $s;
695 }
696
697 function size_of_territory($nb)
698 {
699 return size_of_city($nb);
700 }
701
702 function geoloc_getData_subcities($mapid, $SFields, &$cities, $direct=true)
703 {
704 if ($SFields instanceof UserSet) {
705 $set = $SFields;
706 $SFields = array();
707 } else {
708 $set = new UserSet();
709 }
710 for ($i_mapfield=0; $i_mapfield < count($SFields) ; $i_mapfield++) {
711 if ($SFields[$i_mapfield]->fieldFormName == 'mapid') {
712 break;
713 }
714 }
715 $SFields[$i_mapfield] = new MapSField('mapid',
716 array('gcim.map_id'),
717 array('adresses','geoloc_city_in_maps'),
718 array('am','gcim'),
719 array(getadr_join('am'), 'am.cityid = gcim.city_id'),
720 $mapid);
721 $fields = new SFieldGroup(true, $SFields);
722 $where = $fields->get_where_statement();
723 $joins = $fields->get_select_statement();
724 if ($where) {
725 $where .= ' AND ';
726 }
727 $cityres = $set->get('gc.id,
728 gc.lon / 100000 AS x, gc.lat/100000 AS y,
729 gc.name,
730 COUNT(u.user_id) AS pop,
731 SUM(u.promo % 2) AS yellow',
732 "$joins
733 LEFT JOIN geoloc_city AS gc ON(gcim.city_id = gc.id)",
734 $where . ($direct ? "gcim.infos = 'smallest'" : '1'),
735 'gc.id, gc.alias',
736 'pop DESC');
737 foreach($cityres as $c) {
738 if ($c['pop'] > 0) {
739 $city = $c;
740 $city['x'] = geoloc_to_x($c['x'], $c['y']);
741 $city['y'] = geoloc_to_y($c['x'], $c['y']);
742 $city['size'] = size_of_city($c['pop']);
743 $cities[$c['id']] = $city;
744 }
745 }
746 }
747
748 function geoloc_getData_subcountries($mapid, $sin, $minentities)
749 {
750 $countries = array();
751 $cities = array();
752
753 if ($mapid === false) {
754 $wheremapid = "WHERE gm.parent IS NULL";
755 } else {
756 $wheremapid = "WHERE gm.parent = {?}";
757 }
758 $submapres = XDB::iterator(
759 "SELECT gm.map_id AS id, gm.name, gm.x, gm.y, gm.xclip, gm.yclip,
760 gm.width, gm.height, gm.scale, 1 AS rat
761 FROM geoloc_maps AS gm
762 ". $wheremapid, Env::v('mapid',''));
763
764 global $globals;
765
766 while ($c = $submapres->next()) {
767 $country = $c;
768 $country['color'] = 0xFFFFFF;
769 $country['swf'] = $globals->geoloc->webservice_url."maps/mercator/map_".$c['id'].".swf";
770 $countries[$c['id']] = $country;
771 }
772
773 if ($mapid === false) {
774 return array($countries, $cities);
775 }
776
777 geoloc_getData_subcities(Env::i('mapid'), $sin, $cities);
778 $nbcities = count($cities);
779 $nocity = $nbcities == 0;
780 if ($sin instanceof UserSet) {
781 $set = $sin;
782 $SFields = array();
783 } else {
784 $set = new UserSet();
785 $SFields = $sin;
786 }
787
788 for ($i_mapfield=0; $i_mapfield < count($SFields) ; $i_mapfield++) {
789 if ($SFields[$i_mapfield]->fieldFormName == 'mapid') {
790 break;
791 }
792 }
793 $SFields[$i_mapfield] = new MapSField('mapid',
794 array('map.parent'),
795 array('adresses','geoloc_city_in_maps','geoloc_maps'),
796 array('am','gcim','map'),
797 array(getadr_join('am'),
798 'am.cityid = gcim.city_id',
799 'map.map_id = gcim.map_id'));
800 $fields = new SFieldGroup(true, $SFields);
801 $where = $fields->get_where_statement();
802 $joins = $fields->get_select_statement();
803 $countryres = $set->get('map.map_id AS id,
804 COUNT(u.user_id) AS nbPop,
805 SUM(u.promo % 2) AS yellow,
806 COUNT(DISTINCT gcim.city_id) AS nbCities,
807 SUM(IF(u.user_id IS NULL,0,am.glng)) AS lonPop,
808 SUM(IF(u.user_id IS NULL, 0,am.glat)) AS latPop',
809 $joins,
810 $where,
811 'map.map_id',
812 'NULL');
813
814 $maxpop = 0;
815 $nbentities = $nbcities + count($countryres);
816 foreach ($countryres as $c) {
817 $c['latPop'] /= $c['nbPop'];
818 $c['lonPop'] /= $c['nbPop'];
819 $c['rad'] = size_of_territory($c['nbPop']);
820 if ($maxpop < $c['nbPop']) $maxpop = $c['nbPop'];
821 $c['xPop'] = geoloc_to_x($c['lonPop'], $c['latPop']);
822 $c['yPop'] = geoloc_to_y($c['lonPop'], $c['latPop']);
823 @$countries[$c['id']] = array_merge($countries[$c['id']], $c);
824
825 $nbcities += $c['nbCities'];
826 }
827
828 if ($nocity && $nbcities < $minentities){
829 foreach($countries as $i => $c) {
830 $countries[$i]['nbPop'] = 0;
831 if (@$c['nbCities'] > 0) {
832 geoloc_getData_subcities($c['id'], $sin, $cities, false);
833 }
834 }
835 }
836
837 foreach ($countries as $i => $c) {
838 if (@$c['nbPop'] > 0) {
839 $lambda = pow($c['nbPop'] / $maxpop,0.3);
840 $countries[$i]['color'] = 0x0000FF + round((1-$lambda) * 0xFF)*0x010100;
841 }
842 }
843
844 return array($countries, $cities);
845 }
846
847 // vim:set et sw=4 sts=4 sws=4 foldmethod=marker enc=utf-8:
848 ?>