source: spip-zone/_plugins_/rainette/trunk/services/wunderground.php @ 108245

Last change on this file since 108245 was 108245, checked in by eric@…, 14 months ago

Evolution majeure : ajout d'une gestion de thèmes pour chaque service.
Il est désormais possible d'afficher soit l'icone distant fourni dans l'API, soit un icone local d'un thème compatible avec le service, soit l'icone d'un service de weather.com.
Ceci est particulièrement intéressant car il existe moulte thèmes d'icones pour weather.com.
Pour l'instant, un seul thème est proposé par service à l'exception de owm.
Suivra bientôt un nouveau plugin associé à Rainette qui proposera nombre de thèmes et permettra de les visualiser afin de choisir celui à utiliser.
Refactoring et factorisation des fonctions associées.

  • Property svn:eol-style set to native
File size: 19.4 KB
Line 
1<?php
2/**
3 * Ce fichier contient l'ensemble des constantes et fonctions implémentant le service Wunderground.
4 * Ce service fournit des données au format XML ou JSON.
5 *
6 * @package SPIP\RAINETTE\SERVICES\WUNDERGROUND
7 */
8if (!defined('_ECRIRE_INC_VERSION')) {
9        return;
10}
11
12if (!defined('_RAINETTE_WUNDERGROUND_URL_BASE_REQUETE')) {
13        /**
14         * URL de base (endpoint) des requêtes au service Wunderground.
15         */
16        define('_RAINETTE_WUNDERGROUND_URL_BASE_REQUETE', 'http://api.wunderground.com/api');
17}
18if (!defined('_RAINETTE_WUNDERGROUND_URL_BASE_ICONE')) {
19        /**
20         * URL de base des icônes fournis par le service Wunderground.
21         */
22        define('_RAINETTE_WUNDERGROUND_URL_BASE_ICONE', 'http://icons.wxug.com/i/c');
23}
24
25
26// Configuration des valeurs par défaut des éléments de la configuration dynamique.
27// Ces valeurs sont applicables à tous les modes.
28$GLOBALS['rainette_wunderground_config']['service'] = array(
29        'alias'   => 'wunderground',
30        'nom'     => 'Weather Underground',
31        'credits' => array(
32                'titre'       => 'Weather Underground',
33                'logo'        => 'wunderground.png',
34                'lien'        => 'http://www.wunderground.com/',
35        ),
36        'termes'         => array(
37                'titre' => 'Terms and Conditions of use',
38                'lien' => 'https://www.wunderground.com/weather/api/d/terms.html'
39        ),
40        'enregistrement' => array(
41                'titre' => 'Join Weather Underground',
42                'lien' => 'https://www.wunderground.com/signup?mode=api_signup',
43                'taille_cle' => 16
44        ),
45        'offres'         => array(
46                'titre' => 'Pricing',
47                'lien' => 'https://www.wunderground.com/weather/api/d/pricing.html',
48                'limites' => array(
49                        'day'         => 500,
50                        'minute'      => 10
51                )
52        ),
53        'langues' => array(
54                'disponibles' => array(
55                        'AF' => 'af',
56                        'AR' => 'ar',
57                        'AZ' => 'az',
58                        'BY' => 'be',
59                        'BU' => 'bg',
60                        'CA' => 'ca',
61                        'HT' => 'cpf_hat',
62                        'CZ' => 'cs',
63                        'CY' => 'cy',
64                        'DK' => 'da',
65                        'DL' => 'de',
66                        'GR' => 'el',
67                        'EN' => 'en',
68                        'EO' => 'eo',
69                        'SP' => 'es',
70                        'ET' => 'et',
71                        'EU' => 'eu',
72                        'FA' => 'fa',
73                        'FI' => 'fi',
74                        'FR' => 'fr',
75                        'IR' => 'ga',
76                        'GZ' => 'gl',
77                        'GU' => 'gu',
78                        'IL' => 'he',
79                        'HI' => 'hi',
80                        'CR' => 'hr',
81                        'HU' => 'hu',
82                        'HY' => 'hy',
83                        'ID' => 'id',
84                        'IS' => 'is',
85                        'IT' => 'it',
86                        'JP' => 'ja',
87                        'JW' => 'jv',
88                        'KA' => 'ka',
89                        'KM' => 'km',
90                        'KR' => 'ko',
91                        'KU' => 'ku',
92                        'LA' => 'la',
93                        'LT' => 'lt',
94                        'LV' => 'lv',
95                        'GM' => 'man',
96                        'MI' => 'mi',
97                        'MK' => 'mk',
98                        'MN' => 'mn',
99                        'MR' => 'mr',
100                        'MT' => 'mt',
101                        'MY' => 'my',
102                        'NL' => 'nl',
103                        'NO' => 'no',
104                        'OC' => 'oc',
105                        'PA' => 'pa',
106                        'PL' => 'pl',
107                        'PS' => 'ps',
108                        'BR' => 'pt',
109                        'RO' => 'ro',
110                        'RU' => 'ru',
111                        'SK' => 'sk',
112                        'SL' => 'sl',
113                        'AL' => 'sq',
114                        'SR' => 'sr',
115                        'SW' => 'sv',
116                        'SI' => 'sw',
117                        'TH' => 'th',
118                        'TK' => 'tk',
119                        'TL' => 'tl',
120                        'TR' => 'tr',
121                        'TT' => 'tt',
122                        'UA' => 'uk',
123                        'UZ' => 'uz',
124                        'VU' => 'vi',
125                        'SN' => 'wo',
126                        'YI' => 'yi',
127                        'CN' => 'zh',
128                        'TW' => 'zh_tw',
129                ),
130                'defaut'      => 'EN'
131        ),
132        'defauts' => array(
133                'inscription'   => '',
134                'unite'         => 'm',
135                'condition'     => 'wundergound',
136                'theme'         => 'a',
137                'theme_local'   => 'observation',
138                'theme_weather' => 'sticker',
139        ),
140        // TODO : tout à revoir
141        'transcodage_weather' => array(
142                'chanceflurries'  => array(41, 46),
143                'chancerain'      => array(39, 45),
144                'chancesleet'     => array(39, 45),
145                'chancesnow'      => array(41, 46),
146                'chancetstorms'   => array(38, 47),
147                'clear'           => array(32, 31),
148                'cloudy'          => array(26, 26),
149                'flurries'        => array(15, 15),
150                'fog'             => array(20, 20),
151                'hazy'            => array(21, 21),
152                'mostlycloudy'    => array(28, 27),
153                'mostlysunny'     => array(34, 33),
154                'partlycloudy'    => array(30, 29),
155                'partlysunny'     => array(28, 27),
156                'sleet'           => array(5, 5),
157                'rain'            => array(11, 11),
158                'snow'            => array(16, 16),
159                'sunny'           => array(32, 31),
160                'tstorms'         => array(4, 4),
161                'thunderstorms'   => array(4, 4),
162                'unknown'         => array(4, 4),
163                'scatteredclouds' => array(30, 29),
164                'overcast'        => array(26, 26)
165        )
166);
167
168// Configuration des données fournies par le service wunderground pour le mode 'infos'.
169// -- Seules les données non calculées sont configurées.
170$GLOBALS['rainette_wunderground_config']['infos'] = array(
171        'periode_maj' => 86400,
172        'format_flux' => 'json',
173        'cle_base'    => array('location'),
174        'donnees'     => array(
175                // Lieu
176                'ville'     => array('cle' => array('city')),
177                'pays'      => array('cle' => array('country_name')),
178                'pays_iso2' => array('cle' => array('country_iso3166')),
179                'region'    => array('cle' => array('state')),
180                // Coordonnées
181                'longitude' => array('cle' => array('lon')),
182                'latitude'  => array('cle' => array('lat')),
183                // Informations complémentaires : aucune configuration car ce sont des données calculées
184        ),
185);
186
187// Configuration des données fournies par le service wwo pour le mode 'conditions'.
188// -- Seules les données non calculées sont configurées.
189$GLOBALS['rainette_wunderground_config']['conditions'] = array(
190        'periode_maj' => 1800,
191        'format_flux' => 'json',
192        'cle_base'    => array('current_observation'),
193        'donnees'     => array(
194                // Données d'observation
195                'derniere_maj'          => array('cle' => array('observation_time_rfc822')),
196                'station'               => array('cle' => array('observation_location', 'full')),
197                // Températures
198                'temperature_reelle'    => array('cle' => array('temp_'), 'suffixe_unite' => array('id_cle' => 0, 'm' => 'c', 's' => 'f')),
199                'temperature_ressentie' => array('cle' => array('feelslike_'), 'suffixe_unite' => array('id_cle' => 0, 'm' => 'c', 's' => 'f')),
200                // Données anémométriques
201                'vitesse_vent'          => array('cle' => array('wind_'), 'suffixe_unite' => array('id_cle' => 0, 'm' => 'kph', 's' => 'mph')),
202                'angle_vent'            => array('cle' => array('wind_degrees')),
203                'direction_vent'        => array('cle' => array()),
204                // Données atmosphériques : risque_uv est calculé
205                'precipitation'         => array('cle' => array('precip_today_'), 'suffixe_unite' => array('id_cle' => 0, 'm' => 'metric', 's' => 'in')),
206                'humidite'              => array('cle' => array('relative_humidity')),
207                'point_rosee'           => array('cle' => array('dewpoint_'), 'suffixe_unite' => array('id_cle' => 0, 'm' => 'c', 's' => 'f')),
208                'pression'              => array('cle' => array('pressure_'), 'suffixe_unite' => array('id_cle' => 0, 'm' => 'mb', 's' => 'in')),
209                'tendance_pression'     => array('cle' => array('pressure_trend')),
210                'visibilite'            => array('cle' => array('visibility_'), 'suffixe_unite' => array('id_cle' => 0, 'm' => 'km', 's' => 'mi')),
211                'indice_uv'             => array('cle' => array('UV')),
212                // Etats météorologiques natifs
213                'code_meteo'            => array('cle' => array('icon')),
214                'icon_meteo'            => array('cle' => array('icon_url')),
215                'desc_meteo'            => array('cle' => array('weather')),
216                'trad_meteo'            => array('cle' => array()),
217                // Etats météorologiques calculés : icone, resume, periode sont calculés
218        ),
219);
220
221// Configuration des données fournies par le service wwo pour le mode 'conditions'.
222// -- Seules les données non calculées sont configurées.
223$GLOBALS['rainette_wunderground_config']['previsions'] = array(
224        'periodicites'       => array(
225                24 => array('max_jours' => 10),
226                //              1  => array('max_jours' => 10)
227        ),
228        'periodicite_defaut' => 24,
229        'periode_maj'        => 1800,
230        'format_flux'        => 'json',
231        'cle_base'           => array('forecast', 'simpleforecast', 'forecastday'),
232        'cle_heure'          => array(),
233        'structure_heure'    => false,
234        'donnees'            => array(
235                // Données d'observation
236                'date'                 => array('cle' => array('date', 'epoch')),
237                'heure'                => array('cle' => array()),
238                // Données astronomiques
239                'lever_soleil'         => array('cle' => array()),
240                'coucher_soleil'       => array('cle' => array()),
241                // Températures
242                'temperature'          => array('cle' => array()),
243                'temperature_max'      => array('cle' => array('high', ''), 'suffixe_unite' => array('id_cle' => 1, 'm' => 'celsius', 's' => 'fahrenheit')),
244                'temperature_min'      => array('cle' => array('low', ''), 'suffixe_unite' => array('id_cle' => 1, 'm' => 'celsius', 's' => 'fahrenheit')),
245                // Données anémométriques
246                'vitesse_vent'         => array('cle' => array('avewind', ''), 'suffixe_unite' => array('id_cle' => 1, 'm' => 'kph', 's' => 'mph')),
247                'angle_vent'           => array('cle' => array('avewind', 'degrees')),
248                'direction_vent'       => array('cle' => array()),
249                // Données atmosphériques
250                'risque_precipitation' => array('cle' => array()),
251                'precipitation'        => array('cle' => array('qpf_allday', ''), 'suffixe_unite' => array('id_cle' => 1, 'm' => 'mm', 's' => 'in')),
252                'humidite'             => array('cle' => array('avehumidity')),
253                'point_rosee'          => array('cle' => array()),
254                'pression'             => array('cle' => array()),
255                'visibilite'           => array('cle' => array()),
256                'indice_uv'            => array('cle' => array()),
257                // Etats météorologiques natifs
258                'code_meteo'           => array('cle' => array('icon')),
259                'icon_meteo'           => array('cle' => array('icon_url')),
260                'desc_meteo'           => array('cle' => array('conditions')),
261                'trad_meteo'           => array('cle' => array()),
262                // Etats météorologiques calculés : icone, resume, periode sont calculés
263        ),
264);
265
266// Configuration des données fournies par le service Wunderground en cas d'erreur.
267// -- Seules les données non calculées sont configurées.
268$GLOBALS['rainette_wunderground_config']['erreurs'] = array(
269        'cle_base'    => array('response', 'error'),
270        'donnees'     => array(
271                // Erreur
272                'code'     => array('cle' => array('type')),
273                'message'  => array('cle' => array('description')),
274        ),
275);
276
277
278/**
279 * ------------------------------------------------------------------------------------------------
280 * Les fonctions qui suivent définissent l'API standard du service et sont appelées par la fonction
281 * unique de chargement des données météorologiques `meteo_charger()`.
282 * PACKAGE SPIP\RAINETTE\WUNDERGROUND\API
283 * ------------------------------------------------------------------------------------------------
284 */
285
286/**
287 * @param string $mode
288 *
289 * @return array
290 */
291function wunderground_service2configuration($mode) {
292        // On merge la configuration propre au mode et la configuration du service proprement dit
293        // composée des valeurs par défaut de la configuration utilisateur et e paramètres généraux.
294        $config = array_merge($GLOBALS['rainette_wunderground_config'][$mode], $GLOBALS['rainette_wunderground_config']['service']);
295
296        return $config;
297}
298
299
300/**
301 * Contruit l'url de la requête en fonction du lieu, du mode et de la périodicité demandés.
302 *
303 * @api
304 *
305 * @param string $lieu
306 *        Lieu pour lequel on requiert le nom du cache.
307 * @param string $mode
308 *        Type de données météorologiques. Les valeurs possibles sont `infos`, `conditions` ou `previsions`.
309 * @param int    $periodicite
310 *        La périodicité horaire des prévisions :
311 *            - `24`, ou `1`, pour le mode `previsions`
312 *            - `0`, pour les modes `conditions` et `infos`
313 * @param array  $configuration
314 *        Configuration complète du service, statique et utilisateur.
315 *
316 * @return string
317 *        Chemin complet du fichier cache.
318 */
319function wunderground_service2url($lieu, $mode, $periodicite, $configuration) {
320
321        // Determination de la demande
322        $demande = '';
323        switch ($mode) {
324                case 'infos':
325                        $demande = 'geolookup';
326                        break;
327                case 'conditions':
328                        $demande = 'conditions';
329                        break;
330                case 'previsions':
331                        $demande = ($periodicite == 24) ? 'forecast10day/astronomy' : 'hourly10day/astronomy';
332                        break;
333        }
334
335        // On normalise le lieu et on récupère son format.
336        // Le service accepte la format ville,pays, le format latitude,longitude, le format adresse IP
337        // et le format weather ID (comme FRXX0076 pour Paris).
338        include_spip('inc/rainette_normaliser');
339        $lieu_normalise = lieu_normaliser($lieu, $format_lieu);
340        if ($format_lieu == 'weather_id') {
341                $query = "locid:${lieu_normalise}";
342        } elseif ($format_lieu == 'adresse_ip') {
343                $query = "autoip.json?geo_ip=${lieu_normalise}";
344        } elseif ($format_lieu == 'latitude_longitude') {
345                $query = $lieu_normalise;
346        } else { // Format ville,pays
347                $query = $lieu_normalise;
348                $elements = explode(',', $lieu_normalise);
349                if (count($elements) == 2) {
350                        // Le pays est précisé, il faut alors le positionner avant la ville et le séparer par un slash.
351                        $query = $elements[1] . '/' . $elements[0];
352                }
353        }
354
355        // Identification de la langue du resume.
356        // Le choix de la langue n'a d'interet que si on utilise le resume natif du service. Si ce n'est pas
357        // le cas on demande à l'API de renvoyer la langue par défaut
358        $code_langue = langue_determiner($configuration);
359
360        $url = _RAINETTE_WUNDERGROUND_URL_BASE_REQUETE
361                   . '/' . $configuration['inscription']
362                   . '/' . $demande
363                   . '/lang:' . $code_langue
364                   . '/q'
365                   . '/' . $query . '.' . $configuration['format_flux'];
366
367        return $url;
368}
369
370
371/**
372 * @param array $erreur
373 *
374 * @return bool
375 */
376function wunderground_erreur_verifier($erreur) {
377
378        // Initialisation
379        $est_erreur = false;
380
381        // Une erreur est toujours décrite par un type (code) et un message.
382        if (!empty($erreur['code']) and !empty($erreur['message'])) {
383                $est_erreur = true;
384        }
385
386        return $est_erreur;
387}
388
389
390/**
391 * Complète par des données spécifiques au service le tableau des conditions issu
392 * uniquement de la lecture du flux.
393 *
394 * @api
395 *
396 * @param array $tableau
397 *        Tableau standardisé des conditions contenant uniquement les données fournies sans traitement
398 *        par le service.
399 * @param array $configuration
400 *        Configuration complète du service, statique et utilisateur.
401 *
402 * @return array
403 *        Tableau standardisé des conditions météorologiques complété par les données spécifiques
404 *        du service.
405 */
406function wunderground_complement2conditions($tableau, $configuration) {
407        // TODO : vérifier sur le site si le cas '' vers '' a un sens ou pas ?
408        static $tendances = array('0' => 'steady', '+' => 'rising', '-' => 'falling', '' => '');
409
410        if ($tableau) {
411                // Traiter le cas où l'indice uv n'est pas fourni: wunderground renvoie une valeur négative.
412                // On écrase cette valeur par la chaine vide qui indique que la donnée n'est pas disponible.
413                if (is_int($tableau['indice_uv']) and $tableau['indice_uv'] < 0) {
414                        $tableau['indice_uv'] = '';
415                }
416
417                // Convertir la valeur de tendance dans le standard du plugin.
418                // La documentation indique que les directions uniques sont fournies sous forme de texte comme North
419                // alors que les autres sont des acronymes. En outre, la valeur semble être traduite
420                // --> Le mieux est donc de convertir à partir de l'angle
421                include_spip('inc/rainette_convertir');
422                $tableau['direction_vent'] = angle2direction($tableau['angle_vent']);
423                // Correspondance des tendances de pression dans le système standard
424                $tableau['tendance_pression'] = $tendances[$tableau['tendance_pression']];
425
426                // Parfois le nom de la station se termine par une virgule et un espace : on supprime ces deux caractères.
427                $tableau['station'] = rtrim($tableau['station'], ' ,');
428
429                // Compléter le tableau standard avec les états météorologiques calculés
430                etat2resume_wunderground($tableau, $configuration);
431        }
432
433        return $tableau;
434}
435
436
437/**
438 * Complète par des données spécifiques au service le tableau des conditions issu
439 * uniquement de la lecture du flux.
440 *
441 * @api
442 *
443 * @param array $tableau
444 *        Tableau standardisé des conditions contenant uniquement les données fournies sans traitement
445 *        par le service.
446 * @param array $configuration
447 *        Configuration complète du service, statique et utilisateur.
448 * @param int   $index_periode
449 *        Index où trouver et ranger les données.
450 *
451 * @return array
452 *        Tableau standardisé des conditions météorologiques complété par les données spécifiques
453 *        du service.
454 */
455function wunderground_complement2previsions($tableau, $configuration, $index_periode) {
456
457        if ($tableau and ($index_periode > -1)) {
458                // Déterminer la direction du vent dans le standard du plugin.
459                // La documentation indique que les directions uniques sont fournies sous forme de texte comme North
460                // alors que les autres sont des acronymes. En outre, la valeur semble être traduite
461                // --> Le mieux est donc de convertir à partir de l'angle
462                include_spip('inc/rainette_convertir');
463                $tableau['direction_vent'] = angle2direction($tableau['angle_vent']);
464
465                // Compléter le tableau standard avec les états météorologiques calculés
466                etat2resume_wunderground($tableau, $configuration);
467        }
468
469        return $tableau;
470}
471
472
473/**
474 * ---------------------------------------------------------------------------------------------
475 * Les fonctions qui suivent sont des utilitaires uniquement appelées par les fonctions de l'API
476 * ---------------------------------------------------------------------------------------------
477 */
478
479/**
480 * Calcule les états en fonction des états météorologiques natifs fournis par le service.
481 *
482 * @internal
483 *
484 * @param array $tableau
485 *        Tableau standardisé des conditions contenant uniquement les données fournies sans traitement
486 *        par le service. Le tableau est mis à jour et renvoyé à l'appelant.
487 * @param array $configuration
488 *        Configuration complète du service, statique et utilisateur.
489 *
490 * @return void
491 */
492function etat2resume_wunderground(&$tableau, $configuration) {
493
494        if ($tableau['code_meteo'] and $tableau['icon_meteo']) {
495                // Determination de l'indicateur jour/nuit qui permet de choisir le bon icone
496                // Pour ce service (cas actuel) le nom du fichier icône commence par "nt_" pour la nuit.
497                $icone = basename($tableau['icon_meteo']);
498                if (strpos($icone, 'nt_') === false) {
499                        // C'est le jour
500                        $tableau['periode'] = 0;
501                } else {
502                        // C'est la nuit
503                        $tableau['periode'] = 1;
504                }
505
506                // Détermination du résumé à afficher.
507                // Depuis la 3.4.6 on affiche plus que le résumé natif de chaque service car les autres services
508                // que weather.com possèdent de nombreuses traductions qu'il convient d'utiliser.
509                // Pour éviter de modifier la structure de données, on conserve donc desc_meteo et resume même si
510                // maintenant ces deux données coincident toujours.
511                $tableau['resume'] = ucfirst($tableau['desc_meteo']);
512
513                // Determination de l'icone qui sera affiché.
514                // -- on stocke le code afin de le fournir en alt dans la balise img
515                $tableau['icone']['code'] = $tableau['code_meteo'];
516                // -- on calcule le chemin complet de l'icone.
517                if ($configuration['condition'] == $configuration['alias']) {
518                        // On affiche l'icône natif fourni par le service et désigné par son url
519                        // en faisant une copie locale dans IMG/.
520                        include_spip('inc/distant');
521                        $url = _RAINETTE_WUNDERGROUND_URL_BASE_ICONE . '/' . $configuration['theme'] . '/' . $icone;
522                        $tableau['icone']['source'] = copie_locale($url);
523                } else {
524                        include_spip('inc/rainette_normaliser');
525                        if ($configuration['condition'] == "{$configuration['alias']}_local") {
526                                // On affiche un icône d'un thème local compatible avec Wunderground.
527                                $chemin = icone_local_normaliser(
528                                        "{$tableau['icon_meteo']}.png",
529                                        $configuration['alias'],
530                                        $configuration['theme_local']);
531                        } else {
532                                // On affiche l'icône correspondant au code météo transcodé dans le système weather.com.
533                                $chemin = icone_weather_normaliser(
534                                        $tableau['code_meteo'],
535                                        $configuration['theme_weather'],
536                                        $configuration['transcodage_weather'],
537                                        $tableau['periode']);
538                        }
539                        include_spip('inc/utils');
540                        $tableau['icone']['source'] = find_in_path($chemin);
541                }
542        }
543}
Note: See TracBrowser for help on using the repository browser.