Géo-référence 
vendredi, 3 mars 2006, 11:04 PM - Géo-référence, PHP, MySQL
Un site web qui offre un contenu dynamique en fonction des informations personnelles des visiteurs, ce n'est pas un secret, c'est une clef vers le succès. Plus vous aurez des informations, plus vous aurez la possibilité d'offrir un service de qualité et +.

Parmi ces informations, les coordonnées terrestres de l'utilisateur peuvent s'avérer être une mine d'or. Vous pouvez les obtenir par son adresse IP, mais il vaut mieux lui demander son code postal: dans les 2 cas, des bases de données existent pour faire la relation avec la latitude / longitude. (Pour un exemple de modèle de données complet, regardez Service canadien de toponymie - Modèle de données et dictionnaire de données - format pdf).

À quoi peut bien servir de connaître l'endroit où est situé votre visiteur? Entre autres, cela vous permet d'afficher des résultats de recherche triés par proximité et/ou d'afficher du contenu relatif à sa région.

Si vous désirez connaître uniquement la distance entre le point X et le point Y, vous pouvez effectuer l'opération à l'aide d'une fonction PHP que vous trouverez facilement en cherchant sur le web. Mais si vous désirez connaître la distance entre le point X et plusieurs autres points, situer le point X dans une zone et trier le résultat, il est beaucoup plus simple pour vous et plus efficace pour votre serveur d'effectuer ces opérations avec MySQL. Je vous propose de voir quelques exemples.

Les exemples suivants vont fonctionner à partir de ces données:

<?php
  
/*
    CREATE TABLE IF NOT EXISTS `zip_sample` (
      `postal_code` VARCHAR(6) NOT NULL,
      `city` VARCHAR(30),
      `province` CHAR(2),
      `area_code` CHAR(3),
      `time_zone` DECIMAL(3,1),
      `latitude` DECIMAL(9,5),
      `longitude` DECIMAL(9,5),
      PRIMARY KEY  (`postal_code`)
    ) ENGINE=MyISAM;

    INSERT INTO `zip_sample` (`postal_code`, `city`, 
`province`, `area_code`, `time_zone`, `latitude`, `longitude`) VALUES
    ('H4G1S1', 'Verdun', 'QC', '514', 5.0, 45.46790, -73.56806),
    ('G6V1C8', 'Lévis', 'QC', '418', 5.0, 46.82299, -71.17045),
    ('H2J2H2', 'Montréal', 'QC', '514', 5.0, 45.52288, -73.57741),
    ('H2A2V2', 'Montréal', 'QC', '514', 5.0, 45.56218, -73.59733),
    ('E1A0A8', 'Moncton', 'NB', '506', 4.0, 46.10303, -64.75025),
    ('G2K1P8', 'Québec', 'QC', '418', 5.0, 46.84719, -71.29976);
  */
?>


Pour commencer, prenons d'abord la latitude et la longitude de notre visiteur (le format du code postal devrait être préalablement validé):

<?php

  $postal_code 
'H4G1S1';
  
$aLatLong = array();

  
$q mysql_query(
    
'SELECT latitude, longitude
     FROM zip_sample 
     WHERE postal_code = "'
.$postal_code.'"
     ;'
);

  list(
$aLatLong['latitude'], $aLatLong['longitude']) = 
    
mysql_fetch_row($q);

  if (!
$aLatLong['latitude'] || !$aLatLong['longitude']) {
    echo 
"Les coordonnées pour $postal_code ne sont pas disponibles.";
    exit;
  }

  
// Le array $aLatLong contient maintenant
  // la latitude et la longitude de notre visiteur
  // $aLatLong['latitude'] = 45.46790
  // $aLatLong['longitude'] = -73.56806
?>


Le code qui suit affiche la distance entre un code postal donné et tous les autres disponibles dans la table zip_sample. Les données sont triées par proximité.

<?php
  
// Pour obtenir en miles plutôt qu'en km,
  // remplacez 1.61 par 0.8684
  
$q mysql_query(
    
'SELECT postal_code,
    DEGREES(
     ACOS(
      (
       SIN(RADIANS('
.$aLatLong['latitude'].'))
       *
       SIN(RADIANS(latitude))
      )
     +
      (
       COS(RADIANS('
.$aLatLong['latitude'].'))
       *
       COS(RADIANS(latitude))
       *
       COS(RADIANS('
.$aLatLong['longitude'].' - longitude))
      )
     ) 
     *
     60 * 1.1515 * 1.61
    ) AS km_diff
    FROM zip_sample
    ORDER BY km_diff
    ;'
);

  while (
$r mysql_fetch_assoc($q)) {
    echo 
'La distance entre '.$postal_code.' et '
    
.$r['postal_code'].' est '
    
.sprintf("%01.1f"$r['km_diff']).' km'."\n";
  }

  
// Résultat:
  /*
    La distance entre H4G1S1 et H4G1S1 est 0.0 km
    La distance entre H4G1S1 et H2J2H2 est 6.2 km
    La distance entre H4G1S1 et H2A2V2 est 10.7 km
    La distance entre H4G1S1 et G2K1P8 est 232.5 km
    La distance entre H4G1S1 et G6V1C8 est 238.4 km
    La distance entre H4G1S1 et E1A0A8 est 687.3 km
  */
?>


Pour savoir si votre visiteur fait partie d'une région quelconque, ça se complique surtout par le fait qu'il faut connaître les coordonnées qui la délimitent. Si vous n'avez pas ces coordonnées, mais que vous avez une carte avec les régions dessinées à la craie, utilisez Toporama - vous pouvez cliquer sur un point précis pour obtenir ses informations (par exemple, pour C1 j'obtiens "Degrés décimaux : 45.86° N 73.228° O" que je convertis par latitude = 45.86 et longitude = -73.228).



Latitude Longitude
C1: 45.86 -73.228
C2: 45.693 -72.954
C3: 45.291 -73.481
C4: 45.352 -74.127
C5: 45.726 -73.912


Maintenant que nous connaissons les coordonnées de notre région, nous pouvons ajouter le polygone comme une donnée dans une nouvelle table. Pour ce faire, nous devons d'abord créer une table qui contiendra une colonne spatiale de type Polygon et, ensuite, nous utiliserons les fonctions géométriques de MySQL pour l'insertion des coordonnées. (Pour référence, lisez le chapitre 18 de la documentation MySQL: Spatial Extensions)

<?php
  
/*
    CREATE TABLE IF NOT EXISTS `area_sample` (
      `id` INT( 11 ) NOT NULL AUTO_INCREMENT PRIMARY KEY,
      `title` CHAR( 30 ) NULL,
      `description` VARCHAR( 255 ) NULL,
      `coord_poly` POLYGON NULL
    ) TYPE = MYISAM;

    // Les 2 parenthèses après la fonction POLYGON sont
    // nécessaires. Référez-vous à la documentation de
    // MySQL pour plus de renseignements
    INSERT INTO area_sample( title, description, coord_poly )
      VALUES (
        'Zone C',
        'Montréal et ses alentours', 
        PolygonFromText(
          'POLYGON(
            (
             45.86 -73.228,
             45.693 -72.954,
             45.291 -73.481,
             45.352 -74.127,
             45.726 -73.912,
             45.86 -73.228
            )
          )'
        )
      );
  */
?>


Nous avons enfin tout ce qu'il nous faut pour vérifier si notre visiteur fait partie de notre région:

<?php
  $q 
mysql_query(
    
'SELECT title, description 
    FROM area_sample
    WHERE
     Contains(
      coord_poly,
      GeomFromText(
       "Point('
.$aLatLong['latitude'].' '.$aLatLong['longitude'].')"
      )
     )
    ;'
);

  while (
$r mysql_fetch_assoc($q)) {
    echo 
$postal_code ' fait partie de la région '
    
$r['title'] . '(' .$r['description']. ")<br \>\n";
  }

  
// Résultat: 
  /* 
    H4G1S1 fait partie de la région Zone C (Montréal et ses alentours)
  */
?>


Nous savons maintenant comment trier des informations selon la proximité et comment trouver la région dans laquelle se situe notre visiteur. Mais ce n'était qu'un bref aperçu du potentiel des fonctions spatiales. Beaucoup d'autres possibilités s'offrent à nous, il n'en tient qu'à notre imagination!

[ ajout commentaire ]   |  [ 0 trackbacks ]   |  permalink