Subversion Repositories eFlore/Projets.eflore-projets

Rev

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

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