Rev 252 | Rev 259 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed
<?php
// declare(encoding='UTF-8');
/**
* Classe implémentant l'API d'eFlore Cartes pour le projet CHORODEP.
*
* @see http://www.tela-botanica.org/wikini/eflore/wakka.php?wiki=EfloreApi01Cartes
*
* @package eFlore/services
* @author Jean-Pascal MILCENT <jpm@tela-botanica.org>
* @license GPL v3 <http://www.gnu.org/licenses/gpl.txt>
* @license CECILL v2 <http://www.cecill.info/licences/Licence_CeCILL_V2-en.txt>
* @version 1.0
* @copyright 1999-2012 Tela Botanica (accueil@tela-botanica.org)
*/
// TODO : Config et Outils sont des classes statiques qui doivent poser des pb pour les tests...
class Cartes {
private $parametres = array();
private $ressources = array();
private $Bdd;
const CODE_REFTAX_DEFAUT = 'bdtfx';
const TYPE_ID_DEFAUT = 'nn';
const CARTE_DEFAUT = 'france_02';
const FORMAT_DEFAUT = '550';
const MIME_SVG = 'image/svg+xml';
const MIME_PNG = 'image/png';
const PRESENCE_CHOROLOGIE = '1';
private $config = array();
private $cheminCartesBase = '';
private $formats_supportes = array(self::MIME_SVG, self::MIME_PNG);
private $UrlNavigation = null;
private $taxonsDemandes = array();
private $imgLargeur = 0;
private $imgHauteur = 0;
private $tableMeta = '';
private $tableOntologie = '';
private $tableChorodep = '';
private $metadonnees = '';
private $ontologies = '';
private $priorites = '';
private $version = '';
private $legende = array();
private $donnees = array();
public function __construct(Bdd $bdd = null, Array $config = null, CacheSimple $cache = null) {
$this->Bdd = is_null($bdd) ? new Bdd() : $bdd;
$this->config = is_null($config) ? Config::get('Cartes') : $config['Cartes'];
$this->tableMeta = is_null($config) ? Config::get('bdd_table_meta') : $config['bdd_table_meta'];
$this->tableOntologie = is_null($config) ? Config::get('bdd_table_ontologies') : $config['bdd_table_ontologies'];
$this->chargerMetadonneesDerniereVersion();
$this->version = $this->metadonnees['version'];
$this->tableChorodep = 'chorodep_v'.str_replace('.', '_', $this->version);
$this->chargerOntologies();
$this->chargerLegende();
$this->chargerPrioritesLegende();
$this->cheminCartesBase = $this->config['chemin'];
$cacheOptions = array('mise_en_cache' => $this->config['cache']['miseEnCache'],
'stockage_chemin' => $this->config['cache']['stockageChemin'],
'duree_de_vie' => $this->config['cache']['dureeDeVie']);
$this->cache = is_null($cache) ? new CacheSimple($cacheOptions) : $cache;
}
private function chargerMetadonneesDerniereVersion() {
$requete = 'SELECT * '.
"FROM {$this->tableMeta} ".
"ORDER BY date_creation DESC ".
"LIMIT 0,1 ";
$resultats = $this->Bdd->recuperer($requete);
if (!is_array($resultats) || count($resultats) <= 0) {
$message = "Les données de version n'a pu être chargée pour la ressource demandée";
$code = RestServeur::HTTP_CODE_RESSOURCE_INTROUVABLE;
throw new Exception($message, $code);
}
$this->metadonnees = $resultats;
}
private function chargerOntologies() {
$requete = 'SELECT * '.
"FROM {$this->tableOntologie} ";
$resultats = $this->Bdd->recupererTous($requete);
if (!is_array($resultats) || count($resultats) <= 0) {
$message = "Les données de légende n'ont pu être chargées pour la ressource demandée";
$code = RestServeur::HTTP_CODE_RESSOURCE_INTROUVABLE;
throw new Exception($message, $code);
}
foreach ($resultats as $ontologie) {
$this->ontologies[$ontologie['id']] = $this->extraireComplementsOntologies($ontologie);
}
}
private function extraireComplementsOntologies($ontologie) {
if ($ontologie['complements'] != '') {
$complements = explode(',', trim($ontologie['complements']));
foreach ($complements as $complement) {
list($cle, $val) = explode('=', trim($complement));
$ontologie[trim($cle)] = trim($val);
}
}
return $ontologie;
}
private function chargerLegende() {
foreach ($this->ontologies as $ontologie) {
if ($ontologie['classe_id'] == self::PRESENCE_CHOROLOGIE) {
$this->legende[$ontologie['code']] = $ontologie['legende'];
}
}
}
private function chargerPrioritesLegende() {
foreach ($this->ontologies as $ontologie) {
if ($ontologie['classe_id'] == self::PRESENCE_CHOROLOGIE && isset($ontologie['priorite'])) {
$this->priorites[$ontologie['code']] = $ontologie['priorite'];
}
}
}
public function consulter($ressources, $parametres) {
//$tpsDebut = microtime(true);
$this->parametres = $parametres;
$this->ressources = $ressources;
$this->definirValeurParDefautDesParametres();
$this->verifierParametres();
$this->analyserRessources();
$resultat = new ResultatService();
$this->chargerDonnees();
if ($this->parametres['retour'] == self::MIME_SVG) {
$svg = $this->genererSVG();
$resultat->corps = $svg;
} else if ($this->parametres['retour'] == self::MIME_PNG) {
$svg = $this->genererSVG();
$png = $this->convertirEnPNG($svg);
$resultat->corps = $png;
}
$resultat->mime = $this->parametres['retour'];
return $resultat;
}
private function definirValeurParDefautDesParametres() {
if (isset($this->parametres['retour']) == false) {
$this->parametres['retour'] = self::MIME_SVG;
}
if (isset($this->parametres['retour.format']) == false) {
$this->parametres['retour.format'] = self::FORMAT_DEFAUT;
}
}
private function verifierParametres() {
$erreurs = array();
if (isset($this->parametres['retour']) == false) {
$erreurs[] = "Le paramètre type de retour 'retour' est obligatoire.";
}
if ($this->verifierValeurParametreRetour() == false) {
$erreurs[] = "Le type de retour '{$this->parametres['retour']}' n'est pas supporté.";
}
if (isset($this->parametres['retour.format']) == false) {
$erreurs[] = "Le paramètre de format de retour 'retour.format' est obligatoire.";
}
if ($this->verifierValeurParametreFormat() == false) {
$erreurs[] = "Le type de format '{$this->parametres['retour.format']}' n'est pas supporté.".
"Veuillez indiquer un nombre entier correspondant à la largeur désirée pour la carte.";
}
if (count($erreurs) > 0) {
$message = implode('<br />', $erreurs);
$code = RestServeur::HTTP_CODE_MAUVAISE_REQUETE;
throw new Exception($message, $code);
}
}
private function verifierValeurParametreRetour() {
return in_array($this->parametres['retour'], $this->formats_supportes);
}
private function verifierValeurParametreFormat() {
if ($ok = preg_match('/^([0-9]+)$/', $this->parametres['retour.format'], $match)) {
$this->imgLargeur = $match[1];
} else if ($ok = preg_match('/^([0-9]+)x([0-9]+)$/', $this->parametres['retour.format'], $match)) {
$this->imgLargeur = $match[1];
$this->imgHauteur = $match[2];
}
return $ok;
}
private function analyserRessources() {
if (count($this->ressources) == 1) {
$message = "A implémenter : carte proportionnelle ensemble des infos";
$code = RestServeur::HTTP_CODE_RESSOURCE_INTROUVABLE;
} else if (count($this->ressources) == 1) {
$positionIds = 0;
if ($this->etreRessourceIdentifiants($positionIds)) {
$ids = $this->ressources[$positionIds];
$this->analyserIdentifiants($ids);
// TODO : charger une nouvelle classe executant le sous service.
} else if ($this->etreRessourceLegende(0)) {
$message = "A implémenter : légende carte proportionnelle ensemble des infos";
$code = RestServeur::HTTP_CODE_RESSOURCE_INTROUVABLE;
throw new Exception($message, $code);
} else {
$message = "La ressource n°1 '{$this->ressources[0]} indiquée n'est pas valable.";
$code = RestServeur::HTTP_CODE_MAUVAISE_REQUETE;
throw new Exception($message, $code);
}
} else if (count($this->ressources) == 2) {
if ($this->etreRessourceIdentifiants(0)) {
$ids = $this->ressources[0];
$this->analyserIdentifiants($ids);
}
if ($this->etreRessourceLegende(1)) {
// TODO : charger une nouvelle classe executant le sous service.
}
}
}
private function etreRessourceIdentifiants($position) {
$ok = true;
if (isset($this->ressources[$position])) {
$ids = $this->ressources[$position];
$projetPattern = '(?:(?:[A-Z0-9]+(\.(?:nn|nt)?):)?(?:[0-9]+,)*[0-9]+)';
$patternComplet = "/^$projetPattern(?:;$projetPattern)*$/i";
$ok = preg_match($patternComplet, $ids) ? true : false;
}
return $ok;
}
private function analyserIdentifiants($ids) {
if (preg_match('/^[0-9]+$/', $ids)) {
$this->taxonsDemandes[self::CODE_REFTAX_DEFAUT]['nn'][] = $ids;
} else {
// ceci contient potentiellement des formes ref_tax1.nn:1,2;ref_tax2.nt:3,4
$projetsListeEtNumNoms = explode(';', $ids);
if (count($projetsListeEtNumNoms) > 0) {
foreach ($projetsListeEtNumNoms as $projetEtNumNoms) {
$projetEtNumNoms = (strpos($projetEtNumNoms, ':')) ? $projetEtNumNoms : self::CODE_REFTAX_DEFAUT.'.nn:'.$projetEtNumNoms;
list($projetEtType, $numNoms) = explode(':', $projetEtNumNoms);
list($projet, $type) = explode('.', $projetEtType);
$this->taxonsDemandes[$projet][$type] = explode(',', $numNoms);
}
}
}
}
private function etreRessourceLegende($position) {
$ok = true;
if (isset($this->ressources[$position])) {
$legende = $this->ressources[$position];
$ok = ($legende == 'legende') ? true : false;
}
return $ok;
}
private function chargerDonnees() {
$conditions = $this->getConditions();
$requete = 'SELECT * '.
"FROM {$this->tableChorodep} ".
(isset($conditions) ? 'WHERE '.implode(' AND ', $conditions) : '');
$resultat = $this->Bdd->recupererTous($requete);
if (!is_array($resultat) || count($resultat) <= 0) {
$message = "Aucune donnée ne correspond à la ressource demandée";
$code = RestServeur::HTTP_CODE_RESSOURCE_INTROUVABLE;
throw new Exception($message, $code);
}
$this->donnees = $resultat;
}
private function getConditions() {
$conditions = null;
if ($nnListe = $this->getListeNumNom()) {
$conditions[] = "num_nom IN ($nnListe) ";
}
if ($ntListe = $this->getListeNumTax()) {
$conditions[] = "num_tax IN ($ntListe) ";
}
return $conditions;
}
private function getListeNumNom() {
$nnListe = null;
$refTax = self::CODE_REFTAX_DEFAUT;
if (isset($this->taxonsDemandes[$refTax])) {
$nnProteges = array();
if (isset($this->taxonsDemandes[$refTax]['nn'])) {
foreach ($this->taxonsDemandes[$refTax]['nn'] as $nn) {
$nnProteges[] = $this->Bdd->proteger($nn);
}
$nnListe = implode(',', $nnProteges);
}
}
return $nnListe;
}
private function getListeNumTax() {
$ntListe = null;
$refTax = self::CODE_REFTAX_DEFAUT;
if (isset($this->taxonsDemandes[$refTax])) {
$ntProteges = array();
if (isset($this->taxonsDemandes[$refTax]['nt'])) {
foreach ($this->taxonsDemandes[$refTax]['nt'] as $nt) {
$ntProteges[] = $this->Bdd->proteger($nt);
}
$ntListe = implode(',', $ntProteges);
}
}
return $ntListe;
}
private function genererSVG() {
$dom = new DOMDocument('1.0', 'UTF-8');
$dom->validateOnParse = true;
$fichierCarteSvg = $this->cheminCartesBase.self::CARTE_DEFAUT.'.svg';
$dom->load($fichierCarteSvg);
$racineElement = $dom->documentElement;
$racineElement->setAttribute('width', $this->imgLargeur);
$css = $this->creerCssCarte();
$styleElement = $dom->getElementsByTagName('style')->item(0);
$css = $styleElement->nodeValue.$css;
$txtCss = $dom->createCDATASection($css);
$styleElement->nodeValue = '';
$styleElement->appendChild($txtCss);
$titre = $this->creerTitre();
$titreCdata = $dom->createCDATASection($titre);
$titreElement = $dom->getElementsByTagName('title')->item(0);
$titreElement->nodeValue = '';
$titreElement->appendChild($titreCdata);
$taxonTitre = $this->creerTitreTaxon();
$taxonCdata = $dom->createCDATASection($taxonTitre);
$xpath = new DOMXPath($dom);
$taxonTitreEl = $xpath->query("//*[@id='titre-taxon']")->item(0);
$taxonTitreEl->nodeValue = '';
$taxonTitreEl->setAttribute('title', $taxonTitre);
$taxonTitreEl->appendChild($taxonCdata);
$svg = $dom->saveXML();
return $svg;
}
private function creerCssCarte() {
$css = '';
$this->getZonesPriorites();
$zonesCouleurs = $this->getZonesCouleurs();
foreach ($zonesCouleurs as $couleur => $zonesClasses) {
$classes = implode(', ', $zonesClasses);
$css .= "$classes{\nfill:$couleur;\n}\n";
}
return $css;
}
private function getZonesPriorites() {
$this->zones = array();
$zonesPrioritaires = array();
foreach ($this->donnees as $donnee) {
foreach ($donnee as $zoneId => $codeLegende) {
if (preg_match('/^[0-9][0-9ab]$/i', $zoneId)) {
if (array_key_exists($codeLegende, $this->priorites)) {
$priorite = $this->priorites[$codeLegende];
if (array_key_exists($zoneId, $zonesPrioritaires) == false) {
$zonesPrioritaires[$zoneId] = 0;
}
if ($priorite > $zonesPrioritaires[$zoneId]) {
$zonesPrioritaires[$zoneId] = $priorite;
$this->zones[$zoneId] = $codeLegende;
}
}
}
}
}
}
private function getZonesCouleurs() {
$zones = array();
foreach ($this->zones as $zoneId => $codeLegende) {
if (array_key_exists($codeLegende, $this->legende)) {
$couleur = $this->legende[$codeLegende];
$zones[$couleur][] = strtolower(".departement$zoneId");
}
}
return $zones;
}
private function creerTitre() {
$titre = "Carte en cours d'élaboration pour ".$this->creerTitreTaxon();
return $titre;
}
private function creerTitreTaxon() {
$noms = array();
foreach ($this->donnees as $donnee) {
$noms[] = $donnee['nom_sci'];
}
$titre = implode(', ', $noms);
return $titre;
}
private function convertirEnPNG($svg) {
$png = null;
if (extension_loaded('imagick')) {
$png = $this->convertirEnPNGAvecImageMagick($svg);
} else {
$message = "Impossible de générer l'image sur le serveur. Extenssion ImageMagick abscente.";
$code = RestServeur::HTTP_CODE_ERREUR;
throw new Exception($message, $code);
}
return $svg;
}
private function convertirEnPNGAvecImageMagick($svg) {
$convertisseur = new Imagick();
$convertisseur->readImageBlob($svg);
$convertisseur->setImageFormat("png24");
$convertisseur->resizeImage($this->imgLargeur, $this->imgHauteur, imagick::FILTER_LANCZOS, 0);
$png = $convertisseur->getImageBlob();
$convertisseur->clear();
$convertisseur->destroy();
return $png;
}
public function getParametreTableau($cle) {
$tableau = array();
$parametre = $this->config[$cle];
if (empty($parametre) === false) {
$tableauPartiel = explode(',', $parametre);
$tableauPartiel = array_map('trim', $tableauPartiel);
foreach ($tableauPartiel as $champ) {
if (strpos($champ, '=') === false) {
$tableau[] = trim($champ);
} else {
list($cle, $val) = explode('=', $champ);
$tableau[trim($cle)] = trim($val);
}
}
}
return $tableau;
}
}
?>