Subversion Repositories eFlore/Projets.eflore-projets

Rev

Rev 1136 | Blame | Compare with Previous | Last modification | View Log | RSS feed

<?php

/**
 * Classe qui remplit un fond cartographique SVG a partir des observations en base de donnees
 * pour un taxon de plante. Elle verifie dans un premier temps la validite des parametres saisis,
 * puis charge le fond cartographique depuis le fichier, recupere dans la base de donnees
 * les observations sur la France metropolitaine pour le taxon donne et remplit la carte
 * en changeant le style CSS des mailles en fonction des coordonnees des points d'observation.
 * Le format et la taille de la carte renvoyee au client est parametrable.
 * 
 * Parametres :
 *   - referentiel : le referentiel taxonomique a interroger pour verifier le taxon. Pour l'instant,
 *     seul bdtfx (Tracheophytes de France metropolirtaine) est utilise par le web service
 *   - num_taxon : le numero taxonomique de la plante dont on veut obtenir la carte de repartition.
 *     Le rang des taxons traites par le web service sont la famille, le genre, l'espece et la sous-espece.
 *     La recherche des observations s'etend en pus sur les sous taxons et les synonymes.
 *   - source : une ou plusieurs sources de donnees a interroger. Si le parametre n'est pas indique,
 *     le web service ira rechercher les observatipons dans toutes les sources de donnees.
 *   - format : la largeur de la carte, exprimee dans une valeur entiere en pixels.
 *     Le ratio largeur:hauteur est conserve lors du redimensionnement de la carte pour le retour
 *   - retour : le type MIME (ou format de fichier) de retour. Sont acceptes par le web service
 *     le PNG (image/png) et le XML (text/html) pour renvoyer le web service
 * 
 * @package framework-0.4
 * @author Alexandre GALIBERT <alexandre.galibert@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 $Id$
 * @copyright 2013 Tela Botanica (accueil@tela-botanica.org)
 *
 */

class MoissonnageCartes {

        const MIME_MAP = "text/html";
        const MIME_PNG = "image/png";
        
        const METHODE_TELECHARGEMENT = "telecharger";
        const METHODE_AFFICHAGE = "afficher";
        
        private $ressources;
        private $parametres;
        
        private $referentiel = '';
        private $taxon   = null;
        private $sources = array();
        private $format  = 0;
        private $retour  = self::MIME_MAP;
        private $methode_livraison = self::METHODE_AFFICHAGE;
        private $erreurs = array();

        
        public function consulter($ressources, $parametres) {
                $this->parametres = $parametres;
                $this->ressources = $ressources;
                $resultat = null;
                $this->chargerClassesSousDossier();
                if ($this->analyserRessources() == true) {
                        $resultat = $this->formerLegende();
                } else {
                        $this->traiterParametres();
                        $resultat = $this->obtenirCarte();
                        if($this->methode_livraison == self::METHODE_TELECHARGEMENT) {
                                $this->telechargerCarte($resultat->corps);
                        }
                }
                return $resultat;
        }
        
        private function analyserRessources() {
                $ok = false;
                if (isset($this->ressources[0]) && $this->ressources[0] == 'legende') {
                        $ok = true;
                }
                return $ok;
        }
        
        private function formerLegende() {
                $legende = new LegendeCartes();
                $resultat = $legende->obtenirLegende();
                return $resultat;
        }
        
        private function chargerClassesSousDossier() {
                $this->verifierExistenceDossier("cartes");
                $nomDossier = dirname(__FILE__).DS."cartes";
                $dossier = opendir($nomDossier);
                $fichiersAInclure = array();
                while ($fichier = readdir($dossier)) {
                        if (filetype($nomDossier.DS.$fichier) == 'file') {
                                $fichiersAInclure[] = $nomDossier.DS.$fichier;
                        }
                }
                $fichiersAInclure = array_reverse($fichiersAInclure);
                foreach ($fichiersAInclure as $fichier) {
                        include_once($fichier);
                }
        }
        
        private function verifierExistenceDossier($nomDossier) {
                $dossier = dirname(__FILE__).DS.$nomDossier;
                if (!file_exists($dossier) || !is_dir($dossier)) {
                        $message = "Problème rencontré lors de la génération de la carte : des ressources ".
                                "nécessaires au fonctionnement du service n'ont pas été localisées sur le serveur.\n";
                        throw new Exception($message);
                }
        }
        
        private function verifierExistenceFichier($nomFichier) {
                if (!file_exists($nomFichier)) {
                        $message = "Problème rencontré lors de la génération de la carte : des ressources ".
                                "nécessaires au fonctionnement du service n'ont pas été localisées sur le serveur.\n";
                        throw new Exception($message);
                }
        }
        
        private function traiterParametres() {
                $this->verifierReferentielEtTaxon();
                $this->verifierParametreSource();
                $this->verifierParametreFormat();
                $this->verifierParametreRetour();
                $this->verifierParametreMethodeLivraison();
                if (count($this->erreurs) > 0) {
                        $this->renvoyerErreurs();
                }
        }
        
        private function verifierParametreFormat() {
                if (!isset($this->parametres['format'])) {
                        $this->erreurs[] = "Le paramètre format (dimensions) de l'image n'a pas été indiqué dans l'URL du service.";
                } elseif (preg_match('/^[1-9]\d{2}$/', $this->parametres['format']) != 1) {
                        $this->erreurs[] = "La valeur du paramètre format n'est pas acceptée par le service. ".
                                "Une largeur valide doit être un nombre entier compris entre 100 et 999.";
                } else {
                        $this->format = $this->parametres['format'];
                }
        }
        
        private function verifierParametreRetour() {
                $typesMime = array(self::MIME_MAP, self::MIME_PNG);
                if (!isset($this->parametres['retour'])) {
                        $this->erreurs[] = "Le paramètre type de retour de l'image n'a pas été indiqué dans l'URL du service.";
                } elseif (!in_array($this->parametres['retour'], $typesMime)) {
                        $this->erreurs[] = "Le format de retour ".$this->parametres['retour']." n'est pas acceptée par le service. ".
                                " Seuls les types MIME suivants sont gérés : ".implode(',', $typesMime);
                } else {
                        $this->retour = $this->parametres['retour'];
                }
        }
        
        private function verifierParametreMethodeLivraison() {
                $typesMethodeLivraison = array(self::METHODE_AFFICHAGE, self::METHODE_TELECHARGEMENT);
                if (isset($this->parametres['methode']) && !in_array($this->parametres['methode'], $typesMethodeLivraison)) {
                        $this->erreurs[] = "Le format de methode de livraison ".$this->parametres['methode']." n'est pas acceptée par le service. ".
                                                " Seuls les methodes suivantes sont gérés : ".implode(',', $typesMethodeLivraison);
                } elseif(isset($this->parametres['methode']) && in_array($this->parametres['methode'], $typesMethodeLivraison)) {
                        $this->methode_livraison = $this->parametres['methode'];
                }
        }
        
        private function verifierParametreSource() {
                $sourcesDisponibles = explode(',', trim(Config::get('sourcesDonnees')));
                if (isset($this->parametres['source'])) {
                        $sourcesParametre = explode(',', trim($this->parametres['source']));
                        foreach ($sourcesParametre as $source) {
                                if (!in_array($source, $sourcesDisponibles)) {
                                        $this->erreurs[] = "La source de données $source n'est pas disponible pour ce service. ".
                                                "Les sources suivantes sont utilisables : ".implode(',', $sourcesDisponibles).".";
                                } else {
                                        $this->sources[] = $source;
                                }
                        }
                } else {
                        $this->sources = $sourcesDisponibles;
                }
        }
        
        private function verifierReferentielEtTaxon() {
                if (!$this->estReferentielDisponible()) {
                        $this->erreurs[] = "Le référentiel ".$this->parametres['referentiel']." n'a pas été trouvé. ".
                                "La liste des référentiels disponibles pour ce service sont : ".Config::get('referentielsDispo');
                } else {
                        $this->referentiel = $this->parametres['referentiel'];
                        $taxon = $this->recupererInformationsTaxon();
                        if (is_null($taxon)) {
                                $this->erreurs[] = "Le taxon d'espèce que vous avez demandé n'a pas été trouvé dans le référentiel.";
                        } else {
                                $this->taxon = $taxon;
                        }
                }
        }
        
        private function renvoyerErreurs() {
                $message = "Les erreurs suivantes ont été rencontrées : \n".implode('\n', $this->erreurs);
                throw new Exception($message, RestServeur::HTTP_CODE_MAUVAISE_REQUETE);
        }
        
        private function estReferentielDisponible() {
                $referentielsDispo = explode(',', Config::get('referentielsDispo'));
                $estDisponible = (isset($this->parametres['referentiel'])
                        && in_array($this->parametres['referentiel'], $referentielsDispo));
                return $estDisponible;
        }
        
        private function recupererInformationsTaxon() {
                $taxon = null;
                if (isset($this->parametres['num_taxon'])) {
                        $numTaxon = $this->parametres['num_taxon'];
                        $nomTable = $this->recupererNomTableReferentiel();
                        $bdd = new Bdd();
                        $requete = "SELECT num_nom, num_nom_retenu, nom_sci, nom_complet, rang, num_taxonomique FROM {$nomTable} ".
                                "WHERE num_taxonomique={$numTaxon} ORDER BY If(num_nom=num_nom_retenu,0,1) LIMIT 0,1";
                        $taxon = $bdd->recuperer($requete);
                        if ($taxon === false) {
                                $taxon = null;
                        }
                }
                return $taxon;
        }
        
        private function recupererNomTableReferentiel() {
                $tablesReferentiel = explode(',', Config::get('bdd_table_referentiel'));
                $nomTable = '';
                foreach ($tablesReferentiel as $table) {
                        if (strstr($table, $this->referentiel) !== false) {
                                $nomTable = $table;
                        }
                }
                return $nomTable;
        }

        /**
         * Va chercher la carte dans le cache si elle existe et n'a pas dépassé la durée
         * de vie $this->dureeCache; sinon, crée la carte et la met en cache.
         * Avec le paramètre "recalculer=1", on force le vidage du cache et on recrée la carte
         */
        protected function obtenirCarte() {
                $carte = null;
                $cacheActif = Config::get('cache_miseEnCache');
                $cheminCache = Config::get('cache_stockageChemin');
                $extension = "cache"; // par défaut; indique un problème de mimetype demandé par "retour="
                if ($this->retour == self::MIME_PNG) {
                        $extension = "png";
                } elseif ($this->retour == self::MIME_MAP) {
                        $extension = "svg";
                }
                $cheminCarteEnCacheSansExtension = $cheminCache . $this->referentiel . "-nt-" . $this->taxon['num_taxonomique'] . "-" . $this->format . ".";
                $cheminCarteEnCache = $cheminCarteEnCacheSansExtension . $extension;

                // a-t-on demandé à régénérer la carte de force ?
                $recalculer = false;
                if (isset($this->parametres['recalculer'])) {
                        $recalculer = ($this->parametres['recalculer'] === '1');
                }
                // le cache est-il actif ?
                if ($cacheActif) {
                        // le fichier existe-t-il en cache ?
                        //echo "Chemin fichier: $cheminCarteEnCache<br/>";
                        if (file_exists($cheminCarteEnCache)) {
                                $limiteDuree = Config::get('cache_dureeDeVie'); // pour ne pas trop faire pipi
                                $dateFichier = filectime($cheminCarteEnCache);
                                $age = time() - $dateFichier;
                                // si le cache est trop vieux ou qu'on a demandé à recalculer
                                if (($age > $limiteDuree) || $recalculer) {
                                        // détruire le fichier obsolète
                                        unlink($cheminCarteEnCache);
                                        // en cas de rechargement forcé, détruire les fichiers cache de même dimension
                                        // et de formats différents, afin ne pas provoquer d'incohérences
                                        // @ACHTUNG système minimaliste - si on modifie les dimensions, ça va foirer
                                        if ($recalculer) {
                                                foreach (glob($cheminCarteEnCacheSansExtension . "*") as $fichierCacheDeMemeDimension) {
                                                        unlink($fichierCacheDeMemeDimension);
                                                }
                                        }
                                } else {
                                        // récupérer le fichier en cache
                                        $carte = file_get_contents($cheminCarteEnCache);
                                }
                        }
                }
                // si la carte n'a pas été trouvée en cache
                if ($carte === null) {
                        // calculer la nouvelle carte
                        $carte = $this->formerCarte();
                        // mettre la nouvelle carte en cache
                        if ($cacheActif) {
                                file_put_contents($cheminCarteEnCache, $carte);
                        }
                }

                // retour du service
                $resultat = new ResultatService();
                $resultat->mime = $this->retour;
                $resultat->corps = $carte;

                return $resultat;
        }

        /**
         * Crée la carte - prend beaucoup de temps
         * @return ResultatService
         */
        protected function formerCarte() {
                $suffixe = 'france_moissonnage';
                
                // le fichier png avec les départements est illisible en petit format
                // dans ce cas là un template plus simple est utilisé (sans les départements)
                if($this->format < 300 && $this->retour == self::MIME_PNG) {
                        $suffixe = $suffixe."_sans_departements";
                }
                
                $nomFichierSVG = Config::get('chemin')."{$suffixe}.svg";
                $this->verifierExistenceFichier($nomFichierSVG);
                
                $formateur = new FormateurSVG($nomFichierSVG, $this->sources, $this->retour, $this->format);
                $formateur->formaterCarte($this->taxon);

                $resultat = $formateur->renvoyerCarte();
                return $resultat;
        }
        
        private function telechargerCarte($fichier) {           
                if (function_exists('mb_strlen')) {
                        $taille = mb_strlen($fichier, '8bit');
                } else {
                        $taille = strlen($fichier);
                }
                
                $extension = ($this->retour == "text/html") ? 'html' : 'png';
                
                header('Content-Description: File Transfer');
                header('Content-Type: application/octet-stream');
                header('Content-Disposition: attachment; filename="carte.'.$extension.'"');
                header('Content-Transfer-Encoding: binary');
                header('Connection: Keep-Alive');
                header('Expires: 0');
                header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
                header('Pragma: public');
                header('Content-Length: '.$taille);
                
        }
        
}

?>