Last active
February 23, 2025 08:16
-
-
Save bpwebs/226d9ad4f608f22ac35848248ae7a444 to your computer and use it in GitHub Desktop.
Route Optimization with Google Apps Script and Google Maps
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Route Optimization with Google Apps Script and Google Maps | |
bpwebs.com |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* 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(); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Blog post URL: https://www.bpwebs.com/route-optimization-with-google-apps-script-and-maps/