Phil Gear philgear.dev
All Writings
2026-08-28 7 min read Phil Gear

The Pacific Northwest Parks Explorer: Saving Fuel on Scenic Tours with Google Maps Routes API

How to use Google Maps Routes API, Waypoint Optimization, and Eco-Friendly pathfinding to tour multiple nature parks and scenic trails while slashing fuel costs.

#Google Maps #Routes API #Parks & Recreation #Eco-Friendly Routing #TypeScript #Travel & Outdoors #Cost Optimization

Planning a weekend adventure across multiple state parks, arboretums, and scenic trailheads is one of the greatest joys of exploring the Pacific Northwest. Whether you are discovering the mossy canopy of Forest Park, the rose terraces of Washington Park, the volcanic vistas of Mt. Tabor, or the waterfalls of Silver Falls, visiting multiple nature spots in a single day can quickly turn into an expensive, winding trek if routed inefficiently.

Without intelligent multi-stop planning, drivers frequently spend more time idling at traffic lights, backtracking across bridges, and burning expensive fuel than enjoying the outdoors.

In this tutorial, we will build The Parks Explorer Route Optimizer using the modern Google Maps Platform Routes API. We will show how to take a wishlist of scenic parks, calculate the mathematically optimal visit sequence, and enable Eco-Friendly Fuel-Efficient routing to save money at the pump while shrinking your road trip’s carbon footprint.


1. The Real Cost of Unplanned Park Hopping

When visiting 4 to 8 parks in a single day, driving between them in a haphazard order introduces three major inefficiencies:

  1. The Zig-Zag Penalty: Visiting an eastside park, then driving west, then returning east forces duplicate highway miles and bridge crossings.
  2. Elevation & Gradient Inefficiencies: Navigating uphill routes without elevation-aware engine modeling spikes fuel burn in stop-and-go hill climbs.
  3. Lost Trailhead Time: Every extra 20 minutes spent backtracking is 20 fewer minutes on the trail.
[Unplanned Route] ──► 58 Miles Driven ──► 3.2 Gallons Fuel ──► 2.5 Hours Behind the Wheel
[Optimized Tour]  ──► 34 Miles Driven ──► 1.8 Gallons Fuel ──► 1.4 Hours Behind the Wheel

Estimated Savings per Road Trip:
Trimming 24 unnecessary driving miles saves ~$16.00 in fuel and vehicle wear on a single weekend outing—enough to cover park admission or a picnic lunch!


2. Architecture of the Parks Tour Engine

[Park Wishlist & Trailheads] ──► [Geocoding] ──► [Routes API: Waypoint Optimizer] ──► [Eco-Friendly Engine] ──► [Interactive Map HUD]
  1. Park Directory & Trailhead Waypoints: List coordinates or place IDs for parks, botanical gardens, and scenic viewpoints.
  2. Traveling Salesperson Optimization (optimizeWaypointOrder: true): Google’s routing algorithms reorder the intermediate park stops into the smoothest circular or one-way loop.
  3. Eco-Friendly Fuel Selection: Choose powertrain models (GASOLINE, HYBRID, ELECTRIC, or DIESEL) to minimize total energy consumption.
  4. Interactive Map HUD: Render high-contrast custom pins and polylines highlighting each stop on the tour.

3. Implementing the Routes API with TypeScript

We use the modern Google Maps Platform Routes API (computeRoutes) with strict field masks to keep latency minimal:

// src/services/park-route-optimizer.ts
export interface ParkWaypoint {
  name: string;
  location: { latitude: number; longitude: number };
  description?: string;
}

export interface OptimizeTourOptions {
  startingPoint: ParkWaypoint; // e.g. Home or Base Camp
  endingPoint: ParkWaypoint;   // e.g. Final scenic sunset viewpoint or home
  parksToVisit: ParkWaypoint[];
  powertrain?: "GASOLINE" | "HYBRID" | "ELECTRIC" | "DIESEL";
}

export async function computeOptimizedParkTour(
  apiKey: string,
  options: OptimizeTourOptions
) {
  const endpoint = "https://routes.googleapis.com/directions/v2:computeRoutes";

  const body = {
    origin: {
      location: {
        latLng: {
          latitude: options.startingPoint.location.latitude,
          longitude: options.startingPoint.location.longitude,
        },
      },
    },
    destination: {
      location: {
        latLng: {
          latitude: options.endingPoint.location.latitude,
          longitude: options.endingPoint.location.longitude,
        },
      },
    },
    intermediates: options.parksToVisit.map((park) => ({
      location: {
        latLng: {
          latitude: park.location.latitude,
          longitude: park.location.longitude,
        },
      },
      via: false, // Must be true scenic stops
    })),
    travelMode: "DRIVE",
    routingPreference: "TRAFFIC_AWARE_OPTIMAL",
    optimizeWaypointOrder: true, // Solves the optimal scenic tour sequence
    extraComputations: ["FUEL_EFFICIENT"],
    routeModifiers: {
      avoidTolls: true,
      avoidHighways: false,
      avoidFerries: true,
    },
    travelModeOptions: {
      driveOptions: {
        engineType: options.powertrain || "HYBRID",
      },
    },
  };

  const response = await fetch(endpoint, {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "X-Goog-Api-Key": apiKey,
      "X-Goog-FieldMask": [
        "routes.duration",
        "routes.distanceMeters",
        "routes.polyline.encodedPolyline",
        "routes.optimizedIntermediateWaypointIndex",
        "routes.travelAdvisory.fuelConsumptionMicroliters",
      ].join(","),
    },
    body: JSON.stringify(body),
  });

  if (!response.ok) {
    const errorText = await response.text();
    throw new Error(`Routes API Error (${response.status}): ${errorText}`);
  }

  return await response.json();
}

4. Unpacking the Tour Itinerary

The Routes API responds with:

  1. optimizedIntermediateWaypointIndex: The optimal chronological visiting order (e.g. [2, 0, 3, 1]), ensuring you don’t zig-zag across town.
  2. fuelConsumptionMicroliters: Accurate predicted fuel usage in microliters (1 Liter = 1,000,000 microliters).
  3. encodedPolyline: The precise GPS geometry for map rendering.
export function getOrderedParkItinerary(
  parks: ParkWaypoint[],
  optimizedIndices: number[]
): ParkWaypoint[] {
  return optimizedIndices.map((idx) => parks[idx]);
}

5. Visualizing the Tour with Interactive Advanced Markers

Render your route on the map using @googlemaps/js-api-loader with Advanced Marker Elements and custom forest-green theme tokens:

// src/components/parks-map.ts
import { Loader } from "@googlemaps/js-api-loader";

export async function initializeParksTourMap(
  mapContainer: HTMLElement,
  apiKey: string,
  orderedParks: ParkWaypoint[],
  encodedPolyline: string
) {
  const loader = new Loader({
    apiKey,
    version: "weekly",
    libraries: ["maps", "marker", "geometry"],
  });

  const { Map } = await loader.importLibrary("maps");
  const { AdvancedMarkerElement, PinElement } = await loader.importLibrary("marker");
  const { encoding } = await loader.importLibrary("geometry");

  const map = new Map(mapContainer, {
    center: { lat: 45.5152, lng: -122.6784 }, // Portland / Cascadia hub
    zoom: 12,
    mapId: "PACIFIC_NW_PARKS_TOUR_MAP",
  });

  // 1. Draw Forest Emerald Polyline
  const decodedPath = encoding.decodePath(encodedPolyline);
  const polyline = new google.maps.Polyline({
    path: decodedPath,
    geodesic: true,
    strokeColor: "#238636", // Forest Emerald Green
    strokeOpacity: 0.9,
    strokeWeight: 5,
  });
  polyline.setMap(map);

  // 2. Drop Numbered Park Pins
  orderedParks.forEach((park, idx) => {
    const pin = new PinElement({
      glyph: `${idx + 1}`,
      glyphColor: "#FFFFFF",
      background: "#1F2E4C", // Tartan Navy token
      borderColor: "#238636",
    });

    new AdvancedMarkerElement({
      map,
      position: { lat: park.location.latitude, lng: park.location.longitude },
      title: `Stop #${idx + 1}: ${park.name}`,
      content: pin.element,
    });
  });

  // 3. Frame all parks in the viewport
  const bounds = new google.maps.LatLngBounds();
  decodedPath.forEach((pt) => bounds.extend(pt));
  map.fitBounds(bounds);
}

6. Sample 4-Park Pacific Northwest Itinerary

Here is an example list of iconic parks you can optimize with this system:

StopPark NameKey Highlights
1Washington ParkInternational Rose Test Garden, Japanese Garden
2Hoyt Arboretum12 miles of cedar and redwood hiking trails
3Forest Park (Lower Macleay)Deep temperate rainforest canopy & Balch Creek
4Mt. Tabor ParkExtinct volcanic cinder cone & panoramic sunset views

When fed into the Routes API, the optimizer automatically figures out the most direct path between hillside parks, accounting for bridge traffic and hills, to ensure you spend more time in nature and less time at the gas station.


Summary & Key Takeaways

  1. Waypoint Optimization (optimizeWaypointOrder: true) eliminates backtracking across multi-park road trips.
  2. Eco-Friendly Routing (FUEL_EFFICIENT) selects slopes and roadways that maximize fuel economy and electric vehicle battery range.
  3. Advanced Markers provide high-contrast, accessible UI cues for explorers on mobile and desktop.

Happy exploring, and enjoy the trails!

Intergenerational Peer Dialogue (3)

A Safe Space for All Generations of Builders & Thinkers

Whether you wrote your first lines of assembly in 1978 or just started your first React project this week—your time, lived wisdom, and curiosity are celebrated here. We reject gatekeeping in all forms.

Eleni Vance, PESeasoned Architect (Emeritus / 20+ Yrs)
2026-08-28

Having spent 30 years designing municipal transit in the Pacific Northwest, seeing modern graph algorithms paired with hybrid regenerative braking formulas is a breath of fresh air. Intergenerational software carries past field lessons into modern code.

Marcus ChenEmerging Developer (Student / Apprentice)
2026-08-28

As someone learning TypeScript and Astro for the first time, breaking down the Routes API TSP parameters step-by-step made this feel accessible rather than intimidating. Thank you for welcoming beginner questions!

Phil GearAuthor
2026-08-28

Thank you both. Eleni, your field insights on municipal grade constraints directly informed how we evaluate elevation penalties; Marcus, your curiosity is what keeps our open-source tools vibrant. Every era of experience is treasured here.

Share Your Perspective (All Eras & Backgrounds Welcome)

Sandboxed moderation ensures a safe, encouraging space for every age.