Subversion Repositories eFlore/Projets.eflore-projets

Rev

Rev 747 | Rev 874 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed

<?php

/**
 * Classe qui genere la carte SVG pour les parametres de la requete qui a ete utilisee pour appeler
 * 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 FormateurSVG {
        
        private $documentXML;
        private $coordonnees = array();
        private $grille = null;
        
        private $largeur  = 0;
        private $hauteur  = 0;
        private $typeMime = '';
        private $format   = 0;
        private $sources  = array();
        
        private $image = null;
        
        const ORIGINE = 20037508.342789244;
        const MIME_MAP = 'text/html';
        
        
        public function __construct($nomFichierSVG, $sources, $typeMime, $format) {
                $this->chargerSVG($nomFichierSVG);
                $this->chargerCoordonnees();
                $this->construireParametresRetour($typeMime, $format);
                $this->creerStyleSources();
                $this->sources = $sources;
        }
        
        private function chargerSVG($nomFichierSVG) {
                $this->documentXML = new DOMDocument("1.0", "UTF-8");
                $this->documentXML->load($nomFichierSVG);
        }
        
        private function chargerCoordonnees() {
                $viewbox = $this->recupererNoeuds('qgisviewbox');
                $this->coordonnees = array(
                        'xMin' => $viewbox->attributes->getNamedItem('xMin')->value,
                        'xMax' => $viewbox->attributes->getNamedItem('xMax')->value,
                        'yMin' => $viewbox->attributes->getNamedItem('yMin')->value,
                        'yMax' => $viewbox->attributes->getNamedItem('yMax')->value
                );
        }
        
        private function recupererNoeuds($nomCouche) {
                $contenuSVG = $this->documentXML->documentElement->childNodes;
                $noeudCouche = null;
                $index = 0;
                while ($index < $contenuSVG->length && is_null($noeudCouche)) {
                        $id = $contenuSVG->item($index)->attributes->getNamedItem('id');
                        if ($id->value == $nomCouche) {
                                $noeudCouche = $contenuSVG->item($index);
                        }
                        $index ++;
                }
                $noeuds = null;
                if (!is_null($noeudCouche)) {
                        $noeuds = $noeudCouche->firstChild;
                }
                return $noeuds;
        }
        
        private function construireParametresRetour($typeMime, $format) {
                $viewBox = $this->documentXML->documentElement->attributes->getNamedItem('viewBox');
                $limitesPixels = explode(' ',$viewBox->value);
                $this->largeur = intval($limitesPixels[2]);
                $this->hauteur = intval($limitesPixels[3]);
                $this->typeMime = $typeMime;
                $this->format = intval($format);
        }
        
        private function creerStyleSources() {
                $couleurs = $this->recupererCouleursSources();
                $reglesCss = array();
                foreach ($couleurs as $codeSource => $codeCouleur) {
                        $reglesCss[] = ".{$codeSource} {\nfill:{$codeCouleur}\n}\n";
                }
                $texteCss = $this->documentXML->createCDATASection(implode(' ', $reglesCss));
                $noeudStyle = new DomElement('style', '');
                $this->documentXML->documentElement->appendChild($noeudStyle);
                $noeudStyle->appendChild($texteCss);
        }
        
        private function recupererCouleursSources() {
                $sourcesDonnees = Config::get('sourcesDonnees');
                $codesSources = str_replace('floradata', 'cel', $sourcesDonnees).',tout';
                $codes = explode(',', $codesSources);
                for ($index = 0; $index < count($codes);  $index ++) {
                        $codes[$index] = "'".$codes[$index]."'";
                }
                $codesSources = implode(',', $codes);
                $bdd = new Bdd();
                $requete = "SELECT code, SUBSTR(complements,9) AS couleur FROM ".Config::get('bdd_table_ontologies')." WHERE code IN ({$codesSources})";
                $couleurs = $bdd->recupererTous($requete);
                $listeCouleurs = array();
                foreach ($couleurs as $couleur) {
                        $couleur['code'] = $couleur['code'] == 'cel' ? 'floradata' : $couleur['code'];
                        $listeCouleurs[$couleur['code']] = $couleur['couleur'];
                }
                return $listeCouleurs;
        }
        
        

        public function formaterCarte($taxon) {
                $limitesCarte = $this->renvoyerLimitesCarte();
                $sourceDonnees = new SourceDonnees($limitesCarte, $taxon);
                // modification temporaire pour lors de la selection d'une famille on ne prenne que floradata
                // (probleme de performance, qui sera réglé en reremplissant la table de moissonnage)
                if($taxon['rang'] <= 140) {
                        foreach ($this->sources as $source) {
                                $nomMethode = "recupererStations".($source == 'floradata' ? 'Floradata' : 'Moissonnage');
                                $stations = $sourceDonnees->$nomMethode($source);
                                $this->ajouterStations($stations, $source);
                        }
                } else {
                        $stations = $sourceDonnees->recupererStationsFloradata($source);
                        $this->ajouterStations($stations, $source);
                }
                $this->supprimerMaillesVides();
        }
        
        public function renvoyerLimitesCarte() {
                $limites = array();
                list($limites['ouest'], $limites['sud']) = $this->convertirMetresEnPixels(
                        $this->coordonnees['xMin'], $this->coordonnees['yMin']);
                list($limites['est'], $limites['nord']) = $this->convertirMetresEnPixels(
                                $this->coordonnees['xMax'], $this->coordonnees['yMax']);
                return $limites;
        }
        
        private function convertirMetresEnPixels($x, $y) {
                $longitude = ($x / self::ORIGINE) * 180;
                $latitude = ($y / self::ORIGINE) * 180;
                $latitude = 180 / M_PI * (2 * atan(exp($latitude * M_PI / 180.0)) - M_PI / 2.0);
                return array(round($longitude, 6), round($latitude, 6));
        }
        
        private function ajouterStations($stations, $source) {
                $grille = $this->recupererNoeuds('grille')->childNodes;
                $index = 0;
                $maille = $grille->item($index);
                foreach ($stations as $station) {
                        if (!isset($station['lat']) || !isset($station['lng']) || !isset($station['commune'])) {
                                continue;
                        }
                        $idMaille = $maille->attributes->getNamedItem('id')->value;
                        $bbox = explode('_', substr($idMaille, 5));
                        $bbox[0] = floatval($bbox[0]);
                        $bbox[1] = floatval($bbox[1]);
                        while ($index < $grille->length && (
                                $bbox[1] > $station['lat'] || ($bbox[1] == $station['lat'] && $bbox[0] < $station['lng'])
                        )) {
                                $maille = $grille->item($index ++);
                                $idMaille = $maille->attributes->getNamedItem('id')->value;
                                $bbox = explode('_', substr($idMaille, 5));
                                $bbox[0] = floatval($bbox[0]);
                                $bbox[1] = floatval($bbox[1]);
                        }
                        if ($bbox[1] == $station['lat'] && $bbox[0] == $station['lng']) {
                                $this->ajouterCommentaire($station, $source, $maille);
                                $this->appliquerStyleMaille($source, $maille);
                        }
                        if ($index == $grille->length) {
                                break;
                        }
                }
        }
        
        private function supprimerMaillesVides() {
                $grille = $this->recupererNoeuds('grille')->childNodes;
                $index = 0;
                while ($index < $grille->length) {
                        if (!$grille->item($index)->hasAttribute('title')) {
                                $grille->item($index)->parentNode->removeChild($grille->item($index));
                        } else {
                                $index ++;
                        }
                }
        }
        
        private function appliquerStyleMaille($source, & $maille) {
                if ($maille->hasAttribute('class') && $maille->attributes->getNamedItem('class')->value != $source) {
                        $maille->setAttribute('class', 'tout');
                } elseif (!$maille->hasAttribute('class')) {
                        $maille->setAttribute('class', $source);
                }
        }
        
        private function ajouterCommentaire($station, $source, & $maille) {
                $commentaires = array();
                if ($maille->hasAttribute('title')) {
                        $commentaires = explode("; ", $maille->attributes->getNamedItem('title')->value);
                }
                $commentaire = ucfirst($source)." : {$station['commune']}, ";
                if (strlen($station['date']) == 4) {
                        $commentaire .= "en {$station['date']} par {$station['auteur']}";
                } else {
                        $date = preg_replace("/(\d{4})-(\d{2})-(\d{2})/", "$3/$2/$1", $station['date']);
                        $commentaire .= "le {$date} par {$station['auteur']}";
                }
                $commentaires[] = trim($commentaire);
                $maille->setAttribute('title', implode('; ', $commentaires));
        }
        
        
        
        public function renvoyerCarte() {
                $this->documentXML->documentElement->setAttribute("width", $this->format);
                $this->documentXML->documentElement->setAttribute("height", $this->hauteur * $this->format / $this->largeur);
                $retour = '';
                if ($this->typeMime == self::MIME_MAP) {
                        $retour = $this->documentXML->saveHTML();
                } else {
                        $retour = $this->convertirEnPng();
                }
                return $retour;
        }
        
        private function convertirEnPng() {
                $this->image = imagecreatetruecolor($this->format,  $this->hauteur * $this->format / $this->largeur);
                imagefill($this->image, 0, 0, imagecolorallocate($this->image, 255, 255, 255));
                $this->transformerLignesEnPng('departements');
                $this->transformerPolygonesEnPng('grille');
                
                // stocker le contenu encode de l'image generee dans une chaine de caracteres
                ob_start();
                imagepng($this->image);
                $png = ob_get_contents();
                ob_end_clean();
                return $png;
        }
        
        private function transformerLignesEnPng($nomCouche) {
                $facteur = floatval($this->format) / floatval($this->largeur);
                $noeudCouche = $this->recupererNoeuds($nomCouche);
                $couleurContour = $noeudCouche->attributes->getNamedItem('stroke')->value;
                for ($index = 0; $index < $noeudCouche->childNodes->length; $index ++) {
                        $noeudLigne = $noeudCouche->childNodes->item($index);
                        for ($indexPath = 0; $indexPath < $noeudLigne->childNodes->length; $indexPath ++) {
                                $coordonneesSvg = $noeudLigne->childNodes->item($indexPath)->attributes->getNamedItem('points')->value;
                                preg_match_all('/\d+.\d/', $coordonneesSvg, $coordonnees);
                                $coordonnees = current($coordonnees);
                                foreach ($coordonnees as $indexCoord => $valeur) {
                                        $coordonnees[$indexCoord] = intval(floatval($valeur) * $facteur);
                                }
                                if ($couleurContour != 'none') {
                                        for ($i = 0; $i < count($coordonnees) - 2; $i += 2) {
                                                imageline($this->image, $coordonnees[$i], $coordonnees[$i+1], $coordonnees[$i+2],
                                                        $coordonnees[$i+3], $this->allouerCouleur($couleurContour));
                                        }
                                }
                        }
                }
        }
        
        private function transformerPolygonesEnPng($nomCouche) {
                $couleurs = $this->recupererCouleursSources();
                $facteur = floatval($this->format) / floatval($this->largeur); 
                $noeudCouche = $this->recupererNoeuds($nomCouche);
                $couleurRemplissage = $noeudCouche->attributes->getNamedItem('fill')->value;
                $couleurContour = $noeudCouche->attributes->getNamedItem('stroke')->value;
                for ($index = 0; $index < $noeudCouche->childNodes->length; $index ++) {
                        $noeudPolygone = $noeudCouche->childNodes->item($index);
                        $couleurPolygone = 'none';
                        if ($noeudPolygone->hasAttribute('class')) {
                                $couleurPolygone = $couleurs[$noeudPolygone->attributes->getNamedItem('class')->value];
                        }
                        for ($indexPath = 0; $indexPath < $noeudPolygone->childNodes->length; $indexPath ++) {
                                $coordonneesSvg = $noeudPolygone->childNodes->item($indexPath)->attributes->getNamedItem('points')->value;
                                preg_match_all('/\d+.\d/', $coordonneesSvg, $coordonnees);
                                $coordonnees = current($coordonnees);
                                foreach ($coordonnees as $indexCoord => $valeur) {
                                        $coordonnees[$indexCoord] = intval(floatval($valeur) * $facteur);
                                }
                                if ($couleurRemplissage != 'none') {
                                        imagefilledpolygon($this->image, $coordonnees, count($coordonnees) / 2, $this->allouerCouleur($couleurRemplissage));
                                }
                                if ($couleurContour != 'none') {
                                        imagepolygon($this->image, $coordonnees, count($coordonnees) / 2, $this->allouerCouleur($couleurContour));
                                }
                                if ($couleurPolygone != 'none') {
                                        $contourGrille = "rgba(255,255,255,".($this->format >= 300 ? 0.4 : 0).")";
                                        imagefilledrectangle($this->image, $coordonnees[0], $coordonnees[1], $coordonnees[4], $coordonnees[5],
                                                $this->allouerCouleur($couleurPolygone));
                                        imagerectangle($this->image, $coordonnees[0], $coordonnees[1], $coordonnees[4], $coordonnees[5],
                                                $this->allouerCouleur($contourGrille));
                                }
                        }
                }
        }
        
        private function allouerCouleur($couleurTexte) {
                preg_match_all('/\d+/', $couleurTexte, $valeurs);
                $rouge = $valeurs[0][0];
                $vert  = $valeurs[0][1];
                $bleu  = $valeurs[0][2];
                $alpha = 0;
                if (count($valeurs[0]) > 3) {
                        $valeurAlpha = floatval($valeurs[0][3].".".$valeurs[0][4]);
                        $alpha = intval((1.0 - $valeurAlpha) * 127.0);
                }
                return imagecolorallocatealpha($this->image, $rouge, $vert, $bleu, $alpha);
        }
        
}

?>