Switches geolocalization to Google Maps.
[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'])) {
339 $extras = strtoupper(preg_replace(array("/[0-9,\"'#~:;_\- ]/", "/\r\n/"),
340 array("", "\n"), $address['extras']));
341 $geoloc = $extras . $geoloc;
342 unset($address['extras']);
343 }
344
345 $arrayText = explode("\n", $text);
346 $arrayGeoloc = explode("\n", $geoloc);
347 $nbText = count($arrayText);
348 $nbGeoloc = count($arrayGeoloc);
349
350 if ((($nbText > $nbGeoloc) || ($nbText < $nbGeoloc - 1))
351 || (($nbText == $nbGeoloc - 1) && ($arrayText[$nbText - 1] == strtoupper($address['country'])))) {
352 $same = false;
353 } else {
354 foreach ($arrayText as $i => $lignText) {
355 if (levenshtein($lignText, trim($arrayGeoloc[$i])) > 3) {
356 $same = false;
357 }
358 }
359 }
360 if ($same) {
361 $address['text'] = $address['geoloc'];
362 unset($address['geoloc']);
363 }
364 }
365
366 // }}}
367 // {{{ cleanText(&$text)
368
369 function cleanText(&$text)
370 {
371 $lines = explode("\n", $text);
372 $n = count($lines);
373 $text = "";
374 for ($i = 0; $i < $n; $i++) {
375 if (trim($lines[$i])) {
376 $text .= trim($lines[$i]) . "\n";
377 }
378 }
379 $text = trim($text);
380 }
381
382 // }}}
383 // {{{ getAreaId(array &$address, $area)
384
385 function getAreaId(array &$address, $area)
386 {
387 if (isset($address[$area . 'Name'])) {
388 $res = XDB::query("SELECT id
389 FROM geoloc_" . $area . "
390 WHERE name = {?}",
391 $address[$area . 'Name']);
392 if ($res->numRows() == 0) {
393 $address[$area . 'Id'] = XDB::execute("INSERT INTO geoloc_" . $area . " (name, country)
394 VALUES ({?}, {?})",
395 $address[$area . 'Name'], $address['countryId']);
396 } else {
397 $address[$area . 'Id'] = $res->fetchOneCell();
398 }
399 }
400 }
401
402 // }}}
403 // {{{ get_address_text($adr)
404 /** make the text of an address that can be read by a mailman
405 * @param $adr an array with all the usual fields
406 */
407 function get_address_text($adr)
408 {
409 $t = "";
410 if (isset($adr['adr1']) && $adr['adr1']) $t.= $adr['adr1'];
411 if (isset($adr['adr2']) && $adr['adr2']) $t.= "\n".$adr['adr2'];
412 if (isset($adr['adr3']) && $adr['adr3']) $t.= "\n".$adr['adr3'];
413 $l = "";
414 if (isset($adr['display']) && $adr['display']) {
415 $keys = explode(' ', $adr['display']);
416 foreach ($keys as $key) {
417 if (isset($adr[$key])) {
418 $l .= " ".$adr[$key];
419 } else {
420 $l .= " ".$key;
421 }
422 }
423 if ($l) substr($l, 1);
424 } elseif ($adr['country'] == 'US' || $adr['country'] == 'CA' || $adr['country'] == 'GB') {
425 if ($adr['city']) $l .= $adr['city'].",\n";
426 if ($adr['region']) $l .= $adr['region']." ";
427 if ($adr['postcode']) $l .= $adr['postcode'];
428 } else {
429 if (isset($adr['postcode']) && $adr['postcode']) $l .= $adr['postcode']." ";
430 if (isset($adr['city']) && $adr['city']) $l .= $adr['city'];
431 }
432 if ($l) $t .= "\n".trim($l);
433 if ($adr['country'] != '00' && (!$adr['countrytxt'] || $adr['countrytxt'] == strtoupper($adr['countrytxt']))) {
434 $res = XDB::query("SELECT pays FROM geoloc_pays WHERE a2 = {?}", $adr['country']);
435 $adr['countrytxt'] = $res->fetchOneCell();
436 }
437 if (isset($adr['countrytxt']) && $adr['countrytxt']) {
438 $t .= "\n".$adr['countrytxt'];
439 }
440 return trim($t);
441 }
442 // }}}
443 // {{{ compare_addresses_text($a, $b)
444 /** compares if two address matches
445 * @param $a the raw text of an address
446 * @param $b the raw text of a complete valid address
447 */
448 function compare_addresses_text($a, $b)
449 {
450 $ta = strtoupper(preg_replace(array("/[0-9,\"'#~:;_\- ]/", "/\r\n/"), array("", "\n"), $a));
451 $tb = strtoupper(preg_replace(array("/[0-9,\"'#~:;_\- ]/", "/\r\n/"), array("", "\n"), $b));
452
453 $la = explode("\n", $ta);
454 $lb = explode("\n", $tb);
455
456 if (count($lb) > count($la) + 1) {
457 return false;
458 }
459 foreach ($la as $i => $l) {
460 if (levenshtein(trim($l), trim($lb[$i])) > 3) {
461 return false;
462 }
463 }
464 return true;
465 }
466
467 // }}}
468 // {{{ fixNumber($oldtext, &$new)
469
470 function fixNumber($oldtext, &$new)
471 {
472 $ThoroughfareName = $new['AddressDetails']['Country']['AdministrativeArea']['SubAdministrativeArea']['Locality']['Thoroughfare']['ThoroughfareName'];
473 $ThoroughfareName = trim(strtoupper(preg_replace(array("/[,\"'#~:;_\-]/", "/\r\n/"),
474 array("", "\n"), $ThoroughfareName)));
475 $oldarray = explode("\n", trim(strtoupper(preg_replace(array("/[,\"'#~:;_\-]/", "/\r\n/"),
476 array("", "\n"), $oldtext))));
477 $mindist = strlen($ThoroughfareName);
478 $minpos = 0;
479 foreach ($oldarray as $i => $oldline) {
480 if (($l = levenshtein(trim($oldline), $ThoroughfareName)) < $mindist) {
481 $mindist = $l;
482 $minpos = $i;
483 }
484 }
485 $nb = explode(" ", $oldarray[$minpos]);
486 $new['text'] = $nb[0] . " " . $new['text'];
487 }
488
489 // }}}
490 // {{{ localize_addresses($uid)
491 /* localize all the address of a user and modify the database
492 * if the new address match with the old one
493 * @param $uid the id of the user
494 */
495 function localize_addresses($uid)
496 {
497 $res = XDB::iterator("SELECT *
498 FROM adresses
499 WHERE uid = {?} and (cityid IS NULL OR cityid = 0)", $uid);
500 $erreur = Array();
501
502 while ($a = $res->next()) {
503 $new = get_address_infos($ta = get_address_text($a));
504 if (compare_addresses_text($ta, get_address_text($new))) {
505 XDB::execute("UPDATE adresses
506 SET adr1 = {?}, adr2 = {?}, adr3 = {?},
507 cityid = {?}, city = {?}, postcode = {?},
508 region = {?}, regiontxt = {?}, country = {?},
509 glat = {?}, glng = {?}
510 WHERE uid = {?} AND adrid = {?}",
511 $new['adr1'], $new['adr2'], $new['adr3'],
512 $new['cityid'], $new['city'], $new['postcode'],
513 $new['region'], $new['regiontxt'], $new['country'],
514 $new['precise_lat'], $new['precise_lon'],
515 $uid, $a['adrid']);
516 $new['store'] = true;
517 if (!$new['cityid']) {
518 $erreur[$a['adrid']] = $new;
519 }
520 } else {
521 $new['store'] = false;
522 $erreur[$a['adrid']] = $new;
523 }
524 }
525 return $erreur;
526 }
527 // }}}
528 // {{{ get_address_infos($txt)
529 /** retrieve the infos on a text address
530 * store on the fly the info of the city concerned
531 * @param $txt the raw text of an address
532 */
533
534 function get_address_infos($txt)
535 {
536 global $globals;
537
538 $url = $globals->geoloc->webservice_url."address.php?precise=1&txt=" . urlencode($txt);
539 if ($globals->debug & DEBUG_BT) {
540 if (!isset(PlBacktrace::$bt['Geoloc'])) {
541 new PlBacktrace('Geoloc');
542 }
543 PlBacktrace::$bt['Geoloc']->start($url);
544 }
545 $f = @fopen($url, 'r');
546 if ($f === false) {
547 if ($globals->debug & DEBUG_BT) {
548 PlBacktrace::$bt['Geoloc']->stop(0, 'Can\'t fetch result');
549 }
550 return false;
551 }
552 $keys = explode('|',fgets($f));
553 $vals = explode('|',fgets($f));
554 if ($globals->debug & DEBUG_BT) {
555 $data = array();
556 for ($i = 0 ; $i < count($keys) ; ++$i) {
557 $data[] = array($keys[$i], $vals[$i]);
558 }
559 PlBacktrace::$bt['Geoloc']->stop(count($keys), null, $data);
560 }
561 $infos = empty_address();
562 foreach ($keys as $i=>$key) {
563 if($vals[$i]) {
564 if ($key == 'sql') {
565 $infos[$key] = $vals[$i];
566 } else {
567 $val = strtr($vals[$i], array(chr(197).chr(147) => "&oelig;"));
568 $infos[$key] = $val;
569 }
570 }
571 }
572 if (empty($infos['country'])) {
573 $infos['country'] = '00';
574 }
575 if (isset($infos['sql']) && $infos['sql']) {
576 $sql = explode(', ', trim($infos['sql'], '()'));
577 if (count($sql) == 16) {
578 for ($i = 0 ; $i < 16 ; ++$i) {
579 $sql[$i] = stripslashes(trim($sql[$i], ' \''));
580 }
581 XDB::execute("REPLACE INTO geoloc_city
582 VALUES ({?}, {?}, {?}, {?}, {?}, {?},
583 {?}, {?}, {?}, {?}, {?}, {?},
584 {?}, {?}, {?}, {?})",
585 $sql[0], $sql[1], $sql[2], $sql[3], $sql[4], $sql[5],
586 $sql[6], $sql[7], $sql[8], $sql[9], $sql[10], $sql[11],
587 $sql[12], $sql[13], $sql[14], $sql[15]);
588 }
589 }
590 if (isset($infos['display']) && $infos['display'])
591 XDB::execute("UPDATE geoloc_pays
592 SET display = {?}
593 WHERE a2 = {?}", $infos['display'], $infos['country']);
594 if (isset($infos['cityid'])) {
595 fix_cities_not_on_map(1, $infos['cityid']);
596 if (floatval($infos['precise_lat']) && floatval($infos['precise_lon'])) {
597 $res = XDB::query("SELECT c.lat / 100000, c.lon / 100000
598 FROM geoloc_city AS c
599 WHERE c.id = {?}", $infos['cityid']);
600 if ($res->numRows()) {
601 list($glat, $glng) = $res->fetchOneRow();
602 $infos['precise_lat'] = $glat;
603 $infos['precise_lon'] = $glng;
604 }
605 }
606 }
607 return $infos;
608 }
609
610 // }}}
611 // {{{ synchro_city($id)
612 /** synchronise the local geoloc_city base to geoloc.org
613 * @param $id the id of the city to synchronize
614 */
615 function synchro_city($id)
616 {
617 global $globals;
618 $url = $globals->geoloc->webservice_url."cityFinder.php?method=id&id=".$id."&out=sql";
619 if (!($f = @fopen($url, 'r'))) {
620 return false;
621 }
622 $s = fgets($f);
623 if ($s) {
624 return XDB::execute("REPLACE INTO geoloc_city VALUES ".$s) > 0;
625 }
626 }
627 // }}}
628 // {{{ function fix_cities_not_on_map($limit)
629 function fix_cities_not_on_map($limit=false, $cityid=false)
630 {
631 $missing = XDB::query("SELECT c.id
632 FROM geoloc_city AS c
633 LEFT JOIN geoloc_city_in_maps AS m ON(c.id = m.city_id)
634 WHERE m.city_id IS NULL"
635 . ($cityid ? " AND c.id = '" . $cityid . "'" : "" )
636 . ($limit ? " LIMIT $limit" : "" ));
637 $maps = get_cities_maps($missing->fetchColumn());
638 if ($maps) {
639 $values = "";
640 foreach ($maps as $cityid => $maps_c) {
641 foreach ($maps_c as $map_id) {
642 $values .= ",($cityid, $map_id, '')";
643 }
644 }
645 if (strlen($values) > 1) {
646 XDB::execute("REPLACE INTO geoloc_city_in_maps
647 VALUES ".substr($values, 1));
648 }
649 } else {
650 return false;
651 }
652 return true;
653 }
654
655 function set_smallest_levels()
656 {
657 $maxlengths = XDB::iterRow("SELECT MAX(LENGTH(gm.path)), gcim.city_id
658 FROM geoloc_city_in_maps AS gcim
659 INNER JOIN geoloc_maps AS gm USING ( map_id )
660 GROUP BY gcim.city_id");
661 while (list($length, $id) = $maxlengths->next()) {
662 XDB::execute("UPDATE geoloc_city_in_maps AS gcim
663 INNER JOIN geoloc_maps AS gm USING(map_id)
664 SET gcim.infos = IF(LENGTH(gm.path) = {?}, 'smallest', '')
665 WHERE gcim.city_id = {?}", $length, $id);
666 }
667 return true;
668 }
669 // }}}
670
671 function geoloc_to_x($lon, $lat)
672 {
673 return deg2rad(1) * $lon *100;
674 }
675
676 function geoloc_to_y($lon, $lat)
677 {
678 if ($lat < -75) {
679 return latToY(-75);
680 }
681 if ($lat > 75) {
682 return latToY(75);
683 }
684 return -100 * log(tan(pi()/4 + deg2rad(1)/2*$lat));
685 }
686
687 function size_of_city($nb)
688 {
689 $s = round(log($nb + 1)*2,2);
690 if ($s < 1) {
691 return 1;
692 }
693 return $s;
694 }
695
696 function size_of_territory($nb)
697 {
698 return size_of_city($nb);
699 }
700
701 function geoloc_getData_subcities($mapid, $SFields, &$cities, $direct=true)
702 {
703 if ($SFields instanceof UserSet) {
704 $set = $SFields;
705 $SFields = array();
706 } else {
707 $set = new UserSet();
708 }
709 for ($i_mapfield=0; $i_mapfield < count($SFields) ; $i_mapfield++) {
710 if ($SFields[$i_mapfield]->fieldFormName == 'mapid') {
711 break;
712 }
713 }
714 $SFields[$i_mapfield] = new MapSField('mapid',
715 array('gcim.map_id'),
716 array('adresses','geoloc_city_in_maps'),
717 array('am','gcim'),
718 array(getadr_join('am'), 'am.cityid = gcim.city_id'),
719 $mapid);
720 $fields = new SFieldGroup(true, $SFields);
721 $where = $fields->get_where_statement();
722 $joins = $fields->get_select_statement();
723 if ($where) {
724 $where .= ' AND ';
725 }
726 $cityres = $set->get('gc.id,
727 gc.lon / 100000 AS x, gc.lat/100000 AS y,
728 gc.name,
729 COUNT(u.user_id) AS pop,
730 SUM(u.promo % 2) AS yellow',
731 "$joins
732 LEFT JOIN geoloc_city AS gc ON(gcim.city_id = gc.id)",
733 $where . ($direct ? "gcim.infos = 'smallest'" : '1'),
734 'gc.id, gc.alias',
735 'pop DESC');
736 foreach($cityres as $c) {
737 if ($c['pop'] > 0) {
738 $city = $c;
739 $city['x'] = geoloc_to_x($c['x'], $c['y']);
740 $city['y'] = geoloc_to_y($c['x'], $c['y']);
741 $city['size'] = size_of_city($c['pop']);
742 $cities[$c['id']] = $city;
743 }
744 }
745 }
746
747 function geoloc_getData_subcountries($mapid, $sin, $minentities)
748 {
749 $countries = array();
750 $cities = array();
751
752 if ($mapid === false) {
753 $wheremapid = "WHERE gm.parent IS NULL";
754 } else {
755 $wheremapid = "WHERE gm.parent = {?}";
756 }
757 $submapres = XDB::iterator(
758 "SELECT gm.map_id AS id, gm.name, gm.x, gm.y, gm.xclip, gm.yclip,
759 gm.width, gm.height, gm.scale, 1 AS rat
760 FROM geoloc_maps AS gm
761 ". $wheremapid, Env::v('mapid',''));
762
763 global $globals;
764
765 while ($c = $submapres->next()) {
766 $country = $c;
767 $country['color'] = 0xFFFFFF;
768 $country['swf'] = $globals->geoloc->webservice_url."maps/mercator/map_".$c['id'].".swf";
769 $countries[$c['id']] = $country;
770 }
771
772 if ($mapid === false) {
773 return array($countries, $cities);
774 }
775
776 geoloc_getData_subcities(Env::i('mapid'), $sin, $cities);
777 $nbcities = count($cities);
778 $nocity = $nbcities == 0;
779 if ($sin instanceof UserSet) {
780 $set = $sin;
781 $SFields = array();
782 } else {
783 $set = new UserSet();
784 $SFields = $sin;
785 }
786
787 for ($i_mapfield=0; $i_mapfield < count($SFields) ; $i_mapfield++) {
788 if ($SFields[$i_mapfield]->fieldFormName == 'mapid') {
789 break;
790 }
791 }
792 $SFields[$i_mapfield] = new MapSField('mapid',
793 array('map.parent'),
794 array('adresses','geoloc_city_in_maps','geoloc_maps'),
795 array('am','gcim','map'),
796 array(getadr_join('am'),
797 'am.cityid = gcim.city_id',
798 'map.map_id = gcim.map_id'));
799 $fields = new SFieldGroup(true, $SFields);
800 $where = $fields->get_where_statement();
801 $joins = $fields->get_select_statement();
802 $countryres = $set->get('map.map_id AS id,
803 COUNT(u.user_id) AS nbPop,
804 SUM(u.promo % 2) AS yellow,
805 COUNT(DISTINCT gcim.city_id) AS nbCities,
806 SUM(IF(u.user_id IS NULL,0,am.glng)) AS lonPop,
807 SUM(IF(u.user_id IS NULL, 0,am.glat)) AS latPop',
808 $joins,
809 $where,
810 'map.map_id',
811 'NULL');
812
813 $maxpop = 0;
814 $nbentities = $nbcities + count($countryres);
815 foreach ($countryres as $c) {
816 $c['latPop'] /= $c['nbPop'];
817 $c['lonPop'] /= $c['nbPop'];
818 $c['rad'] = size_of_territory($c['nbPop']);
819 if ($maxpop < $c['nbPop']) $maxpop = $c['nbPop'];
820 $c['xPop'] = geoloc_to_x($c['lonPop'], $c['latPop']);
821 $c['yPop'] = geoloc_to_y($c['lonPop'], $c['latPop']);
822 @$countries[$c['id']] = array_merge($countries[$c['id']], $c);
823
824 $nbcities += $c['nbCities'];
825 }
826
827 if ($nocity && $nbcities < $minentities){
828 foreach($countries as $i => $c) {
829 $countries[$i]['nbPop'] = 0;
830 if (@$c['nbCities'] > 0) {
831 geoloc_getData_subcities($c['id'], $sin, $cities, false);
832 }
833 }
834 }
835
836 foreach ($countries as $i => $c) {
837 if (@$c['nbPop'] > 0) {
838 $lambda = pow($c['nbPop'] / $maxpop,0.3);
839 $countries[$i]['color'] = 0x0000FF + round((1-$lambda) * 0xFF)*0x010100;
840 }
841 }
842
843 return array($countries, $cities);
844 }
845
846 // vim:set et sw=4 sts=4 sws=4 foldmethod=marker enc=utf-8:
847 ?>