Subversion Repositories eFlore/Projets.eflore-projets

Rev

Rev 252 | Rev 255 | 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;
        }
}
?>