Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save bpwebs/226d9ad4f608f22ac35848248ae7a444 to your computer and use it in GitHub Desktop.
Save bpwebs/226d9ad4f608f22ac35848248ae7a444 to your computer and use it in GitHub Desktop.
Route Optimization with Google Apps Script and Google Maps
Route Optimization with Google Apps Script and Google Maps
bpwebs.com
/**
* Route Optimization with Google Apps Script and Google Maps
* Copyright (c) 2025 bpwebs.com
* Blog Post: https://www.bpwebs.com/route-optimization-with-google-apps-script-and-maps/
*/
const SHEET = "RouteData";
// Define all required cell ranges in an object for easy reference
const RANGES = {
ORIGIN: "B4",
DESTINATION: "B5",
WAYPOINTS: "B7:I7",
WAYPOINT_ORDER: "B10",
TOTAL_DISTANCE: "B11",
TOTAL_DURATION: "B12",
ROUTE_DETAILS: "B13",
MAP_URL: "B14",
};
function optimizeRoute() {
const ss = SpreadsheetApp.getActiveSpreadsheet();
const sheet = ss.getSheetByName(SHEET);
if (!sheet) {
SpreadsheetApp.getUi().alert("Sheet not found: " + SHEET);
return;
}
// Fetch and sanitize input values
const origin = (sheet.getRange(RANGES.ORIGIN).getValue() || "").trim();
const destination = (sheet.getRange(RANGES.DESTINATION).getValue() || "").trim();
const waypoints = getTrimmedRange(sheet.getRange(RANGES.WAYPOINTS).getValues()[0]);
// Validate required input fields
if (!origin || !destination) {
SpreadsheetApp.getUi().alert("Origin and Destination cannot be empty.");
return;
}
try {
// Set up Google Maps Directions API request
let directions = Maps.newDirectionFinder()
.setOrigin(origin)
.setDestination(destination)
.setOptimizeWaypoints(true)
.setMode(Maps.DirectionFinder.Mode.DRIVING);
// Add waypoints to the API request
waypoints.map(wp => wp.trim()).forEach(wp => directions.addWaypoint(wp));
const response = directions.getDirections();
if (!response.routes || !response.routes[0]) {
SpreadsheetApp.getUi().alert("No route found. Check your locations.");
return;
}
// Extract optimized order and route details from API response
const optimizedOrder = response.routes[0].waypoint_order;
const routeDetails = response.routes[0].legs;
let totalDistanceMeters = 0;
let totalDurationSeconds = 0;
let routeDetailsString = "Complete Route Details:\n===================\n";
// Process route details and calculate total distance and duration
routeDetails.forEach((leg, index) => {
routeDetailsString += `Leg ${index + 1}: from \"${leg.start_address}\" to \"${leg.end_address}\"\n`;
routeDetailsString += `Distance: ${leg.distance.text}, Duration: ${leg.duration.text}\n`;
routeDetailsString += "-----------------\n";
totalDistanceMeters += leg.distance.value;
totalDurationSeconds += leg.duration.value;
});
// Convert distance to kilometers and duration to hours and minutes
const totalDistanceKm = (totalDistanceMeters / 1000).toFixed(2) + " km";
const totalHours = Math.floor(totalDurationSeconds / 3600);
const totalMinutes = Math.floor((totalDurationSeconds % 3600) / 60);
const totalDurationFormatted = `${totalHours} hours ${totalMinutes} minutes`;
// Update Google Sheets with results
sheet.getRange(RANGES.WAYPOINT_ORDER).setValue(optimizedOrder.join(", "));
sheet.getRange(RANGES.TOTAL_DISTANCE).setValue(totalDistanceKm);
sheet.getRange(RANGES.TOTAL_DURATION).setValue(totalDurationFormatted);
sheet.getRange(RANGES.ROUTE_DETAILS).setValue(routeDetailsString);
// Generate a Google Maps link with the optimized route
const googleMapsUrl = buildGoogleMapUrl(origin, destination, waypoints, optimizedOrder);
sheet.getRange(RANGES.MAP_URL).setValue(`=HYPERLINK("${googleMapsUrl}", "Map Link")`);
} catch (error) {
SpreadsheetApp.getUi().alert("Error fetching directions: " + error.message);
}
}
// Function to remove empty values from a range
function getTrimmedRange(values) {
let lastIndex = values.length - 1;
while (lastIndex >= 0 && !values[lastIndex]) {
lastIndex--;
}
return values.slice(0, lastIndex + 1);
}
// Function to construct a Google Maps URL with ordered waypoints
function buildGoogleMapUrl(origin, destination, waypoints, waypointOrder) {
const orderedWaypoints = waypointOrder.map(index => waypoints[index]);
let url = `https://www.google.com/maps/dir/${encodeURIComponent(origin)}/`;
orderedWaypoints.forEach(point => {
url += `${encodeURIComponent(point)}/`;
});
url += `${encodeURIComponent(destination)}/`;
return url;
}
// Function to add a custom menu to the Google Sheets UI
function onOpen() {
const ui = SpreadsheetApp.getUi();
ui.createMenu("Routing Tools")
.addItem("Optimize Route", "optimizeRoute")
.addToUi();
}
@bpwebs
Copy link
Author

bpwebs commented Feb 23, 2025

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment