From 7322d53fa640b0b3a9509fd11e8157579085d9d0 Mon Sep 17 00:00:00 2001 From: Stefano Fiorini Date: Tue, 10 Mar 2026 00:51:35 -0500 Subject: [PATCH] Add Google Maps route avoidance flags --- docs/google-maps.md | 10 ++++++++++ integrations/google-maps/traffic.js | 30 +++++++++++++++++++++++++---- 2 files changed, 36 insertions(+), 4 deletions(-) diff --git a/docs/google-maps.md b/docs/google-maps.md index 58d6930..9d90953 100644 --- a/docs/google-maps.md +++ b/docs/google-maps.md @@ -32,5 +32,15 @@ Required Google APIs for the key: ```bash node integrations/google-maps/traffic.js eta --from "DFW Airport" --to "Love Field" --departAt now +node integrations/google-maps/traffic.js eta --from "DFW Airport" --to "Love Field" --avoidTolls node integrations/google-maps/traffic.js leave-by --from "Home" --to "DFW Airport" --arriveBy 2026-03-17T08:30:00-05:00 +node integrations/google-maps/traffic.js leave-by --from "Home" --to "DFW Airport" --arriveBy 2026-03-17T08:30:00-05:00 --avoidTolls ``` + +## Optional route modifiers + +- `--avoidTolls` +- `--avoidHighways` +- `--avoidFerries` + +These flags are passed through to the Google Routes API `routeModifiers` field and work with both `eta` and `leave-by`. diff --git a/integrations/google-maps/traffic.js b/integrations/google-maps/traffic.js index ff86626..eac74e2 100755 --- a/integrations/google-maps/traffic.js +++ b/integrations/google-maps/traffic.js @@ -35,6 +35,9 @@ Commands: Optional flags: --keyPath API key file path (default: ${DEFAULT_KEY_PATH}) --timeZone Display timezone (default: America/Chicago) + --avoidTolls Avoid toll roads when possible + --avoidHighways Avoid highways when possible + --avoidFerries Avoid ferries when possible Notes: - Requires Google Maps APIs (Routes API + Geocoding API) enabled for the key. @@ -82,7 +85,15 @@ function fmtMinutes(sec) { return `${Math.round(sec / 60)} min`; } -async function computeRoute({ from, to, departAt, key }) { +function routeModifiersFromOpts(opts = {}) { + const modifiers = {}; + if (opts.avoidTolls) modifiers.avoidTolls = true; + if (opts.avoidHighways) modifiers.avoidHighways = true; + if (opts.avoidFerries) modifiers.avoidFerries = true; + return Object.keys(modifiers).length ? modifiers : undefined; +} + +async function computeRoute({ from, to, departAt, key, modifiers }) { const [o, d] = await Promise.all([geocode(from, key), geocode(to, key)]); const body = { origin: { location: { latLng: { latitude: o.lat, longitude: o.lng } } }, @@ -94,6 +105,8 @@ async function computeRoute({ from, to, departAt, key }) { units: 'IMPERIAL', }; + if (modifiers) body.routeModifiers = modifiers; + if (departAt && departAt !== 'now') body.departureTime = new Date(departAt).toISOString(); const res = await fetch('https://routes.googleapis.com/directions/v2:computeRoutes', { @@ -136,7 +149,8 @@ async function cmdEta(opts) { const tz = opts.timeZone || 'America/Chicago'; const departTs = opts.departAt && opts.departAt !== 'now' ? new Date(opts.departAt).getTime() : Date.now(); - const route = await computeRoute({ from: opts.from, to: opts.to, departAt: opts.departAt || 'now', key }); + const modifiers = routeModifiersFromOpts(opts); + const route = await computeRoute({ from: opts.from, to: opts.to, departAt: opts.departAt || 'now', key, modifiers }); const arriveTs = departTs + (route.durationSec || 0) * 1000; console.log(JSON.stringify({ @@ -147,6 +161,9 @@ async function cmdEta(opts) { eta: fmtMinutes(route.durationSec), trafficDelay: route.staticSec && route.durationSec ? fmtMinutes(Math.max(0, route.durationSec - route.staticSec)) : null, distanceMiles: route.distanceMiles ? Number(route.distanceMiles.toFixed(1)) : null, + avoidTolls: !!opts.avoidTolls, + avoidHighways: !!opts.avoidHighways, + avoidFerries: !!opts.avoidFerries, timeZone: tz, }, null, 2)); } @@ -158,14 +175,16 @@ async function cmdLeaveBy(opts) { const arriveTs = new Date(opts.arriveBy).getTime(); if (!Number.isFinite(arriveTs)) throw new Error('Invalid --arriveBy ISO datetime'); + const modifiers = routeModifiersFromOpts(opts); + // two-pass estimate let departGuess = arriveTs - 45 * 60 * 1000; for (let i = 0; i < 2; i++) { - const route = await computeRoute({ from: opts.from, to: opts.to, departAt: new Date(departGuess).toISOString(), key }); + const route = await computeRoute({ from: opts.from, to: opts.to, departAt: new Date(departGuess).toISOString(), key, modifiers }); departGuess = arriveTs - (route.durationSec || 0) * 1000; } - const finalRoute = await computeRoute({ from: opts.from, to: opts.to, departAt: new Date(departGuess).toISOString(), key }); + const finalRoute = await computeRoute({ from: opts.from, to: opts.to, departAt: new Date(departGuess).toISOString(), key, modifiers }); console.log(JSON.stringify({ from: finalRoute.origin, @@ -175,6 +194,9 @@ async function cmdLeaveBy(opts) { eta: fmtMinutes(finalRoute.durationSec), trafficDelay: finalRoute.staticSec && finalRoute.durationSec ? fmtMinutes(Math.max(0, finalRoute.durationSec - finalRoute.staticSec)) : null, distanceMiles: finalRoute.distanceMiles ? Number(finalRoute.distanceMiles.toFixed(1)) : null, + avoidTolls: !!opts.avoidTolls, + avoidHighways: !!opts.avoidHighways, + avoidFerries: !!opts.avoidFerries, timeZone: tz, }, null, 2)); }