727 |
alex |
1 |
<?php
|
|
|
2 |
|
747 |
alex |
3 |
/**
|
|
|
4 |
* Classe qui genere la carte SVG pour les parametres de la requete qui a ete utilisee pour appeler
|
|
|
5 |
* le web service
|
|
|
6 |
*
|
|
|
7 |
* @package framework-0.4
|
|
|
8 |
* @author Alexandre GALIBERT <alexandre.galibert@tela-botanica.org>
|
|
|
9 |
* @license GPL v3 <http://www.gnu.org/licenses/gpl.txt>
|
|
|
10 |
* @license CECILL v2 <http://www.cecill.info/licences/Licence_CeCILL_V2-en.txt>
|
|
|
11 |
* @version $Id$
|
|
|
12 |
* @copyright 2013 Tela Botanica (accueil@tela-botanica.org)
|
|
|
13 |
*
|
|
|
14 |
*/
|
|
|
15 |
|
|
|
16 |
|
727 |
alex |
17 |
class FormateurSVG {
|
|
|
18 |
|
|
|
19 |
private $documentXML;
|
|
|
20 |
private $coordonnees = array();
|
|
|
21 |
private $grille = null;
|
|
|
22 |
|
|
|
23 |
private $largeur = 0;
|
|
|
24 |
private $hauteur = 0;
|
|
|
25 |
private $typeMime = '';
|
|
|
26 |
private $format = 0;
|
|
|
27 |
private $sources = array();
|
|
|
28 |
|
|
|
29 |
private $image = null;
|
|
|
30 |
|
|
|
31 |
const ORIGINE = 20037508.342789244;
|
|
|
32 |
const MIME_MAP = 'text/html';
|
|
|
33 |
|
|
|
34 |
|
|
|
35 |
public function __construct($nomFichierSVG, $sources, $typeMime, $format) {
|
|
|
36 |
$this->chargerSVG($nomFichierSVG);
|
|
|
37 |
$this->chargerCoordonnees();
|
|
|
38 |
$this->construireParametresRetour($typeMime, $format);
|
|
|
39 |
$this->creerStyleSources();
|
|
|
40 |
$this->sources = $sources;
|
|
|
41 |
}
|
|
|
42 |
|
|
|
43 |
private function chargerSVG($nomFichierSVG) {
|
|
|
44 |
$this->documentXML = new DOMDocument("1.0", "UTF-8");
|
|
|
45 |
$this->documentXML->load($nomFichierSVG);
|
|
|
46 |
}
|
|
|
47 |
|
|
|
48 |
private function chargerCoordonnees() {
|
|
|
49 |
$viewbox = $this->recupererNoeuds('qgisviewbox');
|
|
|
50 |
$this->coordonnees = array(
|
|
|
51 |
'xMin' => $viewbox->attributes->getNamedItem('xMin')->value,
|
|
|
52 |
'xMax' => $viewbox->attributes->getNamedItem('xMax')->value,
|
|
|
53 |
'yMin' => $viewbox->attributes->getNamedItem('yMin')->value,
|
|
|
54 |
'yMax' => $viewbox->attributes->getNamedItem('yMax')->value
|
|
|
55 |
);
|
|
|
56 |
}
|
|
|
57 |
|
|
|
58 |
private function recupererNoeuds($nomCouche) {
|
|
|
59 |
$contenuSVG = $this->documentXML->documentElement->childNodes;
|
|
|
60 |
$noeudCouche = null;
|
|
|
61 |
$index = 0;
|
|
|
62 |
while ($index < $contenuSVG->length && is_null($noeudCouche)) {
|
|
|
63 |
$id = $contenuSVG->item($index)->attributes->getNamedItem('id');
|
|
|
64 |
if ($id->value == $nomCouche) {
|
|
|
65 |
$noeudCouche = $contenuSVG->item($index);
|
|
|
66 |
}
|
|
|
67 |
$index ++;
|
|
|
68 |
}
|
|
|
69 |
$noeuds = null;
|
|
|
70 |
if (!is_null($noeudCouche)) {
|
|
|
71 |
$noeuds = $noeudCouche->firstChild;
|
|
|
72 |
}
|
|
|
73 |
return $noeuds;
|
|
|
74 |
}
|
|
|
75 |
|
|
|
76 |
private function construireParametresRetour($typeMime, $format) {
|
|
|
77 |
$viewBox = $this->documentXML->documentElement->attributes->getNamedItem('viewBox');
|
|
|
78 |
$limitesPixels = explode(' ',$viewBox->value);
|
|
|
79 |
$this->largeur = intval($limitesPixels[2]);
|
|
|
80 |
$this->hauteur = intval($limitesPixels[3]);
|
|
|
81 |
$this->typeMime = $typeMime;
|
|
|
82 |
$this->format = intval($format);
|
|
|
83 |
}
|
|
|
84 |
|
|
|
85 |
private function creerStyleSources() {
|
|
|
86 |
$couleurs = $this->recupererCouleursSources();
|
|
|
87 |
$reglesCss = array();
|
|
|
88 |
foreach ($couleurs as $codeSource => $codeCouleur) {
|
|
|
89 |
$reglesCss[] = ".{$codeSource} {\nfill:{$codeCouleur}\n}\n";
|
|
|
90 |
}
|
|
|
91 |
$texteCss = $this->documentXML->createCDATASection(implode(' ', $reglesCss));
|
|
|
92 |
$noeudStyle = new DomElement('style', '');
|
|
|
93 |
$this->documentXML->documentElement->appendChild($noeudStyle);
|
|
|
94 |
$noeudStyle->appendChild($texteCss);
|
|
|
95 |
}
|
|
|
96 |
|
|
|
97 |
private function recupererCouleursSources() {
|
|
|
98 |
$sourcesDonnees = Config::get('sourcesDonnees');
|
|
|
99 |
$codesSources = str_replace('floradata', 'cel', $sourcesDonnees).',tout';
|
|
|
100 |
$codes = explode(',', $codesSources);
|
|
|
101 |
for ($index = 0; $index < count($codes); $index ++) {
|
|
|
102 |
$codes[$index] = "'".$codes[$index]."'";
|
|
|
103 |
}
|
|
|
104 |
$codesSources = implode(',', $codes);
|
|
|
105 |
$bdd = new Bdd();
|
|
|
106 |
$requete = "SELECT code, SUBSTR(complements,9) AS couleur FROM ".Config::get('bdd_table_ontologies')." WHERE code IN ({$codesSources})";
|
|
|
107 |
$couleurs = $bdd->recupererTous($requete);
|
|
|
108 |
$listeCouleurs = array();
|
|
|
109 |
foreach ($couleurs as $couleur) {
|
|
|
110 |
$couleur['code'] = $couleur['code'] == 'cel' ? 'floradata' : $couleur['code'];
|
|
|
111 |
$listeCouleurs[$couleur['code']] = $couleur['couleur'];
|
|
|
112 |
}
|
|
|
113 |
return $listeCouleurs;
|
|
|
114 |
}
|
|
|
115 |
|
|
|
116 |
|
|
|
117 |
|
|
|
118 |
public function formaterCarte($taxon) {
|
|
|
119 |
$limitesCarte = $this->renvoyerLimitesCarte();
|
747 |
alex |
120 |
$sourceDonnees = new SourceDonnees($limitesCarte, $taxon);
|
875 |
aurelien |
121 |
// modification temporaire pour lors de la selection d'un rang au dessus de famille on ne prenne que floradata
|
873 |
aurelien |
122 |
// (probleme de performance, qui sera réglé en reremplissant la table de moissonnage)
|
874 |
aurelien |
123 |
if($taxon['rang'] >= 180) {
|
873 |
aurelien |
124 |
foreach ($this->sources as $source) {
|
|
|
125 |
$nomMethode = "recupererStations".($source == 'floradata' ? 'Floradata' : 'Moissonnage');
|
|
|
126 |
$stations = $sourceDonnees->$nomMethode($source);
|
|
|
127 |
$this->ajouterStations($stations, $source);
|
|
|
128 |
}
|
|
|
129 |
} else {
|
|
|
130 |
$stations = $sourceDonnees->recupererStationsFloradata($source);
|
727 |
alex |
131 |
$this->ajouterStations($stations, $source);
|
|
|
132 |
}
|
|
|
133 |
$this->supprimerMaillesVides();
|
|
|
134 |
}
|
|
|
135 |
|
|
|
136 |
public function renvoyerLimitesCarte() {
|
|
|
137 |
$limites = array();
|
|
|
138 |
list($limites['ouest'], $limites['sud']) = $this->convertirMetresEnPixels(
|
|
|
139 |
$this->coordonnees['xMin'], $this->coordonnees['yMin']);
|
|
|
140 |
list($limites['est'], $limites['nord']) = $this->convertirMetresEnPixels(
|
|
|
141 |
$this->coordonnees['xMax'], $this->coordonnees['yMax']);
|
|
|
142 |
return $limites;
|
|
|
143 |
}
|
|
|
144 |
|
|
|
145 |
private function convertirMetresEnPixels($x, $y) {
|
|
|
146 |
$longitude = ($x / self::ORIGINE) * 180;
|
|
|
147 |
$latitude = ($y / self::ORIGINE) * 180;
|
|
|
148 |
$latitude = 180 / M_PI * (2 * atan(exp($latitude * M_PI / 180.0)) - M_PI / 2.0);
|
|
|
149 |
return array(round($longitude, 6), round($latitude, 6));
|
|
|
150 |
}
|
|
|
151 |
|
|
|
152 |
private function ajouterStations($stations, $source) {
|
|
|
153 |
$grille = $this->recupererNoeuds('grille')->childNodes;
|
|
|
154 |
$index = 0;
|
|
|
155 |
$maille = $grille->item($index);
|
|
|
156 |
foreach ($stations as $station) {
|
747 |
alex |
157 |
if (!isset($station['lat']) || !isset($station['lng']) || !isset($station['commune'])) {
|
|
|
158 |
continue;
|
|
|
159 |
}
|
727 |
alex |
160 |
$idMaille = $maille->attributes->getNamedItem('id')->value;
|
|
|
161 |
$bbox = explode('_', substr($idMaille, 5));
|
|
|
162 |
$bbox[0] = floatval($bbox[0]);
|
|
|
163 |
$bbox[1] = floatval($bbox[1]);
|
|
|
164 |
while ($index < $grille->length && (
|
|
|
165 |
$bbox[1] > $station['lat'] || ($bbox[1] == $station['lat'] && $bbox[0] < $station['lng'])
|
|
|
166 |
)) {
|
|
|
167 |
$maille = $grille->item($index ++);
|
|
|
168 |
$idMaille = $maille->attributes->getNamedItem('id')->value;
|
|
|
169 |
$bbox = explode('_', substr($idMaille, 5));
|
|
|
170 |
$bbox[0] = floatval($bbox[0]);
|
|
|
171 |
$bbox[1] = floatval($bbox[1]);
|
|
|
172 |
}
|
|
|
173 |
if ($bbox[1] == $station['lat'] && $bbox[0] == $station['lng']) {
|
747 |
alex |
174 |
$this->ajouterCommentaire($station, $source, $maille);
|
727 |
alex |
175 |
$this->appliquerStyleMaille($source, $maille);
|
|
|
176 |
}
|
|
|
177 |
if ($index == $grille->length) {
|
|
|
178 |
break;
|
|
|
179 |
}
|
|
|
180 |
}
|
|
|
181 |
}
|
|
|
182 |
|
|
|
183 |
private function supprimerMaillesVides() {
|
|
|
184 |
$grille = $this->recupererNoeuds('grille')->childNodes;
|
|
|
185 |
$index = 0;
|
|
|
186 |
while ($index < $grille->length) {
|
|
|
187 |
if (!$grille->item($index)->hasAttribute('title')) {
|
|
|
188 |
$grille->item($index)->parentNode->removeChild($grille->item($index));
|
|
|
189 |
} else {
|
|
|
190 |
$index ++;
|
|
|
191 |
}
|
|
|
192 |
}
|
|
|
193 |
}
|
|
|
194 |
|
|
|
195 |
private function appliquerStyleMaille($source, & $maille) {
|
747 |
alex |
196 |
if ($maille->hasAttribute('class') && $maille->attributes->getNamedItem('class')->value != $source) {
|
|
|
197 |
$maille->setAttribute('class', 'tout');
|
|
|
198 |
} elseif (!$maille->hasAttribute('class')) {
|
|
|
199 |
$maille->setAttribute('class', $source);
|
|
|
200 |
}
|
727 |
alex |
201 |
}
|
|
|
202 |
|
747 |
alex |
203 |
private function ajouterCommentaire($station, $source, & $maille) {
|
|
|
204 |
$commentaires = array();
|
727 |
alex |
205 |
if ($maille->hasAttribute('title')) {
|
747 |
alex |
206 |
$commentaires = explode("; ", $maille->attributes->getNamedItem('title')->value);
|
|
|
207 |
}
|
|
|
208 |
$commentaire = ucfirst($source)." : {$station['commune']}, ";
|
|
|
209 |
if (strlen($station['date']) == 4) {
|
|
|
210 |
$commentaire .= "en {$station['date']} par {$station['auteur']}";
|
727 |
alex |
211 |
} else {
|
747 |
alex |
212 |
$date = preg_replace("/(\d{4})-(\d{2})-(\d{2})/", "$3/$2/$1", $station['date']);
|
|
|
213 |
$commentaire .= "le {$date} par {$station['auteur']}";
|
727 |
alex |
214 |
}
|
747 |
alex |
215 |
$commentaires[] = trim($commentaire);
|
|
|
216 |
$maille->setAttribute('title', implode('; ', $commentaires));
|
727 |
alex |
217 |
}
|
|
|
218 |
|
|
|
219 |
|
|
|
220 |
|
|
|
221 |
public function renvoyerCarte() {
|
|
|
222 |
$this->documentXML->documentElement->setAttribute("width", $this->format);
|
|
|
223 |
$this->documentXML->documentElement->setAttribute("height", $this->hauteur * $this->format / $this->largeur);
|
|
|
224 |
$retour = '';
|
|
|
225 |
if ($this->typeMime == self::MIME_MAP) {
|
|
|
226 |
$retour = $this->documentXML->saveHTML();
|
|
|
227 |
} else {
|
|
|
228 |
$retour = $this->convertirEnPng();
|
|
|
229 |
}
|
|
|
230 |
return $retour;
|
|
|
231 |
}
|
|
|
232 |
|
|
|
233 |
private function convertirEnPng() {
|
|
|
234 |
$this->image = imagecreatetruecolor($this->format, $this->hauteur * $this->format / $this->largeur);
|
|
|
235 |
imagefill($this->image, 0, 0, imagecolorallocate($this->image, 255, 255, 255));
|
|
|
236 |
$this->transformerLignesEnPng('departements');
|
|
|
237 |
$this->transformerPolygonesEnPng('grille');
|
|
|
238 |
|
|
|
239 |
// stocker le contenu encode de l'image generee dans une chaine de caracteres
|
|
|
240 |
ob_start();
|
|
|
241 |
imagepng($this->image);
|
|
|
242 |
$png = ob_get_contents();
|
|
|
243 |
ob_end_clean();
|
|
|
244 |
return $png;
|
|
|
245 |
}
|
|
|
246 |
|
|
|
247 |
private function transformerLignesEnPng($nomCouche) {
|
|
|
248 |
$facteur = floatval($this->format) / floatval($this->largeur);
|
|
|
249 |
$noeudCouche = $this->recupererNoeuds($nomCouche);
|
|
|
250 |
$couleurContour = $noeudCouche->attributes->getNamedItem('stroke')->value;
|
|
|
251 |
for ($index = 0; $index < $noeudCouche->childNodes->length; $index ++) {
|
|
|
252 |
$noeudLigne = $noeudCouche->childNodes->item($index);
|
|
|
253 |
for ($indexPath = 0; $indexPath < $noeudLigne->childNodes->length; $indexPath ++) {
|
|
|
254 |
$coordonneesSvg = $noeudLigne->childNodes->item($indexPath)->attributes->getNamedItem('points')->value;
|
|
|
255 |
preg_match_all('/\d+.\d/', $coordonneesSvg, $coordonnees);
|
|
|
256 |
$coordonnees = current($coordonnees);
|
|
|
257 |
foreach ($coordonnees as $indexCoord => $valeur) {
|
|
|
258 |
$coordonnees[$indexCoord] = intval(floatval($valeur) * $facteur);
|
|
|
259 |
}
|
|
|
260 |
if ($couleurContour != 'none') {
|
|
|
261 |
for ($i = 0; $i < count($coordonnees) - 2; $i += 2) {
|
|
|
262 |
imageline($this->image, $coordonnees[$i], $coordonnees[$i+1], $coordonnees[$i+2],
|
|
|
263 |
$coordonnees[$i+3], $this->allouerCouleur($couleurContour));
|
|
|
264 |
}
|
|
|
265 |
}
|
|
|
266 |
}
|
|
|
267 |
}
|
|
|
268 |
}
|
|
|
269 |
|
|
|
270 |
private function transformerPolygonesEnPng($nomCouche) {
|
|
|
271 |
$couleurs = $this->recupererCouleursSources();
|
|
|
272 |
$facteur = floatval($this->format) / floatval($this->largeur);
|
|
|
273 |
$noeudCouche = $this->recupererNoeuds($nomCouche);
|
|
|
274 |
$couleurRemplissage = $noeudCouche->attributes->getNamedItem('fill')->value;
|
|
|
275 |
$couleurContour = $noeudCouche->attributes->getNamedItem('stroke')->value;
|
|
|
276 |
for ($index = 0; $index < $noeudCouche->childNodes->length; $index ++) {
|
|
|
277 |
$noeudPolygone = $noeudCouche->childNodes->item($index);
|
|
|
278 |
$couleurPolygone = 'none';
|
|
|
279 |
if ($noeudPolygone->hasAttribute('class')) {
|
|
|
280 |
$couleurPolygone = $couleurs[$noeudPolygone->attributes->getNamedItem('class')->value];
|
|
|
281 |
}
|
|
|
282 |
for ($indexPath = 0; $indexPath < $noeudPolygone->childNodes->length; $indexPath ++) {
|
|
|
283 |
$coordonneesSvg = $noeudPolygone->childNodes->item($indexPath)->attributes->getNamedItem('points')->value;
|
|
|
284 |
preg_match_all('/\d+.\d/', $coordonneesSvg, $coordonnees);
|
|
|
285 |
$coordonnees = current($coordonnees);
|
|
|
286 |
foreach ($coordonnees as $indexCoord => $valeur) {
|
|
|
287 |
$coordonnees[$indexCoord] = intval(floatval($valeur) * $facteur);
|
|
|
288 |
}
|
|
|
289 |
if ($couleurRemplissage != 'none') {
|
|
|
290 |
imagefilledpolygon($this->image, $coordonnees, count($coordonnees) / 2, $this->allouerCouleur($couleurRemplissage));
|
|
|
291 |
}
|
|
|
292 |
if ($couleurContour != 'none') {
|
|
|
293 |
imagepolygon($this->image, $coordonnees, count($coordonnees) / 2, $this->allouerCouleur($couleurContour));
|
|
|
294 |
}
|
|
|
295 |
if ($couleurPolygone != 'none') {
|
|
|
296 |
$contourGrille = "rgba(255,255,255,".($this->format >= 300 ? 0.4 : 0).")";
|
|
|
297 |
imagefilledrectangle($this->image, $coordonnees[0], $coordonnees[1], $coordonnees[4], $coordonnees[5],
|
|
|
298 |
$this->allouerCouleur($couleurPolygone));
|
|
|
299 |
imagerectangle($this->image, $coordonnees[0], $coordonnees[1], $coordonnees[4], $coordonnees[5],
|
|
|
300 |
$this->allouerCouleur($contourGrille));
|
|
|
301 |
}
|
|
|
302 |
}
|
|
|
303 |
}
|
|
|
304 |
}
|
|
|
305 |
|
|
|
306 |
private function allouerCouleur($couleurTexte) {
|
|
|
307 |
preg_match_all('/\d+/', $couleurTexte, $valeurs);
|
|
|
308 |
$rouge = $valeurs[0][0];
|
|
|
309 |
$vert = $valeurs[0][1];
|
|
|
310 |
$bleu = $valeurs[0][2];
|
|
|
311 |
$alpha = 0;
|
|
|
312 |
if (count($valeurs[0]) > 3) {
|
|
|
313 |
$valeurAlpha = floatval($valeurs[0][3].".".$valeurs[0][4]);
|
|
|
314 |
$alpha = intval((1.0 - $valeurAlpha) * 127.0);
|
|
|
315 |
}
|
|
|
316 |
return imagecolorallocatealpha($this->image, $rouge, $vert, $bleu, $alpha);
|
|
|
317 |
}
|
|
|
318 |
|
|
|
319 |
}
|
|
|
320 |
|
|
|
321 |
?>
|