Subversion Repositories eFlore/Projets.eflore-projets

Rev

Rev 255 | Rev 268 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
255 jpm 1
<?php
2
// declare(encoding='UTF-8');
3
/**
4
* Gère le sous-service Taxons de Cartes.
5
*
6
* @see http://www.tela-botanica.org/wikini/eflore/wakka.php?wiki=EfloreApi01Cartes
7
*
8
* @package eFlore/services
9
* @author Jean-Pascal MILCENT <jpm@tela-botanica.org>
10
* @license GPL v3 <http://www.gnu.org/licenses/gpl.txt>
11
* @license CECILL v2 <http://www.cecill.info/licences/Licence_CeCILL_V2-en.txt>
12
* @version 1.0
13
* @copyright 1999-2012 Tela Botanica (accueil@tela-botanica.org)
14
*/
15
// TODO : Config et Outils sont des classes statiques qui doivent poser des pb pour les tests...
16
class TaxonsCartes {
17
 
18
	private $parametres = array();
19
	private $ressources = array();
20
 
260 jpm 21
	const CODE_REFTAX_DEFAUT = 'bdtfx';
255 jpm 22
	const TYPE_ID_DEFAUT = 'nn';
23
	const CARTE_DEFAUT = 'france_02';
24
	const FORMAT_DEFAUT = '550';
260 jpm 25
	const VERSION_DEFAUT = '+';
255 jpm 26
	const MIME_SVG = 'image/svg+xml';
27
	const MIME_PNG = 'image/png';
28
	const PRESENCE_CHOROLOGIE = '1';
29
 
30
	private $config = array();
31
	private $cheminCartesBase = '';
260 jpm 32
	private $formatsSupportes = array(self::MIME_SVG, self::MIME_PNG);
33
	private $reftaxSupportes = array(self::CODE_REFTAX_DEFAUT);
255 jpm 34
	private $UrlNavigation = null;
35
	private $taxonsDemandes = array();
36
	private $imgLargeur = 0;
37
	private $imgHauteur = 0;
38
	private $tableMeta = '';
39
	private $tableOntologie = '';
40
	private $tableChorodep = '';
41
	private $metadonnees = '';
42
	private $ontologies = '';
43
	private $priorites = '';
44
	private $version = '';
45
	private $legende = array();
46
	private $donnees = array();
47
 
48
 
49
	public function __construct(Conteneur $conteneur) {
50
		$this->Bdd = $conteneur->getBdd();
51
		$this->tableOntologie = $conteneur->getParametre('bdd_table_ontologies');
52
		$this->config = $conteneur->getParametre('Cartes');
53
		$this->tableMeta = $conteneur->getParametre('bdd_table_meta');
54
		$this->tableOntologie = $conteneur->getParametre('bdd_table_ontologies');
55
		$this->cheminCartesBase = $this->config['chemin'];
56
		$cacheOptions = array('mise_en_cache' => $this->config['cache']['miseEnCache'],
57
					'stockage_chemin' => $this->config['cache']['stockageChemin'],
58
					'duree_de_vie' => $this->config['cache']['dureeDeVie']);
59
		$this->cache = $conteneur->getCacheSimple($cacheOptions);
60
	}
61
 
62
	public function consulter($ressources, $parametres) {
63
		$this->parametres = $parametres;
64
		$this->ressources = $ressources;
65
 
66
		$this->definirValeurParDefautDesParametres();
67
		$this->verifierParametres();
68
 
69
		$resultat = $this->obtenirResultat();
70
 
71
		return $resultat;
72
	}
73
 
74
	private function definirValeurParDefautDesParametres() {
75
		if (isset($this->parametres['retour']) == false) {
76
			$this->parametres['retour'] = self::MIME_SVG;
77
		}
78
		if (isset($this->parametres['retour.format']) == false) {
79
			$this->parametres['retour.format'] = self::FORMAT_DEFAUT;
80
		}
260 jpm 81
		if (isset($this->parametres['version.projet']) == false) {
82
			$this->parametres['version.projet'] = self::VERSION_DEFAUT;
83
		}
255 jpm 84
	}
85
 
86
	private function verifierParametres() {
87
		$erreurs = array();
88
 
89
		if (isset($this->parametres['retour']) == false) {
90
			$erreurs[] = "Le paramètre type de retour 'retour' est obligatoire.";
91
		}
92
		if ($this->verifierValeurParametreRetour() == false) {
93
			$erreurs[] = "Le type de retour '{$this->parametres['retour']}' n'est pas supporté.";
94
		}
95
		if (isset($this->parametres['retour.format']) == false) {
96
			$erreurs[] = "Le paramètre de format de retour 'retour.format' est obligatoire.";
97
		}
98
		if ($this->verifierValeurParametreFormat() == false) {
99
			$erreurs[] = "Le type de format '{$this->parametres['retour.format']}' n'est pas supporté.".
260 jpm 100
				"Veuillez indiquer un nombre entier correspondant à la largeur désirée pour la carte.";
255 jpm 101
		}
260 jpm 102
		if ($this->verifierValeurParametreVersionProjet() == false) {
103
			$erreurs[] = "Le format de version.projet '{$this->parametres['version.projet']}' n'est pas supporté.".
104
				"Veuillez indiquer le code '+' (=dernière version) ou une année sur 4 chiffres séparé d'un mois ".
105
				"sur deux chiffres par un point. Ex. : 2012.01";
106
		}
255 jpm 107
 
108
		if (count($erreurs) > 0) {
109
			$message = implode('<br />', $erreurs);
110
			$code = RestServeur::HTTP_CODE_MAUVAISE_REQUETE;
111
			throw new Exception($message, $code);
112
		}
113
	}
114
 
115
	private function verifierValeurParametreRetour() {
260 jpm 116
		return in_array($this->parametres['retour'], $this->formatsSupportes);
255 jpm 117
	}
118
 
119
	private function verifierValeurParametreFormat() {
120
		if ($ok = preg_match('/^([0-9]+)$/', $this->parametres['retour.format'], $match)) {
121
			$this->imgLargeur = $match[1];
122
		} else if ($ok = preg_match('/^([0-9]+)x([0-9]+)$/', $this->parametres['retour.format'], $match)) {
123
			$this->imgLargeur = $match[1];
124
			$this->imgHauteur = $match[2];
125
		}
126
		return $ok;
127
	}
260 jpm 128
	private function verifierValeurParametreVersionProjet() {
129
		$ok = (preg_match('/^(?:[+]|[0-9]{4}\.[0-9]{2})$/', $this->parametres['version.projet']) == 0) ? false : true;
130
		return $ok;
131
	}
255 jpm 132
 
133
	private function obtenirResultat() {
134
		$this->analyserIdentifiants();
260 jpm 135
		$this->verifierIdentifiants();
255 jpm 136
 
260 jpm 137
		$this->chargerMetadonnees();
138
		$this->definirVersion();
139
		$this->tableChorodep = 'chorodep_v'.str_replace('.', '_', $this->version);
140
		$this->chargerOntologies();
141
		$this->chargerLegende();
142
		$this->chargerPrioritesLegende();
255 jpm 143
		$this->chargerDonnees();
144
		$svg = $this->genererSVG();
145
		$img = ($this->parametres['retour'] == self::MIME_PNG) ? $this->convertirEnPNG($svg) : $svg;
146
 
147
		$resultat = new ResultatService();
148
		$resultat->corps = $img;
149
		$resultat->mime = $this->parametres['retour'];
150
 
151
		return $resultat;
152
	}
153
 
154
	private function analyserIdentifiants() {
155
		$ids = $this->ressources[0];
156
		if (preg_match('/^[0-9]+$/', $ids)) {
157
			$this->taxonsDemandes[self::CODE_REFTAX_DEFAUT]['nn'][] = $ids;
158
		} else {
159
			// ceci contient potentiellement des formes ref_tax1.nn:1,2;ref_tax2.nt:3,4
160
			$projetsListeEtNumNoms = explode(';', $ids);
161
			if (count($projetsListeEtNumNoms) > 0) {
162
				foreach ($projetsListeEtNumNoms as $projetEtNumNoms) {
163
					$projetEtNumNoms = (strpos($projetEtNumNoms, ':')) ? $projetEtNumNoms : self::CODE_REFTAX_DEFAUT.'.nn:'.$projetEtNumNoms;
164
					list($projetEtType, $numNoms) = explode(':', $projetEtNumNoms);
260 jpm 165
 
166
					$projetEtType = (strpos($projetEtType, '.')) ? $projetEtType : self::CODE_REFTAX_DEFAUT.'.'.$projetEtType;
255 jpm 167
					list($projet, $type) = explode('.', $projetEtType);
260 jpm 168
 
255 jpm 169
					$this->taxonsDemandes[$projet][$type] = explode(',', $numNoms);
170
				}
171
			}
172
		}
173
	}
174
 
260 jpm 175
	private function verifierIdentifiants() {
176
		foreach (array_keys($this->taxonsDemandes) as $reftax) {
177
			$ok = in_array($reftax, $this->reftaxSupportes);
178
			if ($ok == false) {
179
				$message = "Le reférentiel taxonomique '$reftax' n'est pas pris en compte par ce projet.";
180
				$code = RestServeur::HTTP_CODE_MAUVAISE_REQUETE;
181
				throw new Exception($message, $code);
182
			}
183
		}
184
	}
185
 
186
	private function chargerMetadonnees() {
255 jpm 187
		$requete = 'SELECT * '.
260 jpm 188
			"FROM {$this->tableMeta} ".
189
			"ORDER BY date_creation DESC ";
190
		$resultats = $this->Bdd->recupererTous($requete);
255 jpm 191
 
192
		if (!is_array($resultats) || count($resultats) <= 0) {
260 jpm 193
			$message = "Les méta-données n'ont pu être chargée pour la ressource demandée";
255 jpm 194
			$code = RestServeur::HTTP_CODE_RESSOURCE_INTROUVABLE;
195
			throw new Exception($message, $code);
196
		}
197
 
198
		$this->metadonnees = $resultats;
260 jpm 199
	}
255 jpm 200
 
260 jpm 201
	private function definirVersion() {
202
		if ($this->parametres['version.projet'] == '+') {
203
			$this->version = $this->metadonnees[0]['version'];
204
		} else {
205
			$this->version = $this->parametres['version.projet'];
206
		}
255 jpm 207
	}
208
 
209
	private function chargerOntologies() {
210
		$requete = 'SELECT * '.
260 jpm 211
			"FROM {$this->tableOntologie} ";
255 jpm 212
		$resultats = $this->Bdd->recupererTous($requete);
213
 
214
		if (!is_array($resultats) || count($resultats) <= 0) {
215
			$message = "Les données de légende n'ont pu être chargées pour la ressource demandée";
216
			$code = RestServeur::HTTP_CODE_RESSOURCE_INTROUVABLE;
217
			throw new Exception($message, $code);
218
		}
219
 
220
		foreach ($resultats as $ontologie) {
221
			$this->ontologies[$ontologie['id']] = $this->extraireComplementsOntologies($ontologie);
222
		}
223
	}
224
 
225
 
226
	private function extraireComplementsOntologies($ontologie) {
227
		if ($ontologie['complements'] != '') {
228
			$complements = explode(',', trim($ontologie['complements']));
229
			foreach ($complements as $complement) {
230
				list($cle, $val) = explode('=', trim($complement));
231
				$ontologie[trim($cle)] = trim($val);
232
			}
233
		}
234
		return $ontologie;
235
	}
236
 
237
	private function chargerLegende() {
238
		foreach ($this->ontologies as $ontologie) {
239
			if ($ontologie['classe_id'] == self::PRESENCE_CHOROLOGIE) {
240
				$this->legende[$ontologie['code']] = $ontologie['legende'];
241
			}
242
		}
243
	}
244
 
245
	private function chargerPrioritesLegende() {
246
		foreach ($this->ontologies as $ontologie) {
247
			if ($ontologie['classe_id'] == self::PRESENCE_CHOROLOGIE && isset($ontologie['priorite'])) {
248
				$this->priorites[$ontologie['code']] = $ontologie['priorite'];
249
			}
250
		}
251
	}
252
 
253
	private function chargerDonnees() {
254
		$conditions = $this->getConditions();
255
		$requete = 'SELECT * '.
256
					"FROM {$this->tableChorodep} ".
257
		(isset($conditions) ? 'WHERE '.implode(' AND ', $conditions) : '');
258
		$resultat = $this->Bdd->recupererTous($requete);
259
 
260
		if (!is_array($resultat) || count($resultat) <= 0) {
261
			$message = "Aucune donnée ne correspond à la ressource demandée";
262
			$code = RestServeur::HTTP_CODE_RESSOURCE_INTROUVABLE;
263
			throw new Exception($message, $code);
264
		}
265
		$this->donnees = $resultat;
266
	}
267
 
268
	private function getConditions() {
269
		$conditions = null;
270
		if ($nnListe = $this->getListeNumNom()) {
271
			$conditions[] = "num_nom IN ($nnListe) ";
272
		}
273
		if ($ntListe = $this->getListeNumTax()) {
274
			$conditions[] = "num_tax IN ($ntListe) ";
275
		}
276
		return $conditions;
277
	}
278
 
279
	private function getListeNumNom() {
280
		$nnListe = null;
281
		$refTax = self::CODE_REFTAX_DEFAUT;
282
		if (isset($this->taxonsDemandes[$refTax])) {
283
			$nnProteges = array();
284
			if (isset($this->taxonsDemandes[$refTax]['nn'])) {
285
				foreach ($this->taxonsDemandes[$refTax]['nn'] as $nn) {
286
					$nnProteges[] = $this->Bdd->proteger($nn);
287
				}
288
				$nnListe = implode(',', $nnProteges);
289
			}
290
		}
291
		return $nnListe;
292
	}
293
 
294
	private function getListeNumTax() {
295
		$ntListe = null;
296
		$refTax = self::CODE_REFTAX_DEFAUT;
297
		if (isset($this->taxonsDemandes[$refTax])) {
298
			$ntProteges = array();
299
			if (isset($this->taxonsDemandes[$refTax]['nt'])) {
300
				foreach ($this->taxonsDemandes[$refTax]['nt'] as $nt) {
301
					$ntProteges[] = $this->Bdd->proteger($nt);
302
				}
303
				$ntListe = implode(',', $ntProteges);
304
			}
305
		}
306
		return $ntListe;
307
	}
308
 
309
	private function genererSVG() {
310
		$dom = new DOMDocument('1.0', 'UTF-8');
311
		$dom->validateOnParse = true;
312
 
313
		$fichierCarteSvg = $this->cheminCartesBase.self::CARTE_DEFAUT.'.svg';
314
		$dom->load($fichierCarteSvg);
315
 
316
		$racineElement = $dom->documentElement;
317
		$racineElement->setAttribute('width', $this->imgLargeur);
318
 
319
		$css = $this->creerCssCarte();
320
		$styleElement = $dom->getElementsByTagName('style')->item(0);
321
		$css = $styleElement->nodeValue.$css;
322
		$txtCss = $dom->createCDATASection($css);
323
		$styleElement->nodeValue = '';
324
		$styleElement->appendChild($txtCss);
325
 
326
		$titre = $this->creerTitre();
327
		$titreCdata = $dom->createCDATASection($titre);
328
		$titreElement = $dom->getElementsByTagName('title')->item(0);
329
		$titreElement->nodeValue = '';
330
		$titreElement->appendChild($titreCdata);
331
 
332
		$taxonTitre = $this->creerTitreTaxon();
333
		$taxonCdata = $dom->createCDATASection($taxonTitre);
334
		$xpath = new DOMXPath($dom);
335
		$taxonTitreEl = $xpath->query("//*[@id='titre-taxon']")->item(0);
336
		$taxonTitreEl->nodeValue = '';
337
		$taxonTitreEl->setAttribute('title', $taxonTitre);
338
		$taxonTitreEl->appendChild($taxonCdata);
339
 
340
		$svg = $dom->saveXML();
341
		return $svg;
342
	}
343
 
344
	private function creerCssCarte() {
345
		$css = '';
346
		$this->getZonesPriorites();
347
		$zonesCouleurs = $this->getZonesCouleurs();
348
		foreach ($zonesCouleurs as $couleur => $zonesClasses) {
349
			$classes = implode(', ', $zonesClasses);
350
			$css .= "$classes{\nfill:$couleur;\n}\n";
351
		}
352
		return $css;
353
	}
354
 
355
	private function getZonesPriorites() {
356
		$this->zones = array();
357
		$zonesPrioritaires = array();
358
		foreach ($this->donnees as $donnee) {
359
			foreach ($donnee as $zoneId => $codeLegende) {
360
				if (preg_match('/^[0-9][0-9ab]$/i', $zoneId)) {
361
					if (array_key_exists($codeLegende, $this->priorites)) {
362
						$priorite = $this->priorites[$codeLegende];
363
						if (array_key_exists($zoneId, $zonesPrioritaires) == false) {
364
							$zonesPrioritaires[$zoneId] = 0;
365
						}
366
						if ($priorite > $zonesPrioritaires[$zoneId]) {
367
							$zonesPrioritaires[$zoneId] = $priorite;
368
							$this->zones[$zoneId] = $codeLegende;
369
						}
370
					}
371
				}
372
			}
373
		}
374
	}
375
 
376
	private function getZonesCouleurs() {
377
		$zones = array();
260 jpm 378
		ksort($this->zones);
255 jpm 379
		foreach ($this->zones as $zoneId => $codeLegende) {
380
			if (array_key_exists($codeLegende, $this->legende)) {
381
				$couleur = $this->legende[$codeLegende];
260 jpm 382
				$zones[$couleur][] = strtolower('.departement'.sprintf('%02s', $zoneId));
255 jpm 383
			}
384
		}
385
		return $zones;
386
	}
387
 
388
	private function creerTitre() {
389
		$titre = "Carte en cours d'élaboration pour ".$this->creerTitreTaxon();
390
		return $titre;
391
	}
392
 
393
	private function creerTitreTaxon() {
394
		$noms = array();
395
		foreach ($this->donnees as $donnee) {
396
			$noms[] = $donnee['nom_sci'];
397
		}
398
		$titre = implode(', ', $noms);
399
		return $titre;
400
	}
401
 
402
	private function convertirEnPNG($svg) {
403
		$png = null;
404
		if (extension_loaded('imagick')) {
405
			$png = $this->convertirEnPNGAvecImageMagick($svg);
406
		} else {
407
			$message = "Impossible de générer l'image sur le serveur. Extenssion ImageMagick abscente.";
408
			$code = RestServeur::HTTP_CODE_ERREUR;
409
			throw new Exception($message, $code);
410
		}
411
		return $svg;
412
	}
413
 
414
	private function convertirEnPNGAvecImageMagick($svg) {
415
		$convertisseur = new Imagick();
416
		$convertisseur->readImageBlob($svg);
417
		$convertisseur->setImageFormat("png24");
418
		$convertisseur->resizeImage($this->imgLargeur, $this->imgHauteur, imagick::FILTER_LANCZOS, 0);
419
		$png = $convertisseur->getImageBlob();
420
		$convertisseur->clear();
421
		$convertisseur->destroy();
422
		return $png;
423
	}
424
 
425
	public function getParametreTableau($cle) {
426
		$tableau = array();
427
		$parametre = $this->config[$cle];
428
		if (empty($parametre) === false) {
429
			$tableauPartiel = explode(',', $parametre);
430
			$tableauPartiel = array_map('trim', $tableauPartiel);
431
			foreach ($tableauPartiel as $champ) {
432
				if (strpos($champ, '=') === false) {
433
					$tableau[] = trim($champ);
434
				} else {
435
					list($cle, $val) = explode('=', $champ);
436
					$tableau[trim($cle)] = trim($val);
437
				}
438
			}
439
		}
440
		return $tableau;
441
	}
442
}
443
?>