carto.inc.php 9.93 KB
Newer Older
Ayolo's avatar
Ayolo committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363
<?
/*
 *  Placement de points sur une carte de France - projet ultra
 *  expérimental
 *
 *  Le but est de lier des points sur la carte
 *
 *  Probleme : on n'a pas des pixels, mais des coordonnées GPS en degrés
 *
 *  Solution :
 *
 *  - Passer les degrés en X / Y coordonnées projetées
 *  selon un systeme de projection donné (il semblerait que celui
 *  utilisé par l'IGN soit le lambert II etendu, particulièrement
 *  adapté pour la France)
 *
 *  - Déterminer un rapport (échelle) entre ces coordonnées et celles
 *  de la carte en Pixel.
 *
 *  C'est de cette 2eme étape que vient mon probleme. J'arrive
 *  aisément à déterminer les coordonnées d'une ville en tracant 2
 *  cercles, en utilisant donc la distance à vol d'oiseau de 2 villes
 *  vers la ville d'arrivée, mais je ne comprends pas pourquoi un
 *  passage en coordonnées cartésiennes ne me donne pas les "bonnes
 *  coordonnées" x et y de la ville en pixels.
 *
 *  Résolution du probleme : Une échelle trop "précise" et inadaptée
 *  était la cause principale. Après simplification de l'échelle, on
 *  obtient une approximation tout à fait intéressante. L'ancienne
 *  provoquait, suite aux nombreux calculs, une erreur trop
 *  importante.
 *
 *  Derniere remarque :
 *
 *  La calibration de l'échelle risque de changer violemment en
 *  fonction de l'image. Et de la vient 90 % du travail de cette
 *  bibliothèque.
 *
 *                                            My 2 cents - pedrov
 */
/* Copyright 2006
 * - Pierre Mauduit <pierre POINT mauduit CHEZ utbm POINT fr>
 *
 * Ce fichier fait partie du site de l'Association des Étudiants de
 * l'UTBM, http://ae.utbm.fr.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 * 02111-1307, USA.
 */

require_once ($topdir . "include/watermark.inc.php");


class carto
{
  /* une ressource GD de l'image de rendu */
  var $fond_carte;
  /* un tableau de points à lier
   * (le but premier est de fournir un trajet) */
  var $pts_link;
  /*
   * un tableau de couleurs indexé par un nom
   */
  var $color;

  /*
   * Constructeur
   */
  function carto ($link)
  {
    global $topdir;

    $this->pts_link = $link;
    $this->fond_carte = imagecreatefrompng($topdir . "images/france.png");
    $this->add_color("red", 255, 0, 0);
    $this->add_color("black", 0, 0, 0);

  }

  /*
   * utilisation du programme binaire externe proj
   *
   * voir le script /usr/share/php4/exec/proj.sh pour plus
   * d'explications. On utilise la projection Lambert II étendue
   *
   */
  function convert_gps_to_proj ($coords_deg)
  {
    /*
     * ATTENTION : l'INSEE fournit des longitudes négatives
     * pour les longitudes Ouest ...
     *
     * Il faut donc les passer en positif et indiquer qu'il s'agit
     * bien de longitudes Ouest (et non Est)
     */
    if ($coords_deg[1] < 0)
      $long = abs($coords_deg[1]) . "dW";
    else
      $long = $coords_deg[1] . "dE";

    /* ca, a priori, ca n'arrivera pas ;-)
     *
     * (pour ceux qui ne comprennent pas, jusqu'à preuve du contraire,
     * l'intégralité du territoire francais métropolitain est dans
     * l'hémisphère Nord ...)
     *
     */
    if ($coords_deg[0] < 0)
      $lat = abs($coords_deg[0]) . "dS";
    else
      $lat = $coords_deg[0] . "dN";

    $res = exec ("/usr/share/php5/exec/proj.sh $lat $long");
    $res = explode("\t", $res);
    return $res;
  }
  /*
   * Conversion coordonnées GPS -> Pixels
   *
   */
  function convert_gps_to_px ($coords)
  {
    /* echelle de la carte france.png
     * A CHANGER SI CHANGEMENT D'IMAGE !
     */

    /* a 0.5 pixels pour 1 km (px / m) */
    $echelle = 1.015 / 2000;
    /* on prendra Paris comme référence */
    /* coordonnees de Paris en degres (GPS) */
    $paris['deg'] = array ('48.866667','2.333333');
    /* coordonnees en pixels sur la carte */
    $paris['px'] = array ('280', '131');
    $paris['utm'] = $this->convert_gps_to_proj ($paris['deg']);
    $res['utm'] = $this->convert_gps_to_proj ($coords);

    $res['px'][0] = $paris['px'][0] + ($res['utm'][0] - $paris['utm'][0]) * $echelle;
    $res['px'][1] = $paris['px'][1] - ($res['utm'][1] - $paris['utm'][1]) * $echelle;

    return array(round($res['px'][0]), round($res['px'][1]));
  }

  /*
   * Calcul de la distance à "vol d'oiseau".
   *
   * retourne la distance (en m) relative entre
   * un premier point de coordonnées ($ref en degres),
   * et un point d'arrivee ($coords en degres)
   *
   *
   */
  function get_distance_from_gps ($ref, $coords)
  {
    list($x_ref, $y_ref) = $this->convert_gps_to_proj ($ref);
    list($x_crd, $y_crd) = $this->convert_gps_to_proj ($coords);

    $x = abs($x_ref - $x_crd);
    $y = abs($y_ref - $y_crd);

    $dist = sqrt (pow($x,2) + pow($y,2));
    return $dist;
  }
  /* Pixels ou autre (pas de projection) */
  function get_distance ($ref, $coords)
  {
    list($x_ref, $y_ref) = $ref;
    list($x_crd, $y_crd) = $coords;

    $x = abs($x_ref - $x_crd);
    $y = abs($y_ref - $y_crd);

    $dist = sqrt (pow($x,2) + pow($y,2));
    return $dist;
  }



  /*
   * Parsing des coordonnees du tableau
   *
   */
  function parse_links ()
  {
    $links = $this->pts_link;

    for ($i = 0; $i < count($links); $i++)
      $links[$i] = $this->convert_gps_to_px ($links[$i]);

    /* on trie les etapes dans l'ordre */
    for ($i = 1; $i < count($links) - 1; $i++)
      {
	$dist = $this->get_distance ($links[0], $links[$i]);
	$l_dists[$dist] = $links[$i];
      }

    $start = $links[0];
    $end = $links[count($links) -1];

    $links = array();
    if (is_array($l_dists))
      sort($l_dists);

    $links[0] = $start;
    foreach ($l_dists as $link)
      $links[] = $link;
    $links[] = $end;


    /* parcours deux fois de suite du meme tableau
     * afin d'eviter l'affichage des lignes sur les carres */
    /* trace de ligne */
    for ($i = 0; $i < count($links); $i++)
      {
	if ($i != 0)
	  $this->draw_line (array($links[$i - 1][0],
				  $links[$i - 1][1]),
			    array($links[$i][0],
				  $links[$i][1]));
      }
    /* tracage des pts depart / arrivee et etapes */
    for ($i = 0; $i < count ($links); $i++)
      {
	/* carre pt de depart ou  arrivee */
	if (($i == 0) || ($i == (count($links) - 1)))
	  $this->draw_circle (array($links[$i][0],
				    $links[$i][1]),
			      10);
	/* Carre d'etape */
	else
	  $this->draw_circle (array($links[$i][0],
				    $links[$i][1]),
			      5);
      }
  }

  /*
   * Ajout d'une couleur
   */
  function add_color ($name, $r, $g, $b)
  {
    $this->color[$name] = imagecolorallocate($this->fond_carte,
					     $r,
					     $g,
					     $b);
  }
  /*
   * Trace d'une ligne sur le fond de carte
   *
   */
  function draw_line ($begin, $end, $width = 4, $color = 'red')
  {
    /* les coordonnees de la ligne / rectangle
     * dependent de la position relative de $begin et $end
     */

    /* algo inspire de php.net */
    if ($width == 1)
      imageline($this->fond_carte,
		$begin[0],
		$begin[1],
		$end[0],
		$end[1],
		$this->color[$color]);

    $t = $width / 2 - 0.5;

    if ($begin[0] == $end[0] ||
	$begin[1] == $end[1])
      imagefilledrectangle($this->fond_carte,
			   round(min($x1, $x2) - $t),
			   round(min($y1, $y2) - $t),
			   round(max($x1, $x2) + $t),
			   round(max($y1, $y2) + $t),
			   $this->color[$color]);

    if ($end[0] != $begin[0])
      $k = ($end[1] - $begin[1]) / ($end[0] - $begin[0]);
    else
      $k = ($end[1] - $begin[1]) / (0.1);
    $a = $t / sqrt(1 + pow($k, 2));
    $rectangle = array(round($begin[0] - (1 + $k) * $a),
		    round($begin[1] + (1 - $k) * $a),
		    round($begin[0] - (1 - $k) * $a),
		    round($begin[1] - (1 + $k) * $a),
		    round($end[0] + (1 + $k) * $a),
		    round($end[1] - (1 - $k) * $a),
		    round($end[0] + (1 - $k) * $a),
		    round($end[1] + (1 + $k) * $a));

    imagefilledpolygon($this->fond_carte,
		       $rectangle,
		       4,
		       $this->color[$color]);
  }
  /*
   * tracé d'un cercle (cercle)
   */
  function draw_circle ($coords, $r = 4, $color = "black")
  {
    imagefilledellipse ($this->fond_carte,
			$coords[0],
			$coords[1],
			$r,
			$r,
			$this->color[$color]);
  }
  /*
   * tracé d'un point (carré)
   */
  function draw_square ($center, $width, $color = "black")
  {
    /* haut gauche */
    $topleft[0] =  $center[0] - ($width / 2);
    $topleft[1] =  $center[1] - ($width / 2);
    /* haut droit */
    $topright[0] = $center[0] + ($width / 2);
    $topright[1] = $center[1] - ($width / 2);
    /* bas droit */
    $botright[0] = $center[0] + ($width / 2);
    $botright[1] = $center[1] + ($width / 2);
    /* bas gauche */
    $botleft[0] = $center[0] - ($width / 2);
    $botleft[1] = $center[1] + ($width / 2);

    imagefilledpolygon ($this->fond_carte,
			array($topleft[0],
			      $topleft[1],
			      $topright[0],
			      $topright[1],
			      $botright[0],
			      $botright[1],
			      $botleft[0],
			      $botleft[1]),
			4,
			$this->color[$color]);
  }


  /*
   * Envoi au navigateur
   *
   */
  function output ($watermark = null)
  {
    header ("Content-Type: image/png");
    if (($watermark == null) || (! file_exists($watermark)))
      $img = new img_watermark ($this->fond_carte);
    else
      $img = new img_watermark ($this->fond_carte, $watermark);
    $img->output ();
    $img->destroy ();
364

Ayolo's avatar
Ayolo committed
365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383
  }
  /*
   * "enregistrer sous"
   */
  function saveas ($file)
  {
    @imagepng($this->fond_carte, $file);
  }
  /*
   * Liberation de la memoire
   */
  function destroy ()
  {
    @imagedestroy ($this->fond_carte);
  }
}


?>