* @license GPL v3 * @license CECILL v2 * @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 TaxonsCartes { private $parametres = array(); private $ressources = array(); const CODE_REFTAX_DEFAUT = 'bdtxa'; const TYPE_ID_DEFAUT = 'nn'; const CARTE_DEFAUT = 'antilles'; const FORMAT_DEFAUT = '587x550'; const VERSION_DEFAUT = '+'; const MIME_SVG = 'image/svg+xml'; const MIME_PNG = 'image/png'; const PRESENCE_CHOROLOGIE = '15'; private $config = array(); private $convertisseur = ''; private $cheminCartesBase = ''; private $formatsSupportes = array(self::MIME_SVG, self::MIME_PNG); private $reftaxSupportes = array(self::CODE_REFTAX_DEFAUT); 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(Conteneur $conteneur) { $this->Bdd = $conteneur->getBdd(); $this->tableOntologie = $conteneur->getParametre('bdd_table_ontologies'); $this->config = $conteneur->getParametre('Cartes'); $this->convertisseur = $this->config['convertisseur']; $this->tableMeta = $conteneur->getParametre('bdd_table_meta'); $this->tableOntologie = $conteneur->getParametre('bdd_table_ontologies'); $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 = $conteneur->getCacheSimple($cacheOptions); } public function consulter($ressources, $parametres) { $this->parametres = $parametres; $this->ressources = $ressources; $this->definirValeurParDefautDesParametres(); $this->verifierParametres(); $resultat = $this->obtenirResultat(); 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; } if (isset($this->parametres['version.projet']) == false) { $this->parametres['version.projet'] = self::VERSION_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 ($this->verifierValeurParametreVersionProjet() == false) { $erreurs[] = "Le format de version.projet '{$this->parametres['version.projet']}' n'est pas supporté.". "Veuillez indiquer le code '+' (=dernière version) ou une année sur 4 chiffres séparé d'un mois ". "sur deux chiffres par un point. Ex. : 2012.01"; } if (count($erreurs) > 0) { $message = implode('
', $erreurs); $code = RestServeur::HTTP_CODE_MAUVAISE_REQUETE; throw new Exception($message, $code); } } private function verifierValeurParametreRetour() { return in_array($this->parametres['retour'], $this->formatsSupportes); } 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 verifierValeurParametreVersionProjet() { $ok = (preg_match('/^(?:[+]|[0-9]{4}\.[0-9]{2})$/', $this->parametres['version.projet']) == 0) ? false : true; return $ok; } private function obtenirResultat() { $this->analyserIdentifiants(); $this->verifierIdentifiants(); $this->chargerMetadonnees(); $this->definirVersion(); $this->tableChorodep = 'bdtxa_v'.str_replace('.', '_', $this->version); $this->chargerOntologies(); $this->chargerLegende(); $this->chargerPrioritesLegende(); $this->chargerDonnees(); $svg = $this->genererSVG(); $img = ($this->parametres['retour'] == self::MIME_PNG) ? $this->convertirEnPNG($svg) : $svg; $resultat = new ResultatService(); $resultat->corps = $img; $resultat->mime = $this->parametres['retour']; return $resultat; } private function analyserIdentifiants() { $ids = $this->ressources[0]; 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); $projetEtType = (strpos($projetEtType, '.')) ? $projetEtType : self::CODE_REFTAX_DEFAUT.'.'.$projetEtType; list($projet, $type) = explode('.', $projetEtType); $this->taxonsDemandes[$projet][$type] = explode(',', $numNoms); } } } } private function verifierIdentifiants() { foreach (array_keys($this->taxonsDemandes) as $reftax) { $ok = in_array($reftax, $this->reftaxSupportes); if ($ok == false) { $message = "Le reférentiel taxonomique '$reftax' n'est pas pris en compte par ce projet."; $code = RestServeur::HTTP_CODE_MAUVAISE_REQUETE; throw new Exception($message, $code); } } } private function chargerMetadonnees() { $requete = 'SELECT * '. "FROM {$this->tableMeta} ". "ORDER BY date_creation DESC "; $resultats = $this->Bdd->recupererTous($requete); if (!is_array($resultats) || count($resultats) <= 0) { $message = "Les méta-données n'ont 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 definirVersion() { if ($this->parametres['version.projet'] == '+') { $this->version = $this->metadonnees[0]['version']; } else { $this->version = $this->parametres['version.projet']; } } private function chargerOntologies() { $requete = 'SELECT * '. "FROM {$this->tableOntologie} WHERE classe_id = (SELECT id FROM eflore_ontologies WHERE code ='presenceChorologie')"; $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']] = Commun::extraireComplementsOntologies($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']; } } } private function chargerDonnees() { $conditions = $this->getConditions(); $requete = 'SELECT nom_sci, `presence_Guadeloupe`, `presence_Martinique`, `presence_Saint_Martin`, '. "`presence_La_Desirade`, `presence_Saint_Barthelemy`, `presence_Marie_Galante`, ". "`presence_Les-Saintes` ". "FROM {$this->tableChorodep} ". (isset($conditions) ? 'WHERE '.implode(' AND ', $conditions) : ''); $resultat = $this->Bdd->recupererTous($requete); $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); if ($this->imgHauteur != 0) { $racineElement->setAttribute('height', $this->imgHauteur); } $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 {"."\n"."fill:$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('/^presence_(.)*$/i', $zoneId)) { $zoneId = $this->formaterZoneId($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 formaterZoneId($zoneId) { $zoneId = strtolower(str_replace('presence_', '', $zoneId)); $zoneId = str_replace('-', '_', $zoneId); return $zoneId; } private function getZonesCouleurs() { $zones = array(); ksort($this->zones); foreach ($this->zones as $zoneId => $codeLegende) { if (array_key_exists($codeLegende, $this->legende)) { $couleur = $this->legende[$codeLegende]; $zones[$couleur][] = strtolower('.'.sprintf('%02s', $zoneId)); } } return $zones; } private function creerTitre() { $titre = "Carte en cours d'élaboration"; if ($this->donnees != array()) { $titre .= ' pour '.$this->creerTitreTaxon(); } return $titre; } private function creerTitreTaxon() { $titre = ''; $noms = array(); foreach ($this->donnees as $donnee) { $noms[] = $donnee['nom_sci']; } $titre = implode(', ', $noms); return $titre; } private function convertirEnPNG($svg) { $png = null; if (isset($this->convertisseur)) { if ($this->convertisseur == 'imagick') { 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); } } else if ($this->convertisseur == 'rsvg') { $png = Commun::convertirEnPNGAvecRsvg($this->getIdFichier(), $this->config['cache_stockageChemin'], $svg); } else { $message = "Le convertisseur indiqué '{$this->convertisseur}' ne fait pas parti de la liste ". "des convertisseurs disponibles : imagick, rsvg."; $code = RestServeur::HTTP_CODE_ERREUR; throw new Exception($message, $code); } } else { $message = "Veuillez indiquer le convertisseur de svg à utiliser pour le service."; $code = RestServeur::HTTP_CODE_ERREUR; throw new Exception($message, $code); } return $png; } private function convertirEnPNGAvecImageMagick($svg) { $convertisseur = new Imagick(); $convertisseur->setBackgroundColor(new ImagickPixel('transparent')); $convertisseur->readImageBlob($svg); $convertisseur->setImageFormat('png32'); $convertisseur->resizeImage($this->imgLargeur, $this->imgHauteur, imagick::FILTER_LANCZOS, 0, true); $png = $convertisseur->getImageBlob(); $convertisseur->clear(); $convertisseur->destroy(); return $png; } private function getIdFichier() { $id = ''; foreach ($this->taxonsDemandes as $reftax => $ids) { $id[] = $reftax; foreach ($ids as $type => $vals) { $id[] = $type; $id[] = implode('-', $vals); } } $id = implode('-', $id); return $id; } 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; } } ?>