Google Sheets nos permite hacer uso de varias funciones relacionadas al manejo de coordenadas y direcciones, esto por estar altamente integrado con Google Cloud Platform, que incluye Google Maps. Una de las cosas más interesantes que podemos hacer es crear rutas secuenciales para planear recorridos, y obtener información súper útil, como la distancia y el tiempo de cada parte del recorrido. Hoy veremos cómo hacer esto en Google Sheets, usando un poco de código de Apps Script y nuestro propio ingenio.
Preparando nuestras coordenadas
Lo primero que tenemos que hacer es tener preparadas nuestras coordenadas en una hoja llamada Direcciones. Esto puede ser en forma de direcciones físicas o de latitud y longitud, que será mucho más preciso. Sí puedes combinarlas, solo ten en cuenta que para el caso de latitud y longitud, debes usar el formato adecuado, es decir, en la misma celda deben estar ambos datos separados por una coma.
Preparando nuestra hoja de rutas y distancias
Ahora, debemos hacer una hoja nueva en el mismo documento en la que escribiremos la ruta completa en el orden de las direcciones que tenemos. Esta hoja se llamará Ruta, y en ella necesitamos crear cuatro encabezados: De, A, Distancia (km) y Tiempo (min).
Configurando nuestro proyecto de Apps Script
A partir de ahora, vamos a tener que usar un poco de código de Apps Script para comunicarnos con Google Maps y pedirle que interprete estas direcciones.
Para eso, vamos al menú Extensiones – Apps Script
Ahora, con un poco de código creamos un menú llamado Calcular ruta, que incluye a su vez el submenú ‘Calcular distancias de ruta‘, el cual ejecutará la función calcular_distancias()
. El código que te aparece por default, lo reemplazaremos por este, que hace lo que acabamos de decir:
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet_direcciones = ss.getSheetByName('Direcciones');
var sheet_ruta = ss.getSheetByName('Ruta');
function onOpen() {
var ui = SpreadsheetApp.getUi();
ui.createMenu('Calcular ruta')
.addItem('Calcular distancias de ruta', 'calcular_distancias')
.addToUi();
}
Lenguaje del código: JavaScript (javascript)
Como variables globales, estamos obteniendo primero cada una de las hojas que creamos hace unos momentos: Direcciones y Ruta, para poder manipularlas luego.
Creando la ruta entre los puntos
Ahora sí, lo que tenemos que hacer ahora es crear esta función de calcular_distancias()
para revisar las direcciones o coordenadas en la hoja actual, y adaptarlas a como la documentación de Google Maps en Apps Script nos pide que configuremos la petición. Entonces, tenemos que revisar antes, justamente cómo funciona esta petición:
Como puedes ver lo primero que hacemos es crear un objeto de tipo Maps.newDirectionFinder()
, el cual debemos configurar. Tenemos que elegir un punto de partida, opcionalmente agregar puntos intermedios, y agregar un punto final. Entonces, nuestra primera función se podría ver así, si construyéramos manualmente un objeto llamado instrucciones
usando Maps.newDirectionFinder()
function calcular_distancias() {
var instrucciones = Maps.newDirectionFinder()
.setOrigin('Plaza, Av. de la República S/N, Tabacalera, 06030 Ciudad de México, CDMX')
.addWaypoint('Paseo de la Reforma y Eje 2 PTE, Juárez, Cuauhtémoc, 06500 Ciudad de México, CDMX')
.addWaypoint('Blvd. Miguel de Cervantes Saavedra 303, Granada, 11520 Ciudad de México, CDMX')
.addWaypoint('Plaza de la Constitución S/N, Centro, Cuauhtémoc, 06000 Ciudad de México, CDMX')
.addWaypoint('19.433921,-99.1406428')
.addWaypoint('Seminario 8, Centro Histórico de la Cdad. de México, Cuauhtémoc, 06060 Ciudad de México, CDMX')
.setDestination('Centro Histórico de la Cdad. de México, Guerrero, 06300 Ciudad de México, CDMX')
.setMode(Maps.DirectionFinder.Mode.DRIVING)
.getDirections();
var ruta = instrucciones.routes[0];
}
Lenguaje del código: JavaScript (javascript)
También debemos elegir si el trayecto se realizará en automóvil, caminando, en bicicleta, etc. Para no complicarnos, vamos a asumir que el recorrido se hará en auto y dejemos esa opción como está.
Finalmente, obtenemos las direcciones y todo esto lo guardamos en nuestra variable ruta
. Lo que haremos es pedirle a Google Maps que nos devuelva las instrucciones necesarias para recorrer esta ruta, y dentro de las mismas se encontrará la información del número de kilómetros entre cada punto, así como el tiempo de recorrido.
Si ejecutamos este código así como está y usamos la función de depuración (debugger) podemos deducir que en la respuesta que nos devuelve Google Maps, está todo lo que necesitamos.
Fíjate cómo en este objeto de ruta que nos devuelve Google Maps, tenemos varias cosas interesantes. Hay un arreglo llamado legs en el que se encuentra la información de cuánta distancia y tiempo hay entre cada punto del mapa en un recorrido simulado por Google. También tenemos la latitud y longitud exacta de la dirección humana que le dimos a interpretar.
Calcular distancias entre direcciones de nuestra hoja
Lo que nos resta por hacer es integrar la información de nuestra hoja de cálculo en el código que acabamos de ver, para solamente guardar la información. Para eso, tenemos que recorrer cada fila con direcciones y construir el objeto correctamente, y eso cambia un poco el código de la función principal.
Si te fijas bien, estamos haciendo lo mismo que hace unos momentos, pero ahora tenemos que leer las celdas con las direcciones desde la hoja, y decidir cuál punto debe ser el origen, cuál debe ser el destino final, y cuáles son los puntos intermedios obligatorios por los que se debe recorrer la ruta. También ponemos algunos controles necesarios para que no tengamos errores, como exigir que la ruta tenga al menos dos puntos, y que las direcciones no estén vacías.
function calcular_distancias() {
var filaInicial = 2;
var ultimaFila = sheet_direcciones.getLastRow();
var direccionesDataRange = sheet_direcciones.getRange(filaInicial, 1,ultimaFila, 1);
var direccionesData = direccionesDataRange.getValues();
var instrucciones = Maps.newDirectionFinder();
// Necesitamos al menos dos coordenadas para que nuestro código funcione
if (direccionesData.length > 2) {
for (var i = 0; i < (direccionesData.length - 1); i++) {
var direccion = direccionesData[i][0];
// Solamente haremos algo si no están vacías las coordenadas,
// para no generar errores inesperados
if (direccion != "") {
// Si es el primer punto, ponemos el origen
if (i == 0) {
instrucciones.setOrigin(direccion);
// Si es el último punto, ponemos el destino
} else if (i == (direccionesData.length - 2)) {
instrucciones.setDestination(direccion);
// Si es un punto intermedio, lo agregamos como waypoint
} else {
instrucciones.addWaypoint(direccion);
}
}
}
} else {
SpreadsheetApp.getActive().toast('Se necesitan al menos dos direcciones o coordenadas.', '⚠️ Error');
return;
}
instrucciones.setMode(Maps.DirectionFinder.Mode.DRIVING);
instrucciones = instrucciones.getDirections();
var ruta = instrucciones.routes[0];
escribir_ruta(ruta);
}
Lenguaje del código: JavaScript (javascript)
Escribir la ruta en la hoja
Después, tenemos que escribir en las columnas siguientes los resultados que obtuvimos. Para eso haremos una función nueva llamada escribir_ruta()
, a la que le estamos pasando el objeto de ruta. En las columnas de De y A usaremos el valor correspondiente de leg.start_address
y leg.end_address
. En la columna que marcamos como distancia, ingresamos la distancia de esa parte del recorrido dividida entre 1000, porque el valor original viene en metros, y en la de tiempo, el tiempo dividido entre 60, porque viene en segundos, y lo queremos en minutos. También usamos una función adicional que nos servirá para limpiar la hoja de ruta antes de empezar una nueva:
function escribir_ruta(ruta) {
var filaInicial = 2;
// Limpiamos las celdas de la ruta actual
limpiar_ruta();
// Escribimos la información de la ruta en la hoja de Ruta
for(var i = 0; i < ruta.legs.length; i++) {
var leg = ruta.legs[i];
var distancia = (leg.distance.value) / 1000; // m a km
var tiempo = (leg.duration.value) / 60; // segundos a min
var de = leg.start_address;
var a = leg.end_address;
sheet_ruta.getRange(filaInicial + i, 1).setValue(de); // columna De
sheet_ruta.getRange(filaInicial + i, 2).setValue(a); // columna A
sheet_ruta.getRange(filaInicial + i, 3).setValue(distancia); // columna Distancia (km)
sheet_ruta.getRange(filaInicial + i, 4).setValue(tiempo); // columna Tiempo (min)
}
}
function limpiar_ruta() {
var filaInicial = 2;
var ultimaFila = sheet_ruta.getLastRow();
var rutaDataRange = sheet_ruta.getRange(filaInicial, 1,ultimaFila, 1);
var rutaSheetData = rutaDataRange.getValues();
for(var i = 0; i < rutaSheetData.length; i++) {
sheet_ruta.getRange(filaInicial + i, 1).setValue('');
sheet_ruta.getRange(filaInicial + i, 2).setValue('');
sheet_ruta.getRange(filaInicial + i, 3).setValue('');
sheet_ruta.getRange(filaInicial + i, 4).setValue('');
}
}
Lenguaje del código: PHP (php)
Si guardamos nuestro código y lo ejecutamos, después de ver las pantallas de siempre sobre los permisos y obtener nuestro menú personalizado, podremos ver que nuestra hoja de Ruta se llena con las coordenadas que le dimos y tenemos la información de distancia y tiempo de cada parte del recorrido:
Parece magia, ¿no? Esto te debe dar ideas para muchos usos prácticos reales, y puedes jugar el código para adaptarlo a lo que necesites.
Código completo para calcular rutas en Google Sheets
Como siempre, te comparto el código completo para que puedas copiar y pegar:
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet_direcciones = ss.getSheetByName('Direcciones');
var sheet_ruta = ss.getSheetByName('Ruta');
function onOpen() {
var ui = SpreadsheetApp.getUi();
ui.createMenu('Calcular ruta')
.addItem('Calcular distancia de ruta', 'calcular_distancias')
.addToUi();
}
function calcular_distancias() {
var filaInicial = 2;
var ultimaFila = sheet_direcciones.getLastRow();
var direccionesDataRange = sheet_direcciones.getRange(filaInicial, 1,ultimaFila, 1);
var direccionesData = direccionesDataRange.getValues();
var instrucciones = Maps.newDirectionFinder();
// Necesitamos al menos dos coordenadas para que nuestro código funcione
if (direccionesData.length > 2) {
for (var i = 0; i < (direccionesData.length - 1); i++) {
var direccion = direccionesData[i][0];
// Solamente haremos algo si no están vacías las coordenadas,
// para no generar errores inesperados
if (direccion != "") {
// Si es el primer punto, ponemos el origen
if (i == 0) {
instrucciones.setOrigin(direccion);
// Si es el último punto, ponemos el destino
} else if (i == (direccionesData.length - 2)) {
instrucciones.setDestination(direccion);
// Si es un punto intermedio, lo agregamos como waypoint
} else {
instrucciones.addWaypoint(direccion);
}
}
}
} else {
SpreadsheetApp.getActive().toast('Se necesitan al menos dos direcciones o coordenadas.', '⚠️ Error');
return;
}
instrucciones.setMode(Maps.DirectionFinder.Mode.DRIVING);
instrucciones = instrucciones.getDirections();
var ruta = instrucciones.routes[0];
escribir_ruta(ruta);
}
function escribir_ruta(ruta) {
var filaInicial = 2;
// Limpiamos las celdas de la ruta actual
limpiar_ruta();
// Escribimos la información de la ruta en la hoja de Ruta
for(var i = 0; i < ruta.legs.length; i++) {
var leg = ruta.legs[i];
var distancia = (leg.distance.value) / 1000; // m a km
var tiempo = (leg.duration.value) / 60; // segundos a min
var de = leg.start_address;
var a = leg.end_address;
sheet_ruta.getRange(filaInicial + i, 1).setValue(de); // columna De
sheet_ruta.getRange(filaInicial + i, 2).setValue(a); // columna A
sheet_ruta.getRange(filaInicial + i, 3).setValue(distancia); // columna Distancia (km)
sheet_ruta.getRange(filaInicial + i, 4).setValue(tiempo); // columna Tiempo (min)
}
}
function limpiar_ruta() {
var filaInicial = 2;
var ultimaFila = sheet_ruta.getLastRow();
var rutaDataRange = sheet_ruta.getRange(filaInicial, 1,ultimaFila, 1);
var rutaSheetData = rutaDataRange.getValues();
for(var i = 0; i < rutaSheetData.length; i++) {
sheet_ruta.getRange(filaInicial + i, 1).setValue('');
sheet_ruta.getRange(filaInicial + i, 2).setValue('');
sheet_ruta.getRange(filaInicial + i, 3).setValue('');
sheet_ruta.getRange(filaInicial + i, 4).setValue('');
}
}
Lenguaje del código: PHP (php)
Corroborando los datos en Google Maps
Ahora, debes cerciorarte de que la información sea correcta. Una forma muy sencilla de hacerlo, es comparar nuestro resultado en el mismo Google Maps. Si juntamos todas las direcciones y las concatenamos con la dirección base de Google Maps, fácilmente lo lograremos si usamos esta fórmula:
=HYPERLINK(ArrayFormula("https://www.google.com/maps/dir/"&CONCATENATE(IF(Direcciones!A2:A<>"",ENCODEURL(Direcciones!A2:A)&"/",""))), "Ver en mapa")
Documentación oficial y límites de uso
En la documentación oficial de Google puedes revisar más información sobre todo lo que se puede hacer con el resultado que devuelve Google Maps, ya que aquí solamente tocamos una pequeña parte de todo lo que se puede lograr.
También es importante que revises los límites diarios de uso de las peticiones de Maps, porque si sobrepasas estos límites, tal vez tengas que considerar más bien usar la API de Google Maps en vez de las funciones que provee Apps Script, que son un poco más limitadas, con la ventaja de no requerir una API Key.
¡Y listo! Ahora ya sabes cómo crear rutas con distancias y tiempos precisos en hojas de cálculo de Google Sheets y Google Maps fácilmente.
Plantilla terminada para descargar
Guía en video
Si tienes dudas puedes ver esta misma guía explicada en video:
¡Espero que te haya sido útil esta guía! No olvides seguirnos en YouTube para más tips, trucos y guías de productividad.
Alekz es un apasionado de la tecnología y los videojuegos. Además de ser locutor comercial, disfruta de la expresión artística más sastisfactoria y técnica que existe: programar (y pintar algo de pixel art). Fundó Tesel para ayudar a empoderar a las empresas a desarrollar su máximo potencial mediante la tecnología. Es un absoluto nerd de Star Trek.
Alekz es un apasionado de la tecnología y los videojuegos. Además de ser locutor comercial, disfruta de la expresión artística más sastisfactoria y técnica que existe: programar (y pintar algo de pixel art). Fundó Tesel para ayudar a empoderar a las empresas a desarrollar su máximo potencial mediante la tecnología. Es un absoluto nerd de Star Trek.
Lo probe y me tira este error:
TypeError: Cannot read property ‘legs’ of undefined
Al depurarlo me aparece esto.
TypeError: Cannot read property ‘legs’ of undefined
escribir_ruta @ CalcularDistancia.gs:60
Como lo puedo solucionar?
Hola Falco, puede ser que no haya encontrado una ruta válida entre los puntos que le diste. Ese error significa que el objeto no trae ninguna ruta.
Hola, muy útil, pero en mi caso, en la Hoja de Direcciones tengo una fórmula en la que la columna A con los puntos de inicio y en la columna B los destinos. ¿Podrías ayudarme a reformular el ejemplo?
function calcular_distancias() {
var filaInicial = 2;
var ultimaFila = sheet_ruta.getLastRow();
//getRange(row, column, optNumRows, optNumColumns)
var direccionesDataRange = sheet_ruta.getRange(filaInicial, 1,ultimaFila, 2);
var direccionesData = direccionesDataRange.getValues();
var instrucciones = Maps.newDirectionFinder();
// Necesitamos al menos dos coordenadas para que nuestro código funcione
if (direccionesData.length > 2) {
for (var i = 0; i < (direccionesData.length – 1); i++) {
var direccion = direccionesData[i][0];
// Si es el primer punto, ponemos el origen
if (i == 0) {
instrucciones.setOrigin(direccion);
// Si es el último punto, ponemos el destino
} else if (i == (direccionesData.length – 2)) {
instrucciones.setDestination(direccion);
}
}
}
instrucciones.setMode(Maps.DirectionFinder.Mode.DRIVING);
instrucciones = instrucciones.getDirections();
var ruta = instrucciones.routes[0];
Buen dia has obtenido la respuesta? yo también tengo ese mismo escenario.
¿Cómo se podría modificar para que ordene la ruta por la menor distancia entre cada punto?
Por ejemplo tengo:
las direcciones:
ABCDEFG
el punto inicial es A, pero la siguiente dirección mas cercana es la D
y el mas cercano a la D es la B y el mas cercano a la B es la C… y asi
Quedando por ejemplo
AD
DB
BC
CE
EG
GF
como se podria modificar para hacer algo asi?
Hola. Si entiendo bien tu pregunta necesitas optimizar una ruta compuesta de varios puntos con base en su distancia y hasta donde he explorado eso no es posible ya que Google Maps no tiene una opción para optimizar. Hay una parámetro llamado “optimize” pero lo que hace es buscarte la ruta con la menor distancia (o el menor tiempo, creo) entre los puntos que pongas dentro de la solicitud pero en un orden preestablecido. No está en capacidad de reordenarlos
Lo que podrías hacer es usar los resultados que te arroje esta herramienta o explorar la Distance Matrix (calcular las distancias punto a punto para un set definido) y luego utilizar OR-Tools de Google que es un paquete de optimización que puedes usar con Java, Python o C# para encontrar la solución que necesitas.
Te dejo el enlace de OR- Tools: https://developers.google.com/optimization
El parámetro «optimize» en que función la pongo? porque si necesito que me de la ruta mas corta, aunque no se a las rápida.!
Hola Gerardo,
Te comparto la sección de código que estoy implementando. En mi caso uso como las coordenadas (latitud y loingitud) de origen y destino como entrada:
var directions = Maps.newDirectionFinder()
.setOrigin(latOrigin,lngOrigin)
.setDestination(latDest,lngDest)
.setMode(Maps.DirectionFinder.Mode.DRIVING)
.setOptimizeWaypoints(true)
.getDirections();
En este enlace está la documentacion: https://developers.google.com/apps-script/reference/maps/direction-finder#setmodemode
Fe de erratas: Leyendo con mas detenimiento encuentro que mediante este método optimize y agregando waypoints (puntos intermedios entre el origen y el destino) es posible devolver la ruta mas corta secuenciando los puntos incluidos como waypoints, por lo que contrario a lo que dije arriba, si existiria posibilidad de rutear minimizando la distancia recorrida con esta herramienta
Conseguiste implementar esto finalmente? estoy buscando algo qeu me pueda ayudar a implementarlo o bien que ya lo tenga
Hola, al crear el hipervínculo para verificar en google maps me dice que existe un “error de análisis de la formula”. A que se debe o que puedo cambiar porfavor, saludos.
Hola, a mi también me ocurre lo mismo con el hipervínculo para verificar en google maps. Agradezco de antemano cualquier ayuda, saludos!
Hola! Me sucedió lo mismo y googleando el error, descubrí que era por la sintaxis de las comas (,), que en mi configuración debía ser con punto y coma (;). Eso es debido a la configuración regional y el idioma.
Es decir en varias posiciones de la fórmula que él publicó para el Hipervinculo para Ver en el mapa de Google Maps, existen tres (3) comas y las reemplacé por punto y coma.
Hola, al querer ejecutar el código me aparece el siguiente error: “exception: Service Error: Google Maps”. ¿Será que tengo que insertar alguna API key? Muchas gracias por la ayuda, saludos!
Excelente la explicación, en el código hice algunas modificaciones porque en mi caso lo que necesito es saber para cada par de direcciones es la distancia y el tiempo. Pero después de varias prubeas comenzó a damr error con la funcion getDirections() me dice que el servicio no está disponible.
Quisiera saber si a alguien le ha pasado y cual pueda ser la causa del error. Gracias anticipadas.
Hola, al incluir más de 27 puntos de localización sale error. ¿Es posible solucionar este inconveniente?
¿Lograste solucionarlo? Me serviría bastante la ayuda.
me sale el siguiente detalle, no puedo realizar
TypeError: Cannot read properties of null (reading ‘getLastRow’)
calcular_distancias @ Código.gs:14