Dibujando rutas en Google Maps

Dibujando rutas

Puedes consultar las partes anteriores del manual de Google Maps Api 3:
Primera parte del manual

Segunda parte del manual

Tercera parte del manual

Este servicio es interesante, a demás podemos usar coordenadas o direcciones directamente, ya que el servicio las traducirá por sí solo.
Las clases principales son: google.maps.DirectionsService que es la que ejecuta la peticion del servicio al servidor y nos devuelve los datos, y google.maps.DirectionsRenderer que es la encargada de mostrar la ruta en nuestro mapa.
El ejempo básico de inicialización es el siguiente:

directionsService = new google.maps.DirectionsService();
directionsDisplay = new google.maps.DirectionsRenderer();

directionsDisplay.setMap(map);

La llamada a route tiene el formato “directionsService.route(request, function(result, status)”. Hay que generar un objeto request primero(DirectionRequest) que solo es necesario rellenar los campos que no sinteresan. Despues la funcion de callback es la que a partir del status tratamos los reultados.

var request = {
  origin:start,
  destination:end,
  travelMode: google.maps.DirectionsTravelMode.DRIVING
};

La llamada y el tratamiento del objeto result. Comprobamos que el status es OK, si no no hay nada que hacer, salvo mostrar el tipo de error.

directionsService.route(request, function(result, status) {
  if (status == google.maps.DirectionsStatus.OK) {
    directionsDisplay.setMap(map);
    var mystr="";
    for(var i in result.routes){
      var mylegs=result.routes[i].legs
      for(var j in mylegs){
        mystr += mylegs[j].distance.text;
        mystr += " " + mylegs[j].duration.text+ " <br/>";
      }
      mystr+="<br/>";
      $(“#infoRecorrido).html(mystr);
    }
    directionsDisplay.setDirections(result);
  }
});

Lo que realmente dibuja el mapa es ‘directionsDisplay.setDirections(result)’ ¿y todo lo demas? pues bien, google nos envia un objeto con bastante información. Los margenes en que se visualizará el mapa, las coordenadas de los puntos de incio y fin, y otras cosas entre ellas la distancia y tiempo de recorrido.
La distancia se encuentra en “result.routes[0..n-1].legs[0..n-1].duration”. Se puede visualizar en texto en tiempo ya tranformado en horas:minutos:segundos con ‘.text’ o directamente en el numero de segundos con ‘.value’.
La respuesta contiene bastante información y se le puede sacar mucho partido (ver: http://code.google.com/intl/es/apis/maps/documentation/directions/ )

bounds = new google.maps.LatLngBounds();
for(var l in markers){
  bounds.extend( markers[l].getPosition() );
}
map.fitBounds(bounds);

visto en: http://blog.shamess.info/2009/09/29/zoom-to-fit-all-markers-on-google-maps-api-v3/

Finalmente y por experiencia propia recomiendo re posicionar los margenes con cierto retardo con una función como esta:

function rebounds(){
  setTimeout(recReBounds,250,(6));
}

function recReBounds(veces)
  if(!veces){
    return false;
  }
  try{
    bounds = new google.maps.LatLngBounds();
    for(var l in markers){
    bounds.extend( markers[l].getPosition() );
  }
  map.fitBounds(bounds);
  }
  catch(e){
    setTimeout(recReBounds,250,(veces-1));
  }
}

rebounds();

A veces la velocidad del navegador que esté utilizando el cliente no carga las imágenes del mapa con mucha velocidad. Si ejecutamos el reajuste de margenes una vez recogidos los datos puede que se reajusten los margenes antes de pintar el mapa, con lo cual el reajuste no tiene efecto. Con un retardo de un cuarto de segundo el usuario siente interacción de la web y el reajuste tiene efecto.

Dibujar marcas en un mapa

Trabajando con los markers

Puedes consultar las partes anteriores del manual de Google Maps Api 3

primera parte del manual

segunda parte del manual

Para empezar vamos a trabajar con un vector de marcas que va a ser una variable global markers. Tan sencillo como:

var markers={};

setMarker hace algo más que posicionar un marker en el mapa. Seleciona el campo donde tenemos la localidad (o direccion), esto lo hace con la funcion dollar de jquery. Esta direccion la traduce a coordenadas LatLng con el servicio geocoder.
Despues invoca al método marker que es el que realmente inserta el marcador en el punto correcto del mapa.

function setMarker(id,addid){
  var dir = $("#"+id).val();
  geocoder.geocode( { 'address': dir}, function(results, status) {
    if (status == google.maps.GeocoderStatus.OK) {
      marker(id, results[0].geometry.location);
    } else {
      marker(id,null);
    }
  });
}

Marker en realidad es una funcion muy sencilla. Si existe el marcador añade una nueva posición, si no existe lo crea indicandole el mapa y la posicion donde irá.
La unica complejidad en cuanto a configuracion del maker es indicarle que no sea arrastrable (draggable) ni que se pueda pinchar (clickable).

function marker(id, location){
  if(markers[id]){
    markers[id].setPosition(location);
  }
  else{
    markers[id] = new google.maps.Marker({
      map: map
      ,position: location
      ,markerOptions:{
        draggable:false
        ,clickable:false
      }
    });
  }

  map.setZoom(10);
}

Otras opciones posibles son hacer zoom y centrar el mapa en el marcador (en este caso está comentado) pero son opciones del mapa, no del marcador.

Borrar un marker

Para borrar un marker es tan sencillo como añadirle una posicion nula. Como en nuestro caso tenemos un objeto global donde están todos nuestros markers nos podemos hacer las funciones hideMarker y hideAllMarkers:

function hideAllMarkers(){
  for(var m in markers){
    markers[m].setPosition(null);
  }
}

function hideMarker(id){
  if(id && id != "" && markers[id]){
    markers[id].setPosition(null);
    return true;
  }
  return false;
}

*Cuidado si se usan otras propiedades de los markers como el texto que despliegan al clickarlos. Si volvemos a ponerle una posicion al marker esas propiedaden persisten. Suponemos que en esta aplicacion no añadimos informacion extra y que los markers no son clickables. En otro caso para más seguridad hay que hacer un new cada vez para chafar las propiedades anteriores, o cambiarlas una por una.

Introducción a Google Maps Api 3

Google Maps api3 con ejemplos

Esta es la primera parte de un manual sobre el Api 3 de google maps. Simplemente es una primera guia para cómo, a partir de pocas lineas de código, poder implementar una aplicación interesante en tu web.

Aportar información geolocalizada está hoy a la orden del día, yo voy a basarme en los ejemplos que proporciona google añadiendo variaciones que amplian su funcionamiento. Para hacerlo de forma más comoda voy a utilizar jQuery 1.4.2 en algunos casos.

Ejemplo ligeramente modificado con jquery:
-muestra un mapa. se puede insertar longitud y latitud numerica y actualizar el mapa. Despues una lista de las propiedades de google.maps

<html>
<head>
<meta name="viewport" content="initial-scale=1.0, user-scalable=no" />
<script type="text/javascript" src="http://maps.google.com/maps/api/js?sensor=false"></script>
<script type="text/javascript" src="./jquery.1.4.2.js"></script>
<script type="text/javascript">
var myOptions, latlng, map;

function initialize() {
  latlng = new google.maps.LatLng(-34.397, 150.644);
  myOptions = {
    zoom: 8,
    center: latlng,
    mapTypeId: google.maps.MapTypeId.ROADMAP
  };
  map = new google.maps.Map(document.getElementById("map_canvas"), myOptions);
}

$(document).ready(function(){
  $("#buttonver").click(function(){
    var latVar,lonVar;
    latVar = $("#lat").val();
    lonVar = $("#lon").val();
    latlng = new google.maps.LatLng(latVar, lonVar);
    myOptions.center=latlng;
    map = new google.maps.Map(document.getElementById("map_canvas"), myOptions);
  });
});
</script>
</head>
<body onload="initialize()">
<div id="map_canvas" style="width:400px; height:400px"></div>
<label for="lat">latitud</label><input type="text" id="lat" name="lat" value="38.5"/>
<label for="lon">longitud</label><input type="text" id="lon" name="lon" value="-0.5"/>
<input type="button" id="buttonver" value="ver" />
</body>
</html>

Añadimos evento cuando haga click para que muestre la longitud y latitud:
*MEJORA: no hacemos un new Map cuando cambiamos las coordenadas. Usamos el método map.setCenter(latlng)

<html>
<head>
<meta name="viewport" content="initial-scale=1.0, user-scalable=no" />
<script type="text/javascript" src="http://maps.google.com/maps/api/js?sensor=false"></script>
<script type="text/javascript" src="./jquery.1.4.2.js"></script>
<script type="text/javascript">
var myOptions, latlng, map;

function initialize() {
  latlng = new google.maps.LatLng(-34.397, 150.644);
  myOptions = {
    zoom: 8,
    center: latlng,
    mapTypeId: google.maps.MapTypeId.ROADMAP
  };
  map = new google.maps.Map(document.getElementById("map_canvas"), myOptions);
  google.maps.event.addListener(map, 'click', function(event) {
    alert(event.latLng);
  });
}

$(document).ready(function(){
  $("#buttonver").click(function(){
    var latVar,lonVar;
    latVar = $("#lat").val();
    lonVar = $("#lon").val();
    latlng = new google.maps.LatLng(latVar, lonVar);
    myOptions.center=latlng;
    map.setCenter(latlng);
  });
});
</script>
</head>
<body onload="initialize()">
<div id="map_canvas" style="width:400px; height:400px"></div>
<label for="lat">latitud</label><input type="text" id="lat" name="lat" value="38.5"/>
<label for="lon">longitud</label><input type="text" id="lon" name="lon" value="-0.5"/>
<input type="button" id="buttonver" value="ver" />
</body>
</html>

Reseña sobre los eventos posibles:

– ‘click’
– ‘dblclick’
– ‘mouseup’ -> no me va
– ‘mousedown’ -> no me va
– ‘mouseover’
– ’mouseout’

En estos eventos se les pasa un event a la funcion calback que contiene una variable latLng.

– ‘zoom_change’ -> el callback no recibe parámetros
– ‘bounds_changed’
– ‘center_changed’

Enlaces de referencia:

-Tutorial:
http://code.google.com/intl/es/apis/maps/documentation/javascript/tutorial.html

-Referencia:
http://code.google.com/intl/es/apis/maps/documentation/javascript/reference.html

-Ejemplos:
http://code.google.com/intl/es/apis/maps/documentation/javascript/examples/

La funcion Dolar

Si llevas tocando JavaScript poco tiempo quizá aun no la hayas usado, pero por poco que lleves usando algun framework (biblioteca/libreria) seguro que te suena.
La función $ viene siendo de las más usadas o ampliamente implementadas en cualquier libreria JS para el manejo del DOM.

Que hay del DOM

DOM son las siglas de Document Object Model. La página html que estás viendo se estructura en el navegador como un árbol, en el que cada uno de los nodos del árbol es un objeto. El hecho de que se el navegador controle la página como un grupo de nodos Objeto beneficia al programador que quiere trabajar con esos objetos. Tratando cada elemento como un objeto le podemos asignar propiedades, eliminarlo o crear nuevos dinámicamente.

Cuando modificas un objeto visible del DOM los cambios se ven reflejados en el navegador. Si quieres ver más cosas sobre el DOM puedes ver la gran documentación de Mozilla de cómo Gecko (Motor de renderizado del a fundación Mozilla) implementa el DOM (DOM Gecko). Si cuando hayas terminado con esto te apetece profundizar más y tienes la posibilidad de poner el ‘Modo Dios’ de lectura puedes pelearte con la docu del w3c, que son los que dicen como tiene que ser el DOM (w3c DOM).

Tocando los nodos del DOM

Visto lo visto, si queremos que la página cambie sin recargar nos va a tocar modificar los elementos del DOM en el lado del cliente. Para acceder a un elemento que queremos modificar tenemos que acceder a el como a un objeto que es. Todos los objetos tienen un padre, y el padre de todos (la raíz del árbol) es el documento en si, es decir, el objeto document.

Para acceder al árbol existen distintas funciones. Estas nos permiten acceder al primer hijo, al último o a la lista de hijos y seleccionar el que queremos mediante corchetes:

result = document.firstChild.childNodes.length;//2
result = document.firstChild.childNodes[0];// [object HTMLHeadElement]
result = document.firstChild.childNodes[1];// [object HTMLBodyElement]
result = document.firstChild.childNodes[1].childNodes[n];//[object HTMLDivElement]

Acceder así a los elementos es bastante arido, por eso hay otras funciones que nos acortan el trabajo

Funciones que devuelven un HTMLCollection.

La definicion en IDL es:
interface HTMLCollection {
readonly attribute unsigned long length;
Node item(in unsigned long index);
Node namedItem(in DOMString name);
};

result = document.getElementsByTagName("div");//[object HTMLCollection]
result = document.getElementsByName("nombre");//[object HTMLCollection]
result = document.getElementsByClassName("nombreDeLaClase");//[object HTMLCollection]

Seleccionar por identificador a veces es más comodo porque el id debe de ser único

result = document.getElementById("identificador");// [object HTMLDivElement] porque hemos dado ese id a un div

Si tenemos más de un elemento con el mismo identificador (cosa que no debe ocurrir) hay que tener cuidado. La funcion ‘getElementById’ devuelve un solo Element (de ahi su nombre en singular) así que nos devolverá la primera ocurrencia solamente.

Los corchetes[] molan

La definición en del objeto HTMLColection nos da una interfaz para seleccionar un elemento concreto ya sea por nombre o por identificador, pero los corchetes facilitan mucho la vida del programador. Se puede invocar por nombre, identificador o numero de orden del elemento de la colección que queramos:

var result = document.getElementsByClassName("nombreDeLaClase");
result.namedItem("nombre") === result["nombre"];// true
result.item("identificador") === result["identificador"];// true
result.item(0) === result[0];// true
result.length;// 1

Para acceder a un elemento que esté en la página usamos a la estrella de las funciones de manejo del DOM:

var miElemento = document.getElementById("identificador");

Así tenemos en la variable ‘miElemento’ el elemento/objeto del DOM con id «identificador». No tenemos que sabernos la ruta completa.

Y una vez seleccionado el elemento que queramos podemos acceder a su contenido en formato HTML. Con innerHTML podemos leer y escribir su contenido directamente. Este método no es un estándar de la especificación del lenguaje ECMAScript, pero todos los navegadores lo implementan. Lo inventó microsoft para una de sus primeras versiones del explorer y al extenderse su uso por comodidad, todos los browsers lo impementaron. Tan sencillo como:

result = document.getElementById("identificador").innerHTML;//a

Con todo esto manejamos el DOM a nuestro gusto, pero ¿me estoy saliendo del tema? no. Como hemos visto se puede acceder de distintas formas a los elementos de nuestra página. Por nombre, id, clase o posición en el árbol, pero escribir estas funciones con un nombre tan largo, con nombres tan parecidos para hacer lo mismo y obtener una tipo de objeto un tanto extraño no es muy cómodo para desarrollar.
La función dolar tiene que ver mucho con el manejo del DOM, y es que esta función surge a raíz de esta problemática.

PrototypeJS

En Prototype la funcion $ es la forma acortada de ‘document.getElementById’. En realidad tiene algo más de potencia ya que si le pasamos varios argumentos nos devolverá un vector con esos elementos seleccionados.
Así que devuelve o un elemento o un array con elementos. Ojo, si los elementos no existen en el árbol nos devolverá un array vacio.

Para seleccionar elementos del DOM Prototype viene con el metodo $F que viene a hacer lo mismo que $ pero dirigido a formularios. Como ellos dicen “es un alias comodo de Form.Element.getValue”.

Pero la funcion que realmente aporta ahorro al desarrollador es la funcion doble dolar $$(). El parámetro pasado a esta funcion debe ser un string que contenga el elemento o elementos a seleccionar, pero aqui viene lo bueno, acepta sintaxis CSS. Es decir que si quemos selecciona un elemento que tenga class con valor “miElemento” introducirmos : $$(“.miElemento”). Hasta aquí poca novedad, pero y si queremos seleccionear los elementos de una lista que tnegan class “miElemento”, pues la llamada será así: $$(“li.miElemento”) y así con todo tipo de selectores css http://www.w3.org/TR/css3-selectors/
A diferencia de $, $$ devolverá siempre un vector de Element.

MooTools

En Mootoos encontramos algo parecido en cuanto a funciones dolar se refiere. La funcion dolar está orientada a identificadores y encontramos los selectores CSS en la doble dolar. Contiene los selectores CSS3.
El selector CSS de MooTools se llama Slick. Lo sacaron para la version 1.3 de la librería y mejora en un 50% el selector de la version 1.2. A demás Slick es más rapido que el selector CSS de Prototype en un porcentaje variable pero en todos los navegadores.
Tambien le podemos pasár a la funcion $$ uno o más objetos Element, y devolverá una instancia de Element que los contenga.
Como vemos Dollars de MooTools añade algo más de funcionalidad que el Dollars de Prototype. En MooTools obtendremos de $$ una instancia del objeto Element de MooTools (como en Prototype) pero la mejora está en que acepta no solo cadenas con selectores si no también elementos propios.

jQuery

jQuery es una libreria orientada al DOM, de hecho la funcion $ es un alias de la funcion jQuery. La funcion $ nos devolverá un objeto jQuery que es la que contiene toda las funciones de la librería. Es la forma de trabajar en jQuery, al objeto que tenemos le hacemos lo que queremos.
Pero a la hora de pasarle parámetros, el $ de jQuery es el más versatil.
Acepta selectores CSS, elementos del DOM o arrays de elementos. A demás con los selectores podemos indicarle un contexto en el que seleccionarlo, si es que sabemos donde está lo que buscamos y no queremos que nos devuelva partes que estén más allá de ese ambito.
El selector jQuery es el más rápido de las tres librerías. Pero no solo de selectores vive el hombre. $ de jQuery acepta codigo HTML a pelo. ¿Y para qué? para generarlo dinamicamente. Esto nos permitirá crear partes de la pagina al vuelo para insertarlas donde queramos.
Por si fuera poco $ en jQuery también es un alias del metodo “$(document).ready” (que es como asignar un metodo en “document.onload”). Así que si le pasamos como parámetro una funcion, esta será ejecutada tras la carga completa del documento.
Sin duda es la función Dollar más versátil. Esta librería está orientada a usar el $ y a usar el DOM, de esta manera ahorra muchas lineas de código a los programadores que solo utilicen JavaScript para manejar el DOM, que son la mayoría, por eso está teniendo tanto exito.

Está claro que el $ no da la felicidad, por eso cada cual se lo implementa a su manera. Es el nombre de función más representativo que existe, corto y poco común. Por corto le proporciona ahorro de tiempo en la programación, y lo poco común le proporciona ahorro de tiempo en la lectura del código.
Como en definitiva para una gran proporcion de programadores vienen usando JavaScript para manejar el DOM sin incluir mucha más logica en las aplicaciones JS, el seleccionar elementos (sea estilo CSS o no) el mejor alias para un selector es el caracter Dolar.
Sinceramente, aunque sea para acortar la funcion “document.getElementById” ya es útil. Si como hemos visto le añadimos algunas funcionalidades extra mejor que mejor.