Rev 875 | 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'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) {
$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);
}
}
?>