* @license GPL v3 * @license CECILL v2 * @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() { $bdd = new Bdd(); $requete = "SELECT code, SUBSTR(complements,9) AS couleur FROM ".Config::get('bdd_table_ontologies')." WHERE classe_id = 10"; $couleurs = $bdd->recupererTous($requete); $listeCouleurs = array(); foreach ($couleurs as $couleur) { $couleur['code'] = $couleur['code'] == 'cel' ? 'floradata' : $couleur['code']; $couleur['code'] = $couleur['code'] == 'VF' ? 'vigie_flore' : $couleur['code']; $couleur['code'] = $couleur['code'] == 'PF' ? 'photoflora' : $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'un rang au dessus de famille on ne prenne que floradata // (probleme de performance, qui sera réglé en reremplissant la table de moissonnage) if($taxon['rang'] >= 180) { foreach ($this->sources as $source) { switch ($source) { case 'floradata' : $nomMethode = "recupererStationsFloradata"; break; case 'vigie_flore' : $nomMethode = "recupererStationsVigieFlore"; break; case 'photoflora' : $nomMethode = "recupererStationsPhotoflora"; break; default : $nomMethode = "recupererStationsMoissonnage"; break; } $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);//print_r($commentaires);echo "

"; $titre = $this->documentXML->createElement('title', implode('; ', $commentaires)); if ($maille->hasChildNodes() && $maille->childNodes->length > 1) { $maille->replaceChild($titre, $maille->childNodes->item(1)); //print_r($maille->childNodes->item(1));echo "

"; } else { $maille->appendChild($titre); } $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; $couleurContour = ($this->format >= 300 ? $couleurContour : "rgb(192,192,192)"); 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 (is_array($coordonnees) && $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); } } ?>