Google has deprecated four widely used parts of the Maps JavaScript API in the last eighteen months. Two of them, the Heatmap Layer and the Drawing Library, have been unavailable since May 2026. The other two, DirectionsService and DistanceMatrixService, are Legacy and deprecated, but still work and have no shutdown date.
This guide lists the current status of each, with links to Google's own pages, and shows how to replace them. Where a replacement comes from MapAtlas, the code uses our real endpoints and response formats.
What is deprecated, and what still works
| Service | Status | Key dates | Google's replacement |
|---|---|---|---|
| Heatmap Layer | Removed | Deprecated 27 May 2025, decommissioned in release 3.65.1c (20 May 2026) | Third-party libraries such as deck.gl |
| Drawing Library (DrawingManager) | Removed | Deprecated August 2025, unavailable as of May 2026 (confirmed in release 3.65.3b, 4 June 2026) | Third-party libraries such as Terra Draw |
| DirectionsService | Legacy, deprecated | Legacy since 1 March 2025, deprecated as of 25 February 2026 (release 3.64.2b), no shutdown date | Route class in the Routes library |
| DistanceMatrixService | Legacy, deprecated | Legacy since 1 March 2025, deprecated as of 25 February 2026 (release 3.64.2b), no shutdown date | RouteMatrix class in the Routes library |
Sources: Google Maps Platform deprecations, the Maps JavaScript API release notes, and the Legacy products page.
Legacy has a specific meaning. According to Google, a Legacy service is feature-frozen and cannot be enabled in new Cloud projects, but it stays supported for existing projects. Google commits to at least 12 months' notice before it decommissions one, and the release note that deprecated DirectionsService and DistanceMatrixService says they are not scheduled to be discontinued and will still receive fixes for major regressions. The same Legacy change on 1 March 2025 covered the Directions API and Distance Matrix API web services and the Places API and JavaScript Places Service.
What breaks
This code fails today on current versions of the Maps JavaScript API:
// Removed in 2026: these constructors no longer work
const heatmap = new google.maps.visualization.HeatmapLayer({ data: points });
const drawingManager = new google.maps.drawing.DrawingManager();
This code still runs in existing projects, but it is deprecated and will not work in a new Cloud project:
// Legacy and deprecated, no shutdown date yet
const directionsService = new google.maps.DirectionsService();
const distanceMatrixService = new google.maps.DistanceMatrixService();
If a heatmap or drawing tool on your site stopped working in May 2026, this is the cause. Fix those first. Plan the routing migration next, before Google announces a date.
Google's replacements
For routing, Google offers two paths, and neither forces you to move logic to a server:
- In the browser: the Routes library in the Maps JavaScript API. The
Routeclass replaces DirectionsService, and theRouteMatrixclass replaces DistanceMatrixService. - On a server: the Routes API, a REST service with Compute Routes and Compute Route Matrix methods.
For visualisation, Google no longer ships a first-party option. Its heatmap page points to deck.gl, and its deprecations page names Terra Draw for drawing.
Migration checklist
1. Audit your codebase
Search for the affected classes:
# Find all files using deprecated Google Maps services
grep -rn "DirectionsService\|DistanceMatrixService\|DirectionsRenderer\|HeatmapLayer\|DrawingManager" \
--include="*.js" --include="*.ts" --include="*.tsx" --include="*.jsx" src/
Record every file and component that uses them. Note whether each call runs in the browser or on a server.
2. Record how you use each service
For each usage, write down:
- Travel modes (driving, walking, cycling, transit)
- Waypoints (simple A to B, or multi-stop routes)
- Response fields you read (distance, duration, polyline, steps, fare)
- Volume (requests per day and per month, for cost estimates)
- Latency needs (user-facing in real time, or batch)
3. Choose a target
Option A: stay with Google. Move to the Route and RouteMatrix classes, or to the Routes API. Check the fields you use against the migration guides, because the response formats differ from the legacy classes.
Option B: switch providers. You are rewriting the integration anyway, so compare providers on the requirements from step 2.
4. Run old and new side by side
Do not migrate in place. Run both implementations for a period and compare distance, duration and latency on your real routes before you cut over.
5. Update error handling
The legacy classes reported errors through a status value in a callback. REST replacements return HTTP status codes and error bodies, so your error handling has to change.
Replacing each service with MapAtlas
The MapAtlas routing endpoints are REST services on gateway.mapmetrics-atlas.net, documented in the MapAtlas API docs. They take a token as a query parameter, so call them from your backend.
DirectionsService to the MapAtlas Directions API
Before (Google, Legacy):
const directionsService = new google.maps.DirectionsService();
directionsService.route(
{
origin: { lat: 52.52, lng: 13.405 },
destination: { lat: 48.8566, lng: 2.3522 },
travelMode: google.maps.TravelMode.DRIVING,
waypoints: [
{ location: { lat: 50.9375, lng: 6.9603 }, stopover: true }
],
},
(result, status) => {
if (status === "OK") {
const route = result.routes[0];
console.log("Distance:", route.legs[0].distance.text);
console.log("Duration:", route.legs[0].duration.text);
}
}
);
After (MapAtlas Directions API):
const response = await fetch(
`https://gateway.mapmetrics-atlas.net/directions/?token=${process.env.MAPATLAS_API_KEY}`,
{
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
// Ordered stops: origin, waypoints, destination
locations: [
{ lat: 52.52, lon: 13.405 },
{ lat: 50.9375, lon: 6.9603 },
{ lat: 48.8566, lon: 2.3522 },
],
costing: "auto", // auto, bicycle, pedestrian, bus, truck, taxi, motor_scooter
units: "kilometers",
}),
}
);
const { trip } = await response.json();
console.log("Distance (km):", trip.summary.length);
console.log("Duration (s):", trip.summary.time);
// Each leg carries its geometry as an encoded polyline (precision 6)
const shapes = trip.legs.map((leg) => leg.shape);
Key differences:
- REST call instead of a JavaScript class, made from your backend.
- Waypoints are the middle entries of
locations. - Different response shape. Totals are in
trip.summary, and each leg has an encoded polyline inshape. Decode it before you draw the route (see the backend example below).
DistanceMatrixService to the MapAtlas Matrix API
The MapAtlas Matrix API returns travel time and distance for every origin and destination pair in one call. Our endpoint limits each pair to 400 km of route distance, so the example uses places around Berlin.
Before (Google, Legacy):
const service = new google.maps.DistanceMatrixService();
service.getDistanceMatrix(
{
origins: [
{ lat: 52.52, lng: 13.405 },
{ lat: 52.3906, lng: 13.0645 },
],
destinations: [
{ lat: 51.3397, lng: 12.3731 },
{ lat: 51.0504, lng: 13.7373 },
],
travelMode: google.maps.TravelMode.DRIVING,
},
(response, status) => {
if (status === "OK") {
response.rows.forEach((row, i) => {
row.elements.forEach((element, j) => {
console.log(`Origin ${i} -> Dest ${j}:`,
element.distance.text, element.duration.text);
});
});
}
}
);
After (MapAtlas Matrix API):
const response = await fetch(
`https://gateway.mapmetrics-atlas.net/matrix/?token=${process.env.MAPATLAS_API_KEY}`,
{
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
sources: [
{ lat: 52.52, lon: 13.405 },
{ lat: 52.3906, lon: 13.0645 },
],
targets: [
{ lat: 51.3397, lon: 12.3731 },
{ lat: 51.0504, lon: 13.7373 },
],
costing: "auto",
units: "kilometers",
}),
}
);
const data = await response.json();
// One row per source, one cell per target
data.sources_to_targets.forEach((row: any[], i: number) => {
row.forEach((cell, j) => {
console.log(`Origin ${i} -> Dest ${j}:`, cell.distance, "km", cell.time, "s");
});
});
The grid is the same as Google's rows and elements. Distances come back as numbers in the unit you request, and times in seconds, so format them yourself.
Heatmap Layer replacement
Option 1: a MapLibre GL JS heatmap layer (open source)
import maplibregl from "maplibre-gl";
const map = new maplibregl.Map({
container: "map",
style: "https://gateway.mapmetrics-atlas.net/styles/?fileName=YOUR_ACCOUNT_ID/mapstyle.json&token=YOUR_API_KEY",
center: [13.405, 52.52],
zoom: 10,
});
map.on("load", () => {
map.addSource("heat-data", {
type: "geojson",
data: {
type: "FeatureCollection",
features: heatmapPoints.map((point) => ({
type: "Feature",
geometry: { type: "Point", coordinates: [point.lng, point.lat] },
properties: { weight: point.weight },
})),
},
});
map.addLayer({
id: "heatmap-layer",
type: "heatmap",
source: "heat-data",
paint: {
"heatmap-weight": ["get", "weight"],
"heatmap-intensity": 1,
"heatmap-radius": 20,
"heatmap-opacity": 0.7,
},
});
});
The heatmap renders in the map library itself, with no extra service.
Option 2: the deck.gl HeatmapLayer, which Google's documentation suggests:
import { HeatmapLayer } from "@deck.gl/aggregation-layers";
const heatmapLayer = new HeatmapLayer({
data: heatmapPoints,
getPosition: (d) => [d.lng, d.lat],
getWeight: (d) => d.weight,
radiusPixels: 30,
});
Both work. MapLibre GL JS reads MapAtlas styles directly, and deck.gl can sit on top of any base map.
Drawing Library replacement
DrawingManager let users draw markers, lines, circles and polygons on the map. Terra Draw, the library Google's deprecations page names, provides the same modes and has adapters for several map libraries, including Google Maps and MapLibre GL JS. The shapes come back as GeoJSON, which you can store or send to a geofencing or isochrone service unchanged.
Cost comparison
Google's list prices are the same for the legacy routing SKUs and the Essentials SKUs of their replacements. Every price below comes from the Google Maps Platform pricing page and the MapAtlas pricing page, both checked on 26 September 2026.
| Service | Google, free per month | Google, per 1,000 after that (first tier) | MapAtlas, free per month | MapAtlas, per 1,000 after that (first tier) |
|---|---|---|---|---|
| Directions (legacy) or Compute Routes Essentials | 10,000 | $5.00 | 100,000 navigation requests | €1.53 |
| Directions Advanced (legacy) or Compute Routes Pro | 5,000 | $10.00 | Same navigation tier | €1.53 |
| Distance Matrix (legacy) or Compute Route Matrix Essentials | 10,000 elements | $5.00 | See the pricing page | See the pricing page |
| Geocoding | 10,000 | $5.00 | 25,000 | €3.90 |
Google bills in US dollars and MapAtlas in euros, so convert before you compare. Both apply lower rates at higher volumes. As a worked example, at 500,000 route requests a month:
- Google Compute Routes Essentials: 10,000 free, 90,000 at $5.00 and 400,000 at $4.00, so $2,050.
- MapAtlas navigation: 100,000 free and 400,000 at €1.53, so about €610.
Full example: backend route and map
This pattern keeps your API key on the server and sends only the route geometry to the browser.
Backend API route (Next.js)
// app/api/directions/route.ts
import { NextRequest, NextResponse } from "next/server";
// Decode an encoded polyline with precision 6 into [lon, lat] pairs
function decodePolyline6(str: string): [number, number][] {
const coords: [number, number][] = [];
let index = 0, lat = 0, lng = 0;
while (index < str.length) {
for (const axis of [0, 1]) {
let result = 0, shift = 0, byte: number;
do {
byte = str.charCodeAt(index++) - 63;
result |= (byte & 0x1f) << shift;
shift += 5;
} while (byte >= 0x20);
const delta = result & 1 ? ~(result >> 1) : result >> 1;
if (axis === 0) lat += delta; else lng += delta;
}
coords.push([lng / 1e6, lat / 1e6]);
}
return coords;
}
export async function GET(request: NextRequest) {
const { searchParams } = new URL(request.url);
const [oLat, oLon] = searchParams.get("origin")!.split(",").map(Number);
const [dLat, dLon] = searchParams.get("destination")!.split(",").map(Number);
const response = await fetch(
`https://gateway.mapmetrics-atlas.net/directions/?token=${process.env.MAPATLAS_API_KEY}`,
{
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
locations: [{ lat: oLat, lon: oLon }, { lat: dLat, lon: dLon }],
costing: "auto",
units: "kilometers",
}),
}
);
if (!response.ok) {
return NextResponse.json({ error: "routing failed" }, { status: 502 });
}
const { trip } = await response.json();
const coordinates = trip.legs.flatMap((leg: { shape: string }) => decodePolyline6(leg.shape));
return NextResponse.json({
coordinates,
distanceKm: trip.summary.length,
durationS: trip.summary.time,
});
}
Map component (React and MapLibre GL JS)
import { useEffect, useRef } from "react";
import maplibregl from "maplibre-gl";
function DirectionsMap({ origin, destination }: {
origin: { lat: number; lng: number };
destination: { lat: number; lng: number };
}) {
const mapRef = useRef<HTMLDivElement>(null);
useEffect(() => {
const map = new maplibregl.Map({
container: mapRef.current!,
style: "https://gateway.mapmetrics-atlas.net/styles/?fileName=YOUR_ACCOUNT_ID/mapstyle.json&token=YOUR_API_KEY",
center: [origin.lng, origin.lat],
zoom: 7,
});
map.on("load", async () => {
const res = await fetch(
"/api/directions?" +
new URLSearchParams({
origin: `${origin.lat},${origin.lng}`,
destination: `${destination.lat},${destination.lng}`,
})
);
if (!res.ok) return;
const { coordinates } = await res.json();
map.addSource("route", {
type: "geojson",
data: {
type: "Feature",
geometry: { type: "LineString", coordinates },
properties: {},
},
});
map.addLayer({
id: "route-line",
type: "line",
source: "route",
paint: { "line-color": "#4A90D9", "line-width": 5 },
});
const bounds = coordinates.reduce(
(b: maplibregl.LngLatBounds, coord: [number, number]) => b.extend(coord),
new maplibregl.LngLatBounds(coordinates[0], coordinates[0])
);
map.fitBounds(bounds, { padding: 50 });
});
return () => map.remove();
}, [origin, destination]);
return <div ref={mapRef} style={{ width: "100%", height: "400px" }} />;
}
How to test the migration
Step 1: compare both implementations
For a test period, call the old service and the new one for the same requests. Log distance and duration from both and look at the differences on your real routes, especially ferries, tolls and restricted roads.
Step 2: use a feature flag
Put the provider behind a flag so you can switch back without a deploy:
const useMapAtlasDirections = process.env.FEATURE_MAPATLAS_DIRECTIONS === "true";
const directions = useMapAtlasDirections
? await fetchMapAtlasDirections(origin, destination)
: await fetchGoogleDirections(origin, destination);
Step 3: monitor error rates
After the switch, watch for:
- HTTP 4xx and 5xx rates from the new API
- Latency changes
- Missing fields that your UI reads
- Route differences on edge cases such as ferries, toll roads and restricted areas
Step 4: load test before go-live
If your traffic spikes, for example during morning dispatch on a logistics platform, load test the new API at twice your peak volume before you rely on it.
What to do now
- If you still load the Heatmap Layer or the Drawing Library: replace them first. They no longer work on current versions of the Maps JavaScript API.
- Audit routing code: find every DirectionsService, DirectionsRenderer and DistanceMatrixService call.
- Pick the replacement: the
RouteandRouteMatrixclasses, the Routes API, or another provider. - Migrate one service at a time, behind a feature flag, and compare results before cutting over.
- Watch Google's deprecations page for a shutdown date. Google has committed to at least 12 months' notice for Legacy services.
To try the MapAtlas routing APIs, create a free account and read the API documentation. For help with a migration, use the contact page.
Frequently Asked Questions
Is the Google Maps Drawing Library still available?
No. Google deprecated the Drawing Library, including DrawingManager, in August 2025, and its deprecations page lists it as unavailable as of May 2026. The Maps JavaScript API release notes for version 3.65.3b (4 June 2026) confirm that DrawingManager is no longer available. Google's deprecations page points to Terra Draw as an alternative.
Is the Heatmap Layer still available?
No. Google deprecated the Heatmap Layer on 27 May 2025, and release 3.65.1c on 20 May 2026 states that it is decommissioned. Google suggests third-party libraries such as the deck.gl HeatmapLayer instead.
When will DirectionsService and DistanceMatrixService stop working?
Google has not set a date. Both have been in Legacy status since 1 March 2025 and deprecated since 25 February 2026. Google's release notes say they are not scheduled to be discontinued, will keep receiving fixes for major regressions, and will get at least 12 months' notice before support ends. Legacy services are not available to new Cloud projects, so new projects must use the Route and RouteMatrix classes or another provider.
Can I migrate incrementally or do I need to switch everything at once?
You can migrate one service at a time. Each deprecated class is independent, so fix the removed Drawing Library and Heatmap Layer first, because they already fail, then replace DirectionsService and DistanceMatrixService on your own schedule.
Does MapAtlas support waypoints and several travel modes?
Yes. The MapAtlas Directions API takes an ordered list of locations, so every point between the first and the last is a waypoint, and it supports the auto, bicycle, pedestrian, bus, truck, taxi and motor_scooter costing models. It is a REST endpoint, so call it from your backend to keep the API key off the client.

