Subversion Repositories eFlore/Projets.eflore-projets

Rev

Rev 252 | Rev 255 | 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) {
253 jpm 199
			$message = "A implémenter : carte proportionnelle ensemble des infos";
200
			$code = RestServeur::HTTP_CODE_RESSOURCE_INTROUVABLE;
201
		} else if (count($this->ressources) == 1) {
252 jpm 202
			$positionIds = 0;
203
			if ($this->etreRessourceIdentifiants($positionIds)) {
204
				$ids = $this->ressources[$positionIds];
205
				$this->analyserIdentifiants($ids);
206
				// TODO : charger une nouvelle classe executant le sous service.
253 jpm 207
			} else if ($this->etreRessourceLegende(0)) {
208
				$message = "A implémenter : légende carte proportionnelle ensemble des infos";
209
				$code = RestServeur::HTTP_CODE_RESSOURCE_INTROUVABLE;
210
				throw new Exception($message, $code);
237 jpm 211
			} else {
253 jpm 212
				$message = "La ressource n°1 '{$this->ressources[0]} indiquée n'est pas valable.";
213
				$code = RestServeur::HTTP_CODE_MAUVAISE_REQUETE;
214
				throw new Exception($message, $code);
252 jpm 215
			}
216
		} else if (count($this->ressources) == 2) {
253 jpm 217
			if ($this->etreRessourceIdentifiants(0)) {
218
				$ids = $this->ressources[0];
252 jpm 219
				$this->analyserIdentifiants($ids);
220
			}
221
			if ($this->etreRessourceLegende(1)) {
222
				// TODO : charger une nouvelle classe executant le sous service.
223
			}
224
		}
225
	}
226
 
227
	private function etreRessourceIdentifiants($position) {
228
		$ok = true;
229
		if (isset($this->ressources[$position])) {
230
			$ids = $this->ressources[$position];
231
			$projetPattern = '(?:(?:[A-Z0-9]+(\.(?:nn|nt)?):)?(?:[0-9]+,)*[0-9]+)';
232
			$patternComplet = "/^$projetPattern(?:;$projetPattern)*$/i";
233
			$ok = preg_match($patternComplet, $ids) ? true : false;
234
		}
235
		return $ok;
236
	}
237
 
238
	private function analyserIdentifiants($ids) {
239
		if (preg_match('/^[0-9]+$/', $ids)) {
240
			$this->taxonsDemandes[self::CODE_REFTAX_DEFAUT]['nn'][] = $ids;
241
		} else {
242
			// ceci contient potentiellement des formes ref_tax1.nn:1,2;ref_tax2.nt:3,4
243
			$projetsListeEtNumNoms = explode(';', $ids);
244
			if (count($projetsListeEtNumNoms) > 0) {
245
				foreach ($projetsListeEtNumNoms as $projetEtNumNoms) {
246
					$projetEtNumNoms = (strpos($projetEtNumNoms, ':')) ? $projetEtNumNoms : self::CODE_REFTAX_DEFAUT.'.nn:'.$projetEtNumNoms;
247
					list($projetEtType, $numNoms) = explode(':', $projetEtNumNoms);
248
					list($projet, $type) = explode('.', $projetEtType);
249
					$this->taxonsDemandes[$projet][$type] = explode(',', $numNoms);
237 jpm 250
				}
251
			}
252
		}
253
	}
254
 
252 jpm 255
	private function etreRessourceLegende($position) {
256
		$ok = true;
257
		if (isset($this->ressources[$position])) {
258
			$legende = $this->ressources[$position];
259
			$ok = ($legende == 'legende') ? true : false;
260
		}
261
		return $ok;
262
	}
263
 
248 jpm 264
	private function chargerDonnees() {
252 jpm 265
		$conditions = $this->getConditions();
248 jpm 266
		$requete = 'SELECT * '.
252 jpm 267
				"FROM {$this->tableChorodep} ".
268
				(isset($conditions) ? 'WHERE '.implode(' AND ', $conditions) : '');
248 jpm 269
		$resultat = $this->Bdd->recupererTous($requete);
237 jpm 270
 
248 jpm 271
		if (!is_array($resultat) || count($resultat) <= 0) {
272
			$message = "Aucune donnée ne correspond à la ressource demandée";
273
			$code = RestServeur::HTTP_CODE_RESSOURCE_INTROUVABLE;
274
			throw new Exception($message, $code);
275
		}
276
		$this->donnees = $resultat;
277
	}
237 jpm 278
 
252 jpm 279
	private function getConditions() {
280
		$conditions = null;
281
		if ($nnListe = $this->getListeNumNom()) {
282
			$conditions[] = "num_nom IN ($nnListe) ";
283
		}
284
		if ($ntListe = $this->getListeNumTax()) {
285
			$conditions[] = "num_tax IN ($ntListe) ";
286
		}
287
		return $conditions;
288
	}
289
 
290
	private function getListeNumNom() {
291
		$nnListe = null;
292
		$refTax = self::CODE_REFTAX_DEFAUT;
293
		if (isset($this->taxonsDemandes[$refTax])) {
294
			$nnProteges = array();
295
			if (isset($this->taxonsDemandes[$refTax]['nn'])) {
296
				foreach ($this->taxonsDemandes[$refTax]['nn'] as $nn) {
297
					$nnProteges[] = $this->Bdd->proteger($nn);
298
				}
299
				$nnListe = implode(',', $nnProteges);
300
			}
301
		}
302
		return $nnListe;
303
	}
304
 
305
	private function getListeNumTax() {
306
		$ntListe = null;
307
		$refTax = self::CODE_REFTAX_DEFAUT;
308
		if (isset($this->taxonsDemandes[$refTax])) {
309
			$ntProteges = array();
310
			if (isset($this->taxonsDemandes[$refTax]['nt'])) {
311
				foreach ($this->taxonsDemandes[$refTax]['nt'] as $nt) {
312
					$ntProteges[] = $this->Bdd->proteger($nt);
313
				}
314
				$ntListe = implode(',', $ntProteges);
315
			}
316
		}
317
		return $ntListe;
318
	}
319
 
248 jpm 320
	private function genererSVG() {
237 jpm 321
		$dom = new DOMDocument('1.0', 'UTF-8');
248 jpm 322
		$dom->validateOnParse = true;
323
 
324
		$fichierCarteSvg = $this->cheminCartesBase.self::CARTE_DEFAUT.'.svg';
237 jpm 325
		$dom->load($fichierCarteSvg);
248 jpm 326
 
237 jpm 327
		$racineElement = $dom->documentElement;
328
		$racineElement->setAttribute('width', $this->imgLargeur);
248 jpm 329
 
330
		$css = $this->creerCssCarte();
252 jpm 331
		$styleElement = $dom->getElementsByTagName('style')->item(0);
332
		$css = $styleElement->nodeValue.$css;
248 jpm 333
		$txtCss = $dom->createCDATASection($css);
252 jpm 334
		$styleElement->nodeValue = '';
335
		$styleElement->appendChild($txtCss);
248 jpm 336
 
337
		$titre = $this->creerTitre();
338
		$titreCdata = $dom->createCDATASection($titre);
339
		$titreElement = $dom->getElementsByTagName('title')->item(0);
340
		$titreElement->nodeValue = '';
341
		$titreElement->appendChild($titreCdata);
342
 
343
		$taxonTitre = $this->creerTitreTaxon();
344
		$taxonCdata = $dom->createCDATASection($taxonTitre);
345
		$xpath = new DOMXPath($dom);
346
		$taxonTitreEl = $xpath->query("//*[@id='titre-taxon']")->item(0);
347
		$taxonTitreEl->nodeValue = '';
252 jpm 348
		$taxonTitreEl->setAttribute('title', $taxonTitre);
248 jpm 349
		$taxonTitreEl->appendChild($taxonCdata);
350
 
237 jpm 351
		$svg = $dom->saveXML();
352
		return $svg;
353
	}
354
 
248 jpm 355
	private function creerCssCarte() {
252 jpm 356
		$css = '';
357
		$this->getZonesPriorites();
248 jpm 358
		$zonesCouleurs = $this->getZonesCouleurs();
359
		foreach ($zonesCouleurs as $couleur => $zonesClasses) {
360
			$classes = implode(', ', $zonesClasses);
361
			$css .= "$classes{\nfill:$couleur;\n}\n";
362
		}
363
		return $css;
364
	}
237 jpm 365
 
252 jpm 366
	private function getZonesPriorites() {
367
		$this->zones = array();
368
		$zonesPrioritaires = array();
248 jpm 369
		foreach ($this->donnees as $donnee) {
252 jpm 370
			foreach ($donnee as $zoneId => $codeLegende) {
371
				if (preg_match('/^[0-9][0-9ab]$/i', $zoneId)) {
372
					if (array_key_exists($codeLegende, $this->priorites)) {
373
						$priorite = $this->priorites[$codeLegende];
374
						if (array_key_exists($zoneId, $zonesPrioritaires) == false) {
375
							$zonesPrioritaires[$zoneId] = 0;
376
						}
377
						if ($priorite > $zonesPrioritaires[$zoneId]) {
378
							$zonesPrioritaires[$zoneId] = $priorite;
379
							$this->zones[$zoneId] = $codeLegende;
380
						}
248 jpm 381
					}
382
				}
383
			}
237 jpm 384
		}
252 jpm 385
	}
386
 
387
	private function getZonesCouleurs() {
388
		$zones = array();
389
		foreach ($this->zones as $zoneId => $codeLegende) {
390
			if (array_key_exists($codeLegende, $this->legende)) {
391
				$couleur = $this->legende[$codeLegende];
392
				$zones[$couleur][] = strtolower(".departement$zoneId");
393
			}
394
		}
248 jpm 395
		return $zones;
396
	}
237 jpm 397
 
248 jpm 398
	private function creerTitre() {
399
		$titre = "Carte en cours d'élaboration pour ".$this->creerTitreTaxon();
400
		return $titre;
237 jpm 401
	}
402
 
248 jpm 403
	private function creerTitreTaxon() {
404
		$noms = array();
405
		foreach ($this->donnees as $donnee) {
406
			$noms[] = $donnee['nom_sci'];
407
		}
408
		$titre = implode(', ', $noms);
409
		return $titre;
237 jpm 410
	}
411
 
412
	private function convertirEnPNG($svg) {
413
		$png = null;
414
		if (extension_loaded('imagick')) {
415
			$png = $this->convertirEnPNGAvecImageMagick($svg);
416
		} else {
417
			$message = "Impossible de générer l'image sur le serveur. Extenssion ImageMagick abscente.";
418
			$code = RestServeur::HTTP_CODE_ERREUR;
419
			throw new Exception($message, $code);
420
		}
421
		return $svg;
422
	}
423
 
424
	private function convertirEnPNGAvecImageMagick($svg) {
425
		$convertisseur = new Imagick();
426
		$convertisseur->readImageBlob($svg);
427
		$convertisseur->setImageFormat("png24");
428
		$convertisseur->resizeImage($this->imgLargeur, $this->imgHauteur, imagick::FILTER_LANCZOS, 0);
429
		$png = $convertisseur->getImageBlob();
430
		$convertisseur->clear();
431
		$convertisseur->destroy();
432
		return $png;
433
	}
248 jpm 434
 
435
	public function getParametreTableau($cle) {
436
		$tableau = array();
437
		$parametre = $this->config[$cle];
438
		if (empty($parametre) === false) {
439
			$tableauPartiel = explode(',', $parametre);
440
			$tableauPartiel = array_map('trim', $tableauPartiel);
441
			foreach ($tableauPartiel as $champ) {
442
				if (strpos($champ, '=') === false) {
443
					$tableau[] = trim($champ);
444
				} else {
445
					list($cle, $val) = explode('=', $champ);
446
					$tableau[trim($cle)] = trim($val);
447
				}
448
			}
449
		}
450
		return $tableau;
451
	}
237 jpm 452
}
453
?>