Subversion Repositories eFlore/Projets.eflore-projets

Rev

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