[PLUGINS] +maj gis4
[lhc/web/www.git] / www / plugins / gis / gis_fonctions.php
1 <?php
2
3 if (!defined('_ECRIRE_INC_VERSION')) return;
4
5 include_spip('inc/config');
6 include_spip('inc/json');
7
8 /**
9 * Filtre dec_to_dms, http://www.statemaster.com/encyclopedia/Geographic-coordinate-conversion
10 *
11 * @param decimal $coord
12 * @return string
13 */
14 function dec_to_dms($coord) {
15 return sprintf(
16 "%0.0f° %2.3f",
17 floor(abs($coord)),
18 60*(abs($coord)-floor(abs($coord)))
19 );
20 }
21
22 /**
23 * Filtre dms_to_dec, http://www.statemaster.com/encyclopedia/Geographic-coordinate-conversion
24 *
25 * @param string $ref N, E, S, W
26 * @param int $deg
27 * @param int $min
28 * @param int $sec
29 * @return decimal
30 */
31 function dms_to_dec($ref,$deg,$min,$sec) {
32
33 $arrLatLong = array();
34 $arrLatLong["N"] = 1;
35 $arrLatLong["E"] = 1;
36 $arrLatLong["S"] = -1;
37 $arrLatLong["W"] = -1;
38
39 return ($deg+((($min*60)+($sec))/3600)) * $arrLatLong[$ref];
40 }
41
42 /**
43 * Filtre distance pour renvoyer la distance entre deux points
44 * http://snipplr.com/view/2531/calculate-the-distance-between-two-coordinates-latitude-longitude/
45 * sinon voir ici : http://zone.spip.org/trac/spip-zone/browser/_plugins_/forms/geoforms/inc/gPoint.php
46 *
47 * @param int|array $from
48 * id_gis du point de référence ou tableau de coordonnées
49 * @param int|array $to
50 * id_gis du point distant ou tableau de coordonnées
51 * @param bool $miles
52 * Renvoyer le résultat en miles (kilomètres par défaut)
53 * @return float
54 * Retourne la distance en kilomètre ou en miles
55 */
56 function distance($from, $to, $miles=false) {
57 // On ne travaille que si on a toutes les infos
58 if (
59 // Le départ est soit un tableau soit un entier
60 (
61 (is_array($from) and isset($from['lat']) and isset($from['lon']))
62 or
63 ($from = intval($from) and $from > 0 and $from = sql_fetsel('lat,lon','spip_gis',"id_gis=$from"))
64 )
65 and
66 // Le distant est soit un tableau soit un entier
67 (
68 (is_array($to) and isset($to['lat']) and isset($to['lon']))
69 or
70 ($to = intval($to) and $to > 0 and $to = sql_fetsel('lat,lon','spip_gis',"id_gis=$to"))
71 )
72 ){
73 $pi80 = M_PI / 180;
74 $from['lat'] *= $pi80;
75 $from['lon'] *= $pi80;
76 $to['lat'] *= $pi80;
77 $to['lon'] *= $pi80;
78
79 $r = 6372.797; // mean radius of Earth in km
80 $dlat = $to['lat'] - $from['lat'];
81 $dlng = $to['lon'] - $from['lon'];
82 $a = sin($dlat / 2) * sin($dlat / 2) + cos($from['lat']) * cos($to['lat']) * sin($dlng / 2) * sin($dlng / 2);
83 $c = 2 * atan2(sqrt($a), sqrt(1 - $a));
84 $km = $r * $c;
85
86 return ($miles ? ($km * 0.621371192) : $km);
87 }
88
89 return false;
90 }
91
92 /**
93 * Compilation du critère {distancefrom}
94 *
95 * Critère {distancefrom} qui permet de ne sélectionner que les objets se trouvant à une distance comparée avec un point de repère.
96 * On doit lui passer 3 paramètres obligatoires :
97 * - le point de repère qui est un tableau avec les clés "lat" et "lon" ou un id_gis
98 * - l'opérateur de comparaison
99 * - la distance à comparer, en kilomètres
100 * Cela donne par exemple :
101 * {distancefrom #ARRAY{lat,#LAT,lon,#LON},<,30}
102 * {distancefrom #ARRAY{lat,#ENV{lat},lon,#ENV{lon}},<=,#ENV{distance}}
103 *
104 * @param unknown $idb
105 * @param unknown &$boucles
106 * @param unknown $crit
107 */
108 function critere_distancefrom_dist($idb, &$boucles, $crit) {
109 $boucle = &$boucles[$idb];
110 $id_table = $boucle->id_table; // articles
111 $primary = $boucle->primary; // id_article
112 $objet = objet_type($id_table); // article
113
114 if (
115 // Soit depuis une boucle (GIS) soit un autre objet mais avec {gis}
116 ($id_table == 'gis' or isset($boucle->join['gis']))
117 // Il faut aussi qu'il y ait 3 critères obligatoires
118 and count($crit->param) == 3
119 ){
120 $point_reference = calculer_liste($crit->param[0], array(), $boucles, $boucles[$idb]->id_parent);
121 $operateur = calculer_liste($crit->param[1], array(), $boucles, $boucles[$idb]->id_parent);
122 $distance = calculer_liste($crit->param[2], array(), $boucles, $boucles[$idb]->id_parent);
123
124 // Si le point de référence est un entier, on essaye de récupérer les coordonnées du point GIS
125 // Et si on a toujours pas de tableau correct, on met false
126 $boucle->hierarchie .= '$point_reference = '.$point_reference.';';
127 $boucle->hierarchie .= 'if (is_numeric($point_reference)){ $point_reference = sql_fetsel("lat,lon", "spip_gis", "id_gis = ".intval($point_reference)); }';
128 $boucle->hierarchie .= 'if (!is_array($point_reference) or !isset($point_reference["lat"]) or !isset($point_reference["lon"])){ $point_reference = false; }';
129 // L'opérateur doit exister dans une liste précise
130 $boucle->hierarchie .= '$operateur_distance = trim('.$operateur.');';
131 $boucle->hierarchie .= 'if (!in_array($operateur_distance, array("=","<",">","<=",">="))){ $operateur_distance = false; }';
132 $boucle->hierarchie .= '$distance = '.$distance.';';
133
134 $boucle->select[] = '".(!$point_reference ? "\'\' as distance" : "(6371 * acos( cos( radians(".$point_reference["lat"].") ) * cos( radians( gis.lat ) ) * cos( radians( gis.lon ) - radians(".$point_reference["lon"].") ) + sin( radians(".$point_reference["lat"].") ) * sin( radians( gis.lat ) ) ) ) AS distance")."';
135 $boucle->having[] = '((!$point_reference or !$operateur_distance or !$distance) ? "1=1" : "distance $operateur_distance ".sql_quote($distance))';
136 }
137 }
138
139 /**
140 * Compile le critère `{gis}` qui permet de compléter la boucle avec les points GIS
141 *
142 * Usage
143 * - `{gis}` Retourne les objets ayant des points (et ajoute les balises spéciales GIS tel que `#TITRE_GIS`)
144 * - `{!gis}` Retourne les objets sans points
145 * - `{gis distance<XX}`, sur une boucle `GIS`, filtre une liste de points par rapport à la distance du point de l'env
146 *
147 * @param string $idb
148 * @param array $boucles
149 * @param Critere $crit
150 */
151 function critere_gis_dist($idb, &$boucles, $crit) {
152 $boucle = &$boucles[$idb];
153 $id_table = $boucle->id_table; // articles
154 $primary = $boucle->primary; // id_article
155 $objet = objet_type($id_table); // article
156
157 if ($id_table == 'gis') {
158 // exclure l'élément en cours des résultats
159 $id_gis = calculer_argument_precedent($idb,$primary, $boucles);
160 $boucle->where[]= array("'!='", "'$boucle->id_table." . "$primary'", $id_gis);
161
162 // récupérer les paramètres du critère
163 $op='';
164 $params = $crit->param;
165 $type = array_shift($params);
166 $type = $type[0]->texte;
167 if(preg_match(',^(\w+)([<>=]+)([0-9]+)$,',$type,$r)){
168 $type=$r[1];
169 $op=$r[2];
170 $op_val=$r[3];
171 }
172 if ($op)
173 $boucle->having[]= array("'".$op."'", "'".$type."'",$op_val);
174
175 // récupérer lat/lon du point de la boucle englobante
176 $lat = calculer_argument_precedent($idb,'lat', $boucles);
177 $lon = calculer_argument_precedent($idb,'lon', $boucles);
178
179 // http://www.awelty.fr/developpement-web/php/
180 // http://www.movable-type.co.uk/scripts/latlong-db.html
181 // http://code.google.com/intl/fr/apis/maps/articles/geospatial.html#geospatial
182 $select = "(6371 * acos( cos( radians(\".$lat.\") ) * cos( radians( gis.lat ) ) * cos( radians( gis.lon ) - radians(\".$lon.\") ) + sin( radians(\".$lat.\") ) * sin( radians( gis.lat ) ) ) ) AS distance";
183 $order = "'distance'";
184
185 $boucle->select[]= $select;
186 $boucle->order[]= $order;
187 } else {
188
189 /* Recherche d'objets SANS point */
190 if ($crit->not) {
191 $boucle->from['gis_liens'] = 'spip_gis_liens';
192 $boucle->from_type['gis_liens'] = "LEFT";
193 $boucle->join['gis_liens'] = array("'$id_table'","'id_objet'","'$primary'","'gis_liens.objet='.sql_quote('$objet')");
194 $boucle->where[] = "'gis_liens.id_gis IS NULL'";
195
196 /* Recherche d'objets AVEC point + ajout des champs GIS */
197 } else {
198 // ajouter tous les champs du point au select
199 // et les suffixer pour lever toute ambiguite avec des champs homonymes
200 $boucle->select[]= 'gis.titre AS titre_gis';
201 $boucle->select[]= 'gis.descriptif AS descriptif_gis';
202 $boucle->select[]= 'gis.adresse AS adresse_gis';
203 $boucle->select[]= 'gis.pays AS pays_gis';
204 $boucle->select[]= 'gis.code_pays AS code_pays_gis';
205 $boucle->select[]= 'gis.region AS region_gis';
206 $boucle->select[]= 'gis.departement AS departement_gis';
207 $boucle->select[]= 'gis.ville AS ville_gis';
208 $boucle->select[]= 'gis.code_postal AS code_postal_gis';
209 // jointure sur spip_gis_liens/spip_gis
210 // cf plugin notation
211 // $boucle->join["surnom (as) table de liaison"] = array("surnom de la table a lier", "cle primaire de la table de liaison", "identifiant a lier", "type d'objet de l'identifiant");
212 $boucle->from['gis_liens'] = 'spip_gis_liens';
213 $boucle->join['gis_liens']= array("'$id_table'","'id_objet'","'$primary'","'gis_liens.objet='.sql_quote('$objet')");
214 $boucle->from['gis'] = 'spip_gis';
215 $boucle->join['gis']= array("'gis_liens'","'id_gis'");
216 // bien renvoyer tous les points qui son attachés à l'objet
217 // mais attention, si on trouve en amont un groupement portant sur un champ *de GIS*,
218 // alors cela signifie que la personne veut faire une opération de groupement sur les points donc là on n'ajoute pas id_gis
219 $tous_les_points = true;
220 foreach ($boucle->group as $champ){
221 if (in_array($champ, array('ville', 'code_postal', 'pays', 'code_pays', 'region','departement'))) {
222 $tous_les_points = false;
223 }
224 }
225 if ($tous_les_points) {
226 $boucle->group[] = 'gis_liens.id_gis';
227 }
228 // ajouter gis aux jointures et spécifier les jointures explicites pour pouvoir utiliser les balises de la table de jointure
229 // permet de passer dans trouver_champ_exterieur() depuis index_tables_en_pile()
230 // cf http://article.gmane.org/gmane.comp.web.spip.zone/6628
231 $boucle->jointures[] = 'gis';
232 if (empty($boucle->jointures_explicites)){
233 $boucle->jointures_explicites = 'gis_liens gis';
234 }
235 else{
236 $boucle->jointures_explicites .= ' gis_liens gis';
237 }
238 }
239 }
240 }
241
242 /**
243 * Balise #DISTANCE issue du critère {gis distance<XX}
244 * merci marcimant : http://formation.magraine.net/spip.php?article61
245 *
246 * @param unknown_type $p
247 */
248 function balise_distance_dist($p) {
249 return rindex_pile($p, 'distance', 'gis');
250 }
251
252 /**
253 * Balise #TITRE_GIS : retourne le titre du point
254 * Necessite le critere {gis} sur la boucle
255 *
256 * @param unknown_type $p
257 */
258 function balise_titre_gis_dist($p) {
259 return rindex_pile($p, 'titre_gis', 'gis');
260 }
261
262 /**
263 * Balise #DESCRIPTIF_GIS : retourne le descriptif du point
264 * Necessite le critere {gis} sur la boucle
265 *
266 * @param unknown_type $p
267 */
268 function balise_descriptif_gis_dist($p) {
269 return rindex_pile($p, 'descriptif_gis', 'gis');
270 }
271
272 /**
273 * Balise #ADRESSE_GIS : retourne l'adresse du point
274 * Necessite le critere {gis} sur la boucle
275 *
276 * @param unknown_type $p
277 */
278 function balise_adresse_gis_dist($p) {
279 return rindex_pile($p, 'adresse_gis', 'gis');
280 }
281
282 /**
283 * Balise #PAYS_GIS : retourne le pays du point
284 * Necessite le critere {gis} sur la boucle
285 *
286 * @param unknown_type $p
287 */
288 function balise_pays_gis_dist($p) {
289 return rindex_pile($p, 'pays_gis', 'gis');
290 }
291
292 /**
293 * Balise #CODE_PAYS_GIS : retourne le code pays du point
294 * Necessite le critere {gis} sur la boucle
295 *
296 * @param unknown_type $p
297 */
298 function balise_code_pays_gis_dist($p) {
299 return rindex_pile($p, 'code_pays_gis', 'gis');
300 }
301
302 /**
303 * Balise #VILLE_GIS : retourne la ville du point
304 * Necessite le critere {gis} sur la boucle
305 *
306 * @param unknown_type $p
307 */
308 function balise_ville_gis_dist($p) {
309 return rindex_pile($p, 'ville_gis', 'gis');
310 }
311
312 /**
313 * Balise #REGION_GIS : retourne la région du point
314 * Necessite le critere {gis} sur la boucle
315 *
316 * @param unknown_type $p
317 */
318 function balise_region_gis_dist($p) {
319 return rindex_pile($p, 'region_gis', 'gis');
320 }
321
322 /**
323 * Balise #DEPARTEMENT_GIS : censé retourner le département du point
324 * Necessite le critere {gis} sur la boucle
325 *
326 * @param unknown_type $p
327 */
328 function balise_departement_gis_dist($p) {
329 return rindex_pile($p, 'departement_gis', 'gis');
330 }
331
332 /**
333 * Balise #CODE_POSTAL_GIS : retourne le code postal du point
334 * Necessite le critere {gis} sur la boucle
335 *
336 * @param unknown_type $p
337 */
338 function balise_code_postal_gis_dist($p) {
339 return rindex_pile($p, 'code_postal_gis', 'gis');
340 }
341
342 /**
343 * Définition du fond de carte à utiliser par défaut en prenant compte les defines
344 */
345 function gis_layer_defaut(){
346 $defaut = 'openstreetmap_mapnik';
347 if(defined('_GIS_LAYER_DEFAUT_FORCE')){
348 return _GIS_LAYER_DEFAUT_FORCE;
349 }else{
350 if(defined('_GIS_LAYER_DEFAUT')){
351 $defaut = _GIS_LAYER_DEFAUT;
352 }
353 $config = lire_config('gis/layer_defaut');
354 return $config ? $config : $defaut;
355 }
356 }
357
358 /**
359 * Recuperer les cles primaires du env pour l'appel a l'url json des points
360 * @param $env
361 * @return array
362 */
363 function gis_modele_url_json_env($env){
364 $contexte = array();
365 if (is_string($env))
366 $env = unserialize($env);
367 if ($env){
368 // d'abord toutes les cles primaires connues
369 $tables_sql = lister_tables_objets_sql();
370 foreach (array_keys($tables_sql) as $table){
371 $primary = id_table_objet($table);
372 if (isset($env[$primary])) {
373 $contexte[$primary] = is_array($env[$primary]) ? $env[$primary] : trim($env[$primary]);
374 }
375 }
376 // puis cas particuliers et ceux ajoutés par le pipeline
377 $keys = pipeline('gis_modele_parametres_autorises', array("objet", "id_objet","id_secteur","id_parent","media","recherche","mots","pays","code_pays","region","departement","ville","code_postal","adresse"));
378 foreach ($keys as $key){
379 if (isset($env[$key])) {
380 $contexte[$key] = is_array($env[$key]) ? $env[$key] : trim($env[$key]);
381 }
382 }
383 }
384 return $contexte;
385 }
386
387 /**
388 * Transformer le tableau de kml en tableau d'urls :
389 * si numerique c'est un id de document
390 * si chaine c'est une url qu'on rapatrie en local
391 * @param array $kml
392 * @return array
393 */
394 function gis_kml_to_urls($kml){
395 if ($kml AND count($kml)){
396 include_spip("inc/filtres_mini");
397 include_spip("inc/distant");
398 foreach($kml as $k=>$v){
399 if (is_numeric($v)){
400 $kml[$k] = url_absolue(generer_url_entite($v,"document"));
401 }
402 else
403 $kml[$k] = _DIR_RACINE.copie_locale($kml[$k],"modif");
404 }
405 }
406 return $kml;
407 }
408
409 /**
410 * Retourne les propriétés JSON de l'icône d'un point
411 *
412 * @param string $img
413 * Balise HTML `<img ... />` ou chemin de l'image (qui peut être une URL distante).
414 * @return string
415 * Les propriétés de l'icône
416 **/
417 function gis_icon_properties($img=''){
418 $props = $icon = '';
419
420 if ($img) {
421 if (largeur($img) >= 44)
422 $icon = extraire_attribut(filtrer('image_graver',filtrer('image_recadre',filtrer('image_passe_partout',$img,32,32),32,32,'center','transparent')),'src');
423 else
424 $icon = extraire_attribut($img,'src') ? extraire_attribut($img,'src') : $img;
425 }
426 else
427 $icon = find_in_path('images/marker_defaut.png');
428
429 if ($icon) {
430 $props .= ",\n\"icon\": ". json_encode(url_absolue($icon)).",";
431 list($h,$w) = taille_image($icon);
432 $props .= "\n\"icon_size\": ". json_encode(array($w,$h)).",";
433 $props .= "\n\"icon_anchor\": ". json_encode(array($w/2,$h)).",";
434 $props .= "\n\"popup_anchor\": ". json_encode(array(1,-round($h/1.2,2)));
435 }
436
437 if ($shadow = find_in_path('images/marker_defaut_shadow.png')) {
438 $props .= ",\n\"shadow\": ". json_encode(url_absolue($shadow));
439 list($h,$w) = taille_image($shadow);
440 $props .= ",\n\"shadow_size\": ". json_encode(array($w,$h));
441 }
442
443 return $props;
444 }
445
446 ?>