Google Maps sobre SPIP

Un tutorial para demostrar el uso de Google Maps a través de SPIP.

Google Maps es un servicio ofrecido por Google para hacer búsquedas localizadas, encontrar una dirección o recorrido, ofreciendo mapas satelitales de todo el mundo.

Google también pensó en las muchas otras aplicaciones que se podrían hacer con mapas y, en vez de invertir mucho tiempo y dinero cifrando todo y desarrollándolas, optó por permitir que la comunidad se encargase,

Así es que Google nos propone un API en javascript para insertar sus mapas en nuestros sitios, listos para hacer lo que queramos. Con esta herramienta, sitios como «beenmapped.com» florecieron en internet.

Cuando visité este sitio, pensé que la idea de marcar la procedencia de los visitantes de un sitio sobre un mapa sería una extensión simpática de SPIP. Desgraciadamente, la forma en que SPIP administra a los visitantes no permite hacer eso fácilmente. Por el contrario, es bastante fácil indicar los mensajes de foros de esta forma.

PNG

He aquí un tutorial que explica cómo utilizar Google Maps a través de esqueleto SPIP. El código propuesto sirve para indicar la procedencia de los mensajes de foros, pero no hay ninguna razón para no aplicar esto a otra información geoespacial.

Encontrarán una demostración online aquí:
-  http://6v8.gamboni.org/googlemapw.php
y aquí:
-  http://6v8.gamboni.org/googlemapw.p...

Para los impacientes, el esqueleto está al final del articulo.

API de Google Maps: ¿Cómo funciona?

La interfaz para crear sus propios desarrollos google maps está bien documentada aquí. No obstante, para quiénes no comprenden inglés, he aquí una pequeña introducción.

Ante todo, es necesario obtener una clave para poder utilizar el servicio. Entonces comienza por ir allí:Maps API.

Diseñar el mapa

Desde una página HTML, el mapa será diseñado por el javascript de google dentro de una capa DIV vacía del documento. Es necesario entonces colocar y dimensionar esta capa en su código, por ejemplo:

<div id="map" style="width: 300px; height: 300px"></div>

A continuación se debe importar la librería proporcionada por Google utilizando la clave obtenida anteriormente:

<script src="http://maps.google.com/maps?file=api&v=1&key=su_clave" type="text/javascript"></script>

Esta librería porporciona una clase llamada GMap que se ocupa de generar los mapas. Se puede así dibujar un mapa en nuestro DIV vacío de la siguiente forma (donde «map» es el identificador del DIV en cuestión):

var map = new GMap(document.getElementById("map"));

Tal como se indica en la documentación, es mejor probar que el navegador es compatible antes, por ejemplo con el siguiente código:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
  <head>
    <title>Google Maps JavaScript API Example - simple</title>
    <script src="http://maps.google.com/maps?file=api&v=1&key=abcdefg" type="text/javascript"></script>
  </head>
  <body>
    <div id="map" style="width: 300px; height: 300px"></div>
    <script type="text/javascript">
    //<![CDATA[

    if (GBrowserIsCompatible()) {
      var map = new GMap(document.getElementById("map"));
      map.centerAndZoom(new GPoint(0, 45), 15);
    }

    //]]>
    </script>
  </body>

</html>

Añadir puntos sobre el mapa

Se pueden añadir puntos («Marcadores» o «Markers» en inglés) sobre el mapa a través del objeto GMarker que toma un GPoint como parámetro. Por ejemplo:

//se crea un punto: longitud luego latitud
var point = new GPoint(0,45);
//se crea una marca simple
var mark = new GMarker(point);
//se lo añade sobre el mapa
map.addOverlay(mark);

Existen tres funciones para insertar «bocadillos» de información sobre un marcador

  1. openInfoWindow(htmlElem) que muestra el ID de un elemento de la página y su titulo en el bocadillo
  1. openInfoWindowHtml(htmlString) que toma una cadena en html para mostrar
  1. openInfoWindowXSLT(xmlElem,xsltURI) que, personalmente, no la he explorado

Se puede así hacer un simple «buenos días» sobre el mapa con el código

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
  <head>
    <title>Google Maps JavaScript API Example - simple</title>
    <script src="http://maps.google.com/maps?file=api&v=1&key=abcdefg" type="text/javascript"></script>
  </head>
  <body>
    <div id="map" style="width: 300px; height: 300px"></div>
    <script type="text/javascript">
    //<![CDATA[

    if (GBrowserIsCompatible()) {
      var map = new GMap(document.getElementById("map"));
      map.centerAndZoom(new GPoint(0, 45), 15);
      var point = new GPoint(0,45);
      var mark = new GMarker(point);
      map.addOverlay(mark);
      mark.openWindowHtml("<b>Buenos días!</b>");
    }

    //]]>
    </script>
  </body>

</html>

En este caso, el marcador tendrá siempre el bocadillo de información a la vista. En general, se querrá más bien mostrar el bocadillo si se presiona el marcador. Para eso se agrega un «listener», de la siguiente forma

GEvent.addListener(mark, "click", function() {
                        marker.openInfoWindowHtml("<b>Buenos días</b>");
                      });

Personalmente, encuentro más sencillo el método de insertar código complejo en el bocadillo de información utilizando un elemento de la página que es ocultado por defecto y es indicado por el código javascript:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
  <head>
    <title>Google Maps JavaScript API Example - simple</title>
    <script src="http://maps.google.com/maps?file=api&v=1&key=abcdefg" type="text/javascript"></script>
  </head>
  <body>
    <div id="map" style="width: 300px; height: 300px"></div>
    <script type="text/javascript">
    //<![CDATA[

    if (GBrowserIsCompatible()) {
      var map = new GMap(document.getElementById("map"));
      map.centerAndZoom(new GPoint(0, 45), 15);
      var point = new GPoint(0,45);
      var mark = new GMarker(point);
      GEvent.addListener(mark, "click", function() {
        marker.openInfoWindow("boite");
      });
      map.addOverlay(mark);
    }

    //]]>
    </script>

    <div style="display:none;">
      <div id="boite">
       <b>Bonjour</b>
      </div>
    </div>
  </body>

</html>

Como se van a indicar muchos puntos sobre el mapa, será más sencillo llamar a una función para crear tantos marcadores como sea necesario:

function createMarker(point, id) {
   var marker = new GMarker(point);
   
   GEvent.addListener(marker, "click", function() {
      marker.openInfoWindow(document.getElementById(id));
   });
   
   return marker;
 }

Herramienta

Para este esqueleto, se deben administrar repeticiones un poco diferentes que las de {doublons} de SPIP.
Se debe introducir una función que permita almacenar los ID de los foros y recuperarlos más tarde para hacer un bucle:

function tampons($valeur, $nom, $type, $action){
  static $tampons = array();
  if ($action == 'empile') {
	$tampons["$type:$nom"][] = $valeur;
	return ' ';
  } else if ($action == 'generein'){
	if(count($tampons["$type:$nom"]))
	  return join('|',$tampons["$type:$nom"]);
  }
  return '';
}

Primera versión

El principio de este esqueleto es recuperar, gracias a un filtro, las coordenadas geográficas de los IP de los mensajes a través del sitio hostinfo y luego mostrar estos puntos sobre un mapa utilizando la API de Google Maps.

Se declaran en primer lugar filtros en mes_fonctions.php3 para recuperar la ubicación de los autores de cada mensaje (analizando su IP):

function locateip($ip) {
 
  static $location = array();

  if(!isset($location[$ip])) {
    $stream = file_get_contents("http://www.hostip.info/api/get.html?ip=$ip&position=true");
    $lines = split ("\n", $stream);
    foreach($lines as $l) {
      $prop = split(':',$l);
      $location[$ip][trim($prop[0])] = addslashes(trim($prop[1]));
    }
  }
  return $location[$ip];
}

function latitude($location) {
  return $location['Latitude'];
}

function longitude($location) {  
  return $location['Longitude'];
}

function country($location) {
  return $location['Country'];
}

function city($location) {
  return $location['City'];
}
  1. locateip consulta el sitio hostip y devuelve un cuadro de información sobre el IP en cuestión
  1. latitue, longitude, city y country nos permiten obtener la valiosa información de este cuadro.

En primer lugar se va a hacer un bucle que crea los puntos sobre el mapa utilizando la interfaz javascript de Google Maps:

<BOUCLE_gpoint(FORUMS) {par date} {0,50} {id_article?}>
  [
    var point#COMPTEUR_BOUCLE = new GPoint((#IP|locateip|longitude),
                                           [(#IP|locateip|latitude)]);
    var marker#COMPTEUR_BOUCLE = createMarker(point#COMPTEUR_BOUCLE,
                                              #ID_FORUM);
    map.addOverlay(marker#COMPTEUR_BOUCLE);
    [(#ID_FORUM|tampons{'trouve','forums','empile'})]
  ]
  [(#IP|locateip|latitude|sinon{#ID_FORUM}|tampons{'perdu','forums','empile'})]
</BOUCLE_gpoint>

El bucle generará el código para los 50 últimos mensajes del foro anotados en el sitio [1]:

  var point1 = new GPoint( 6.9167, 46.4333 );
  var marker1 = createMarker(point1,1024);
  map.addOverlay(marker1);

  var point2 = new GPoint( -73.5667, 45.5167 );
  var marker2 = createMarker(point2,1025);
  map.addOverlay(marker2);

Se crea un punto y un marcador para cada mensaje.

[(#IP|locateip|latitude)]) devuelve la latitud proporcionada por el filtro locateip. Se utiliza el filtro longitude para testear si la IP se ha localizado [2], sino, la última línea del código memoriza el identificador del foro que no se encontró en la pila.

Se hace a continuación un bucle «forums» que indica en una DIV oculta los mensajes de foros.

<B_forum>
<div style="display:none;">
<BOUCLE_forum(FORUMS) {id_forum == ^(#NOOP|tampons{'trouve','forums','generein'})$}>
<div id="#ID_FORUM"  class="map_forum">
<div id="pays">[(#IP|locateip|country)]</div>
<div id="ville">[(#IP|locateip|city)]</div>
<div id="date">#DATE</div>
<div id="auteur">#NOM</div>
<div id="texte">#TEXTE</div>
</div>
</BOUCLE_forum>
</div>
</B_forum>

Se contiene cada mensaje en un DIV que lleva el identificador del mensaje. Entonces el código javascript generado anteriormente:

createMarker(point1,1024); creará una DIV con id=1024 conteniendo el mensaje de foro 1024. Entonces, incluso si se ocultan directamente al visitante, estos mensajes se indicarán cuando se presiona el marcador correspondiente.

Observe que esta lista se limita a los mensajes encontrados tomando el identificador de foro en el vector «localizados». Se indica también el número de mensajes de foros que no se localizaron en sobre el vector «perdidos» («perdu»):

<BOUCLE_oubli(FORUMS) {id_forum == ^(#NOOP|tampons{'perdu','forums','generein'})$}></BOUCLE_oubli>
#TOTAL_BOUCLE messages n'ont pas pu étre localisés.
<//B_oubli>

aquí el esqueleto completo:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
  <head>

    <style>
    .map_forum {
      width: 400px;
      max-height: 250px;
      overflow: auto;  
      overflow-x: hidden;
    }
    </style>

    <script src="http://maps.google.com/maps?file=api&v=1&key=VOTRECLEFICI" type="text/javascript"></script>
  </head>
  <body>
    <div id="map" style="width: 800px; height: 600px"></div>
    <script type="text/javascript">
    //<![CDATA[
    
    // Creates a marker whose info window displays the given number
    function createMarker(point, id) {
      var marker = new GMarker(point);
   
      // Show this marker's index in the info window when it is clicked
      GEvent.addListener(marker, "click", function() {
                   marker.openInfoWindow(document.getElementById(id));
                        });
   
      return marker;
    }
   
   var map = new GMap(document.getElementById("map"));
   var map = new GMap(document.getElementById("map"));
   map.addControl(new GLargeMapControl());
   map.addControl(new GMapTypeControl());
   map.centerAndZoom(new GPoint(0,45), 15);

  <BOUCLE_gpoint(FORUMS) {par date} {0,50} {id_article?}>
  [
    var point#COMPTEUR_BOUCLE = new GPoint((#IP|locateip|longitude),
                                         [(#IP|locateip|latitude)]);
    var marker#COMPTEUR_BOUCLE = createMarker(point#COMPTEUR_BOUCLE, #ID_FORUM);
                                         map.addOverlay(marker#COMPTEUR_BOUCLE);
    [(#ID_FORUM|tampons{'trouve','forums','empile'})]
  ]
  [(#IP|locateip|latitude|sinon{#ID_FORUM}|tampons{'perdu','forums','empile'})]
  </BOUCLE_gpoint>
  //]]>
  </script>

  <B_forum>
  <div style="display:none;">
     <BOUCLE_forum(FORUMS) {id_forum == #NOOP|tampons{'trouve','forums','generein'}}>
     <div id="#ID_FORUM"  class="map_forum">
        <div id="pays">[(#IP|locateip|country)]</div>
        <div id="ville">[(#IP|locateip|city)]</div>
        <div id="date">#DATE</div>
        <div id="auteur">#NOM</div>
        <div id="texte">#TEXTE</div> 
     </div>
     </BOUCLE_forum>
   </div>
   </B_forum>

   <BOUCLE_oubli(FORUMS) {id_forum == ^(#NOOP|tampons{'perdu','forums','generein'})$}></BOUCLE_oubli>
   #TOTAL_BOUCLE messages n'ont pas pu étre localisés.
   <//B_oubli>
   <hr/>
   <a href="http://www.hostip.info">
     IP Address Lookup
   </a>
   <a href="http://maps.google.com">Google Map</a> 
   <strong>This site is not afiliated to Google or hostip</strong>
  </div>
</body>
</html>

Atención

Este esqueleto, va a enviar peticiones a hostip cada vez que se recalcule, para localizar el IP. Puede decirse que la caché de SPIP nos salvará, pero como se trabaja con bucles forums, la página tendrá un delay nulo por defecto.

Así se corre el riesgo de sobrecargar al servidor de hostip y por consiguiente, que nuestras peticiones sean rechazadas. Este esqueleto es «bastante» sencillo e ilustrativo, pero no sirve realmente para una aplicación real en foros.

No obstante, este enfoque puede aplicarse a otras aplicaciones que no giren en torno a foros y aprovechar las ventajas del sistema de cacheo de SPIP, para no hacer peticiones pesadas en cada recálculo.

Les aconsejo leer la próxima sección que aborda la creación de una nueva tabla para almacenar la información géoespacial, y reducir así las consultas.

Hacer un caché de ubicaciones

Como no se puede usar la caché de SPIP para limitar el número de peticiones a hostip, debemos crear una propia.

Una nueva tabla

Para eso se va a añadir una nueva tabla en la base de spip que contendrá la información de ubicación de cada IP. Esta tabla será de la forma:

iplongitudelatitudecitycountry
193.252.104.39 3.1667 50.7 Roubaix FRANCE (FR)
202.22.239.172 166.45 -22.2667 Noumea NEW CALEDONIA (NC)

Es necesario pues crear esta tabla en la base de datos mysql. Esta es la consulta a ejecutar:

CREATE TABLE `spip_ip_location` (
  `ip` varchar(16) NOT NULL default '0',
  `longitude` float default NULL,
  `latitude` float default NULL,
  `city` mediumtext,
  `country` mediumtext,
  PRIMARY KEY  (`ip`)
)

Luego se puede dar la información sobre la tabla a SPIP en el archivo mes_fonctions.php3 para poder construir bucles con ella.

global $tables_principales;
$tables_principales['ip_location']['field'] = array(
        "ip"    => "VARCHAR (16) DEFAULT '0' NOT NULL",
        "city"    => "mediumtext DEFAULT NULL NULL",
        "country"    => "mediumtext DEFAULT NULL NULL",
        "longitude"    => "FLOAT DEFAULT NULL NULL",
        "latitude"    => "FLOAT DEFAULT NULL NULL");

$tables_principales['ip_location']['key'] = array(
        "PRIMARY KEY"    => "ip");

function boucle_IP_LOCATION($id_boucle, &$boucles) {
    $boucle = &$boucles[$id_boucle];
    $id_table = $boucle->id_table;
    $boucle->from[] =  "spip_ip_location AS $id_table";
    return calculer_boucle($id_boucle, $boucles);
}

Llenando la tabla

Ahora debemos llenar la nueva tabla consultando hostip. Para ello, vamos a modificar el filtro locateip para que haga la consulta correspondiente y almacene la respuesta en la base.

function locateip($ip) {
  $valeurs = '';
  $col = '';
 
  $stream = file_get_contents("http://www.hostip.info/api/get.html?ip=$ip&position=true");
  $lines = split ("\n", $stream);
  $col .= 'ip';
  $valeurs .= "'$ip'";
  foreach($lines as $l) {
    $prop = split(':',$l);
    if(trim($prop[1])) {
      if ((trim($prop[0]) == 'Latitude') || (trim($prop[0]) == 'Longitude'))
        $new = true;
      $col .= ','.strtolower(trim($prop[0]));
      $valeurs .= ",'".htmlentities(trim($prop[1]),ENT_QUOTES)."'";
    }
  }
 
  if($new) {
    include_ecrire('inc_abstract_sql.php');
    return ' '.spip_query("REPLACE INTO spip_ip_location ($col) VALUES ($valeurs)");
  }
}

El filtro comprueba que se encontraron una longitud y una latitud para la IP y luego guarda estos valores en la base.

Los bucles

Vamos a modificar el bucle que generaba el código javascript con los puntos sobre el mapa.

<BOUCLE_gpoint(FORUMS) {par ip, auteur}>
  //#COMPTEUR_BOUCLE
  <BOUCLE_local(IP_LOCATION) {ip}>
    //#IP [lat: (#LATITUDE) [(#IP|unique)
   var point#_gpoint:COMPTEUR_BOUCLE = new GPoint( #LONGITUDE, #LATITUDE );
   var marker#_gpoint:COMPTEUR_BOUCLE = createMarker(point#_gpoint:COMPTEUR_BOUCLE,
                                                    '#IP');
    map.addOverlay(marker#_gpoint:COMPTEUR_BOUCLE);
    ][(#ID_FORUM|tampons{'trouve','forums','empile'})]]
  </BOUCLE_local>
  [//recherche (#IP|unique{recherche}) [(#IP|locateip)]]
  [(#ID_FORUM|tampons{'oubli','forums','empile'})]
 <//B_local>
</BOUCLE_gpoint>

Se hicieron varias modificaciones:

-  # ahora, IP crea los marcadores una única vez por IP, y el identificador del DIV es pues #IP y no #ID_FORUM y se utiliza (# IP|unique) para no indicar un IP que ya se trató,

  1. ya no se indica la información geoespacial con locateip. Se utiliza un bucle IP_LOCATION y las nuevas balizas introducidas con la nueva tabla para eso. Si el bucle no tiene resultados (no existe la IP en nuestra tabla), entonces sí se usa el filtro locateip para conseguir los datos necesarios

Se guardan en la base todas las entradas encontradas, pero a veces hostip no es capaz de devolver todos los datos y llena la tabla con información incompleta. Entonces se hace un bucle sobre la información incompleta para ver si la base de datos de hostip tiene nueva información:

<!-- <BOUCLE_recache(IP_LOCATION) {city = "(Unknown city)"}>[(#IP|unique) [(#IP|locateip)]] </BOUCLE_recache>-->

Finalmente un bucle de los mensajes de foro encontrados para crear los DIV de cada globito de información:

<B_forum>
<div style="display:none;">
<BOUCLE_forum(FORUMS) {id_forum == ^(#NOOP|tampons{'trouve','forums','generein'}|sinon{'.*'})$} {par ip, auteur}> 
[(#IP|unique{trouve_ouvre}|?{' ',''}) <div id="#IP"  class="map_forum">
[<div id="auteur"><:auteur:>: <strong> (#NOM)</strong></div>]
]
<BOUCLE_local3(IP_LOCATION) {ip}>
#COUNTRY:#CITY
</BOUCLE_local3>
<div id="#ID_FORUM" class="sub_map_forum">
[<div id="date">(#DATE|affdate)</div>]
[<div id="texte">(#TEXTE)</div>]
</div>
[<a href="http://www.hostip.info/api/get.html?ip=#IP&position=true"><:googlemap:verifier:></a>, 
<a href="corrigerIP.php?ip=#IP"><:googlemap:corriger:></a>
(#IP|unique{trouve_ferme}|?{' ',''})</div>]
</BOUCLE_forum>
</div>
</B_forum>

Se proporciona un vínculo para verificar los datos en hostip y otro para corregir los datos en la base si tiene necesidad. La página corrigerIP.php simplemente va borrar el registro de nuestra base y redirigirnos hacia el sitio de hostip

<?php
  include('ecrire/inc_version.php');
  include_ecrire('inc_connect.php');
  include_ecrire('inc_db_mysql.php');

  $sql = "delete from spip_ip_location where ip ='".$_GET['ip']."'";

  spip_query_db($sql);

  header("Location: http://www.hostip.info/correct.html?spip=".$_GET['ip']); /*     Redirect browser */

  exit;
?>

El esqueleto final

El esqueleto final aporta otras funcionalidades. Por ejemplo, se puede entrar un identificador de artículo en el url y sólo se indicarán los mensajes en el foro de ese artículo, como así también el texto en el idioma solicitado por el usuario.

Squelette Complet
-# décompresser,
  1. placer googlemap.php3, googlemap_fonctions.php3 et corrigerIP.php3 à la racine de votre site
  2. placer googlemap.html et googlemap.css dans votre repertoire squelettes,
  3. copier les fichiers de traductions googlemap_XX.php3 dans votre répertoire ecrire/lang/

Notas

[1o sobre el artículo especificado en el url gracias a {id_article?}

[2de ahí los corchetes que rodean al código

El sitio How to Use Google Maps EZ propone otra manera de utilizar el API Google Maps haciendo abstracción del javascript. Con esta librería, todos los pedidos de adición de marcadores, etc, se hacen directamente en código HTML.

Entonces se podría imaginar este mismo esqueleto usando sólo HTML con esta librería.

Discussion

Aucune discussion

Comentar este artículo

¿Quién es usted?
  • [Conectarse]

Para mostrar su avatar con su mensaje, guárdelo en gravatar.com (gratuit et indolore) y no olvide indicar su dirección de correo electrónico aquí.

Añada aquí su comentario

Este formulario acepta los atajos de SPIP, [->url] {{negrita}} {cursiva} <quote> <code> y el código HTML. Para crear párrafos, deje simplemente una línea vacía entre ellos.

Añadir un documento

Seguir los comentarios: RSS 2.0 | Atom

Dernière modification de cette page le 3 de agosto de 2009