En esta nueva entrada vamos a adentrarnos un poco más en el API de Google Maps y vamos a ver cómo calcular una ruta para que podamos extraer la información del tiempo de recorrido, indicaciones, etc. Lo primero que hay que tener claro es cómo trabaja el API de Google Maps y cómo se hacen las peticiones HTTP para poder extraer la información (en este caso en formato XML), si esto no lo tienes muy claro, te recomiendo echar un vistazo a alguna de las entradas en las que se ha tratado esto (recomiendo esta).
Como hemos visto, de lo que se trata es de hacer un petición mediante una URL y ésta nos mostrará la información en formato XML y será de ahí de donde se extraerán todos los datos. Sin más dilación, voy a mostrar un ejemplo de una petición para una ruta en coche (sin restricción en cuanto a carreteras) de Madrid a Barcelona:
https://maps.googleapis.com/maps/api/directions/xml?&origin=madrid&destination=Barcelona&mode=driving&language=es®ion=es&sensor=false
Si hacemos clic sobre el anterior enlace, vamos a ver un archivo XML con distintas partes, y de ahí extraeremos toda la información con respecto a la ruta.
![]() |
XML con la ruta |
Como podemos ver en la imagen anterior hay muchísima información disponible. En este ejemplo no vamos a utilizar toda, pero por poner un ejemplo, hay información de las polilíneas que conforman los diferentes tramos, tiempo de cada tramo, etc.
Primeramente vamos a centrarnos en los diferentes parámetros que tenemos que incluir en la URL.
PARÁMETROS
- Origin (obligatorio): indica el punto de partida de la ruta, pudiendo ser una dirección postal (Puerta del Sol, Madrid) o por latitud/longitud (40.4167522,-3.7033701). Un ejemplo de este parámetro sería, "origin=Madrid" o "origin=40.4167522,-3.7033701".
- Destination (obligatorio): indica el punto de llegada de la ruta, pudiendo ser una dirección postal (La Rambla, Barcelona) o por latitud/longitud (41.3806280,2.1736394). Un ejemplo de este parámetro sería, "origin=Barcelona" o "origin=41.3806280,2.1736394".
- Mode (opcional): indica el tipo de transporte que se va a utilizar. Este parámetro puede tomar 4 valores diferentes que son, driving (en coche), walking (a pie), bicycling (en bicicleta), transit (transporte público). Ejemplificando, vamos a indicar ruta en coche, "mode=driving".
- Avoid (opcional): este parámetro se incluye cuando se quiere aplicar restricciones a los tipos de carreteras. Si este parámetro no se incluye, se comprobarán todas las carreteras disponibles. Cuando el parámetro se incluye puede tomar dos valores, tolls (evita peajes) y highways (evita autopistas/autovías). Un ejemplo sería, "avoid=tolls". Debe recordarse que si no se quiere incluir restricciones, este parámetro se debe obviar.
- Waypoints (opcional): indica el número de hitos por los cuales se debe pasar, es decir, sitios de parada obligatoria entre el origen y el destino. La localización de estos hitos puede ser por dirección postal o por latitud/longitud, y deben estar separados por una barra vertical "|". Además, si se quiere que el orden de los hitos sea de tal forma que el recorrido sea mínimo, se puede añadir al principio de los waypoints el parámetro "optimize:true". Por ejemplo, vamos a añadir 2 hitos (sin optimización de ruta), y sería así, "waypoints=Sevilla|Valencia", y el mismo ejemplo con optimización de ruta, "waypoints=optimize:true|Sevilla|Valencia".
- Region: este es un parámetro opcional y hace que al realizar la búsqueda dé prioridad a resultados de la región seleccionada, es decir, si buscamos la ciudad de León habiendo puesto como región España, el primer resultado será de León (España), en cambio, si ponemos como región México, nos aparece como primer resultado la ciudad de León (México). Para establecer la región hay que incluir la sentencia "region=" y la región que queremos, por ejemplo para España sería, "region=es". Entra aquí para más información sobre los códigos.
- Language (idioma): es el idioma en el que se devuelven los resultados, aquí podemos ver los idiomas disponibles. Para seleccionar español la sentencia sería, "language=es".
- Sensor (obligatorio): determina si la petición procede de un dispositivo con sensor (por ejemplo un receptor GNSS (GPS) de un teléfono móvil). Se puede seleccionar entre true o false. Un ejemplo sería, "sensor=false".
Además de los mencionados, hay algún parámetro más, pero únicamente vamos a utilizar estos.
Ahora vamos a crear una aplicación en Visual Basic que nos diga qué ruta seguir entre dos puntos, y nos dé algo más de información. Lo primero creamos un proyecto nuevo (Windows Forms) y le añadimos una clase denominada Ruta, y añadimos el siguiente código.
Imports System.Xml.XPath Imports System.Reflection Public Class Ruta Public Property Status As String Public Property Copyright As String Public Property OrdenHitos As New ArrayList Public Property TiempoTotal As New ArrayList Public Property IDruta As String Public Property DistanciaTotal As New ArrayList Public Property DuracionTotal As New ArrayList Public Property PeticionHTTP As String Sub New() 'Borramos el contenido de todas las propiedades For Each Propiedad As PropertyInfo In Me.GetType.GetProperties() Propiedad = Nothing Next End Sub Public Enum TipoTransporte Coche = 0 Andando = 1 Bibicleta = 2 End Enum Public Enum RestriccionesVias Sin_restricciones = 0 Sin_Peajes = 1 Sin_Autovias_Autopistas = 2 End Enum Public Function CalcularRuta(ByVal DireccionOrigen As String, ByVal DireccionDestino As String, Optional TipoTransport As TipoTransporte = 0, Optional ByVal RestriccionesCarretera As RestriccionesVias = 0, Optional ByVal Hitos As ArrayList = Nothing, Optional ByVal optimizar As Boolean = False, Optional ByVal region As String = "es", Optional ByVal idioma As String = "es") As String() 'Tipo de transporte Dim transporte As String = TipoTransport Select Case TipoTransport Case 0 transporte = "&mode=driving" Case 1 transporte = "&mode=walking" Case 2 transporte = "&mode=bicycling" End Select 'Dirección origen (sustituimos espacio en blanco por +) DireccionOrigen = DireccionOrigen.Replace(" ", "+") DireccionOrigen = "&origin=" & DireccionOrigen 'Dirección destino (sustituimos espacio en blanco por +) DireccionDestino = DireccionDestino.Replace(" ", "+") DireccionDestino = "&destination=" & DireccionDestino 'Hitos y optimización Dim todosHitos As String = "&waypoints=" If optimizar = True Then todosHitos = "&waypoints=optimize:true|" Else todosHitos = "&waypoints=" End If 'Añadimos los hitos (en caso de haberlos) If Hitos IsNot Nothing Then For Each item As Object In Hitos item = item.ToString.Replace(" ", "+") todosHitos = todosHitos & item & "|" Next Else todosHitos = "" End If 'Restricciones en carreteeras (peajes) Dim peajesFin As String = "" Select Case RestriccionesCarretera Case 0 'No evitamos peajes peajesFin = "" Case 1 'evitar los peajes de carretera y de puentes. peajesFin = "&avoid=tolls" Case 2 'evitar las autopistas y las autovías peajesFin = "&avoid=highways" Case Else End Select 'Añadimos la región ("es" por defecto) region = "®ion=" & region 'Añadimos el idioma ("es" por defecto) idioma = "&language=" & idioma 'Creamos la url con todos los datos' Dim url = "https://maps.googleapis.com/maps/api/directions/xml?" & DireccionOrigen & DireccionDestino & todosHitos & transporte & peajesFin & idioma & region & "&sensor=false" PeticionHTTP = url 'Creamos una variable donde se almacenarán los datos de 'Latitud, Longitud, Tiempo, Distancia, Indicaciones. Dim DatosRuta As New ArrayList() 'Creamos la variable de salida (luego se redimensionará) Dim ValorSalida(0) As String 'Preparamos la peticción HTTP Dim req As System.Net.HttpWebRequest = DirectCast(System.Net.WebRequest.Create(url), System.Net.HttpWebRequest) req.Timeout = 5000 Try 'Preparamos el archivo xml Dim res As System.Net.WebResponse = req.GetResponse() Dim responseStream As Stream = res.GetResponseStream() Dim NodeIter As XPathNodeIterator Dim docNav As New XPathDocument(responseStream) Dim nav = docNav.CreateNavigator 'Creamos las variables que contendrán los paths del XML Dim Exruta, Extiempo, Exdistancia, Exindicaciones, Exlatitud, Exlongitud, Excopyrights, ExordenRuta, Exstatus, ExduracionTot, ExdistanciTot, Extiemposeg, ExPolilineas As String 'Creamos los paths Exstatus = "DirectionsResponse/status" Exlatitud = "DirectionsResponse/route/leg/step/start_location/lat" Exlongitud = "DirectionsResponse/route/leg/step/start_location/lng" Extiempo = "DirectionsResponse/route/leg/step/duration/text" Exdistancia = "DirectionsResponse/route/leg/step/distance/text" Exindicaciones = "DirectionsResponse/route/leg/step/html_instructions" Exruta = "DirectionsResponse/route/summary" Excopyrights = "DirectionsResponse/route/copyrights" ExordenRuta = "DirectionsResponse/route/waypoint_index" ExduracionTot = "DirectionsResponse/route/leg/duration/value" ExdistanciTot = "DirectionsResponse/route/leg/distance/value" Extiemposeg = "DirectionsResponse/route/leg/step/duration/value" ExPolilineas = "DirectionsResponse/route/leg/step/polyline/points" '****************************************************** 'Recorremos el XML entero para cada datos buscado (path) 'Propiedades---- 'Estatus de ruta NodeIter = nav.Select(Exstatus) While (NodeIter.MoveNext()) Status = NodeIter.Current.Value End While 'Copyright de los datos de la ruta NodeIter = nav.Select(Excopyrights) While (NodeIter.MoveNext()) Copyright = NodeIter.Current.Value End While 'Orden de los hitos (en caso de haberlos) NodeIter = nav.Select(ExordenRuta) While (NodeIter.MoveNext()) OrdenHitos.Add(NodeIter.Current.Value) End While 'ID de la ruta NodeIter = nav.Select(Exruta) While (NodeIter.MoveNext()) IDruta = NodeIter.Current.Value End While 'Duración total de la ruta (por tramos en caso de haber hitos) NodeIter = nav.Select(ExduracionTot) While (NodeIter.MoveNext()) DuracionTotal.Add(NodeIter.Current.Value) End While 'Distancia total del recorrido (por tramos en caso de haber hitos) NodeIter = nav.Select(ExdistanciTot) While (NodeIter.MoveNext()) DistanciaTotal.Add(NodeIter.Current.Value) End While '----- 'Datos que se devolverán en la función (los recorremos de una vez con un foreach---------- Dim r As New ArrayList({(Exlatitud), (Exlongitud), (Extiempo), (Exdistancia), (Exindicaciones)}) For Each item In r NodeIter = nav.Select(item) While (NodeIter.MoveNext()) DatosRuta.Add(NodeIter.Current.Value) End While Next '***************************************************** 'Variable que almacenará los datos de Latitud, Longitud, Tiempo, Distancia, Indicaciones. ReDim ValorSalida(DatosRuta.Count - 1) 'Ordenamos la variable de salida para que contenga la información de cada tramo Dim tamaño = CInt(DatosRuta.Count / 5) Dim contador As Integer = 0 For i = 0 To tamaño - 1 ValorSalida(contador) = DatosRuta(i) ValorSalida(contador + 1) = DatosRuta(i + tamaño) ValorSalida(contador + 2) = DatosRuta(i + tamaño + tamaño) ValorSalida(contador + 3) = DatosRuta(i + tamaño + tamaño + tamaño) ValorSalida(contador + 4) = DatosRuta(i + tamaño + tamaño + tamaño + tamaño) contador += 5 Next 'Cerramos responseStream.Close() Catch Status = "UNKNOWN_ERROR" End Try Return ValorSalida End Function End Class
El código es un poco largo y se podría hacer (mucho) mejor, pero se entiende bastante bien. Simplemente es una clase que agrupa una serie de propiedades y una función. El siguiente paso sería, desde el formulario principal, crear un objeto de la clase Ruta y llamar a la función CalcularRuta, y ésta nos devolverá un Arraylist con los datos de latitud1/longitud1/tiempo1/distancia1/indicaciones1/latitud2, etc. , ordenados por tramos. Una vez la función ha devuelto el valor, también se pueden comprobar el valor de las propiedades de la clase Ruta para saber, por ejemplo, el Copyright de la ruta, la distancia total recorrida, el orden de los hitos, etc.
Ahora vamos a crear un formulario para incluir los datos de la ruta en un DataGridView, y algún Textbox más con información. En este caso la opción de incluir hitos no se ha incluido.
El formulario quedaría así.
![]() |
Formulario para ruta |
Ahora, antes de poner el código fuente del formulario vamos a analizar unos pequeños detalles. Al calcular la ruta, uno de los datos que nos devuelve son las indicaciones, pero estas indicaciones tienen algunas etiquetas HTML que debemos eliminar antes de mostrarlas. Para ello, vamos a incluir una función que las quite:
Function QuitarEtiqueta(ByVal str As String) As String 'Eliminamos etiquetas HTML y ponemos en mayúsculas Dim RegExp As String = "]*>[^<]*" Dim RegExp2 As String = "]*>[^<]*" Dim R As New Regex(RegExp) Dim R2 As New Regex(RegExp2) Dim mc As MatchCollection = R.Matches(str) If mc.Count > 0 Then For Each m In mc Dim cadena = ((m.Result("$0").ToString)) str = str.Replace(cadena, cadena.ToString.ToUpper) Next End If Dim mc2 As MatchCollection = R2.Matches(str) If mc.Count > 0 Then For Each m In mc2 Dim cadena = ((m.Result("$0").ToString)) str = str.Replace(cadena, cadena.ToString.ToUpper) Next End If str = str.Replace("", "").Replace("", "").Replace("", " ").Replace("", "") Return str End Function
Ahora vamos a ver el código fuente del formulario.
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click DataGridView1.Rows.Clear() 'Creamos un objeto de la clase ruta Dim objRuta As New Ruta 'Variable que almacenará los datos de la ruta Dim DatosRuta() As String 'Llamamos a la función para que nos devuelva los datos DatosRuta = objRuta.CalcularRuta(txtOrigen.Text, txtDestino.Text, comboTransporte.SelectedIndex, comboRestricciones.SelectedIndex) 'Propiedadad para saber es estatus de la petición txtStatus.Text = objRuta.Status 'Calculamos el tiempo total de la ruta (en segundos) Dim tiempoTotal As Integer = 0 For Each item In objRuta.DuracionTotal tiempoTotal += item Next 'Pasamos el tiempo a días, horas, minutos y segundos Dim t2 As TimeSpan = TimeSpan.FromSeconds(tiempoTotal) Dim TiempoT = t2.Days.ToString() & " días, " & t2.Hours.ToString() & " horas, " & t2.Minutes.ToString() & " minutos, " & t2.Seconds.ToString() & " segundos " txtTiempoTotal.Text = TiempoT 'Calculamos la distancia total Dim DistanciaTotal As Integer = 0 For Each item In objRuta.DistanciaTotal DistanciaTotal += item Next txtDistanciaTotal.Text = DistanciaTotal / 1000 & " km" 'Valor de la petición HTTP txtURL.Text = objRuta.PeticionHTTP 'Copyright e ID de la ruta lblCopyright.Text = objRuta.Copyright lblIDruta.Text = objRuta.IDruta 'Rellenamos el Datagridview con los datos de la ruta Dim contadorT = 0 For i = 0 To UBound(DatosRuta) - 1 Step 5 DataGridView1.Rows.Add(DatosRuta(i + 2), DatosRuta(i + 3), QuitarEtiqueta(DatosRuta(i + 4)), DatosRuta(i), DatosRuta(i + 1)) Next End Sub Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load comboRestricciones.SelectedIndex = 0 comboTransporte.SelectedIndex = 0 End Sub
Lo único que hay que tener en cuenta es que algunas propiedades, como por ejemplo DistanciaTotal, es un arraylist porque si se añaden hitos a la ruta (no es el caso de este ejemplo), devolverá la distancia total por cada tramo entre punto de partida/salida con los hitos.
Como hemos visto, el proceso para adquirir la información es muy simple, sólo hay que hacer la URL para obtener el archivo XML, y a partir de ahí, es cuestión de adquirir lo que nos interese y mostrarlo. En nuestro caso el resultado sería así:
![]() |
Resultado de ruta |
Más información sobre rutas: https://developers.google.com/maps/documentation/webservices/?hl=es
Puedes descargar el código fuente aquí:
Excelente trabajo !
ResponderEliminarhola, me a encantado tu trabajo, pero en este punto del calculo de ruta, como podemos graficarlo en el mapa utilizando el webbrowser al mas puro estilo de la web de google donde aparece una linea desde el punto al punto b.
ResponderEliminargracias.
Lo que se puede hacer es dibujarlo en un mapa estático a partir de la información que nos devuelve la ruta, en concreto, con el campo que se llama polilínea.
EliminarUn saludo
Buen trabajo. ¿Sabes si es posible con API de Google saber cada tramo de ruta por qué carretera va y de que tipo es, si es de peaje, autovía, etc?
ResponderEliminarMuchas gracias
En principio eso no se puede saber.
EliminarEcha un vistazo al enlace de esta ruta (https://maps.googleapis.com/maps/api/directions/xml?&origin=madrid&destination=Barcelona&mode=driving&language=es®ion=es&sensor=false) y ahí te muestra toda la información disponible.
También puedes revisar la documentación (en castellano): https://developers.google.com/maps/documentation/webservices/?hl=es
Se podria agregar mas de un Origen Y destino, estoy trabajando en un proyecto que es de como corren los trenes y en base al tiempo de salida y transito saber donde se interceptan para una mejor administración de los encuentros y si se pueden plasmar en trazado en un mapa sea web o no,
ResponderEliminarSe podria agregar mas de un Origen Y destino, estoy trabajando en un proyecto que es de como corren los trenes y en base al tiempo de salida y transito saber donde se interceptan para una mejor administración de los encuentros y si se pueden plasmar en trazado en un mapa sea web o no,
ResponderEliminarExcelente proyecto, muchas gracias por compartir Saludos
ResponderEliminarExcelente proyecto, como puedo descargar el archivo de ejemplo?
ResponderEliminarGracias por el aporte, probé el proyecto pero me mando en el text de estatus "UNKNOWN_ERROR" ya probe por aparte la URL y esta correcta¡¡
ResponderEliminarSabras si existe un error ya que no lo hacia y no le he cambiado nada al modulo de calculo apartir del XML
Hola gracias por el aporte, esta excelente tu programa, estoy intentado hacer un programa para una linea de taxis, pero aun tengo problemas, que posibilidad hay de que me des una pequeña asesoria :)
ResponderEliminarSaludos,
ResponderEliminarUna pregunta: Estoy empleando el código para una aplicación, en relación con el tráfico, he colocado el tiempo de inicio en la URL (departure_time), pero al calcular la ruta me arroja siempre el mismo tiempo y he comparado con google maps, pero los resultados son diferentes en cuanto al tiempo total de la ruta. No si tiene en cuenta el trafico a la hora indicada y fecha o se debe contar con alguna Key (clave) en especifica para acceder a los servicios en relación con el trafico.
Ejemplo:
Resultados por el programa:
Url: https://maps.googleapis.com/maps/api/directions/xml?&origin=2.437713,-76.604398&destination=2.442145,-76.602778&departure_time=1514829600&mode=driving&language=es®ion=es&sensor=false
Resultado: Tiempo total de ruta: 2 minutos, 13 segundos
Resultados por google maps:
Datos iniciales:
1. Depart at: 6:00 PM 1 de Enero de 2018
2. Origen: Cra. 3 #7-2, Popayán, Cauca(2.437713,-76.60439)
3. Destino: Cra. 3 #3-1, Popayán, Cauca (2.442145,-76.602778)
Resultados: Tiempo total de ruta: 4 min.
Muchas gracias,
Muy buen aporte.
El enlace que se encuentra en el Blog, para mas información de rutas, dice que no encuentra la pagina que busca.
EliminarEnlace: Más información sobre rutas: https://developers.google.com/maps/documentation/webservices/?hl=es
HOLA BUENAS MUY BUEN APORTE PERO TENGO UN PROBLEMA NO ME CALCULA LA DISTANCIA TOTAL DE LA RUTA
ResponderEliminarme sale mucho este problemas OVER_QUERY_LIMIT
ResponderEliminarFelicidades por tu excelente trabajo,
ResponderEliminarMe gustaria si fueses tan amable que me aclararas exactamente como dibujar una ruta optimizada en google maps si es posible en vba, el ejemplo que estoy usando es este
https://maps.googleapis.com/maps/api/directions/json?origin=Madrid&destination=madrid&waypoints=optimize:true|sevilla|zaragoza|cordoba&key
si no es posible me gustaria saber extraer el valor de "waypoint_order" : [ 0, 2, 1 ] para hacer un hipervinculo con https://www.google.es/maps/dir/Madrid/Barcelona/poniendo el orden optimo de los waypoints
Muchas gracias
Buenos dias, muy buen trabajo.
ResponderEliminarConsulta, al intentar calcular, no me calcula y me tira UNKNOWN_ERROR.
Podrias ayudarme?
Desde ya muchas gracias