En los últimos dieciocho meses Google ha declarado obsoletas cuatro partes muy usadas de la Maps JavaScript API. Dos de ellas, la Heatmap Layer y la Drawing Library, no están disponibles desde mayo de 2026. Las otras dos, DirectionsService y DistanceMatrixService, son Legacy y están obsoletas, pero siguen funcionando y no tienen fecha de cierre.
Esta guía recoge el estado actual de cada una, con enlaces a las páginas de Google, y explica cómo sustituirlas. Cuando el sustituto es de MapAtlas, el código usa nuestros endpoints y formatos de respuesta reales.
Qué está obsoleto y qué sigue funcionando
| Servicio | Estado | Fechas clave | Sustituto de Google |
|---|---|---|---|
| Heatmap Layer | Retirado | Obsoleto desde el 27 de mayo de 2025, retirado en la versión 3.65.1c (20 de mayo de 2026) | Bibliotecas de terceros como deck.gl |
| Drawing Library (DrawingManager) | Retirado | Obsoleto desde agosto de 2025, no disponible desde mayo de 2026 (confirmado en la versión 3.65.3b, 4 de junio de 2026) | Bibliotecas de terceros como Terra Draw |
| DirectionsService | Legacy, obsoleto | Legacy desde el 1 de marzo de 2025, obsoleto desde el 25 de febrero de 2026 (versión 3.64.2b), sin fecha de cierre | Clase Route de la biblioteca Routes |
| DistanceMatrixService | Legacy, obsoleto | Legacy desde el 1 de marzo de 2025, obsoleto desde el 25 de febrero de 2026 (versión 3.64.2b), sin fecha de cierre | Clase RouteMatrix de la biblioteca Routes |
Fuentes: deprecaciones de Google Maps Platform, las notas de versión de la Maps JavaScript API y la página de productos Legacy.
Legacy tiene un significado concreto. Según Google, un servicio Legacy no recibe funciones nuevas y no se puede activar en proyectos de Cloud nuevos, pero sigue con soporte en los proyectos existentes. Google se compromete a avisar con al menos 12 meses antes de retirarlo, y la nota de versión que declaró obsoletos DirectionsService y DistanceMatrixService dice que no está prevista su retirada y que seguirán recibiendo correcciones de regresiones graves. El mismo cambio a Legacy del 1 de marzo de 2025 afectó a los servicios web Directions API y Distance Matrix API, a la Places API y al JavaScript Places Service.
Qué deja de funcionar
Este código falla hoy en las versiones actuales de la 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();
Este código sigue funcionando en proyectos existentes, pero está obsoleto y no funciona en un proyecto de Cloud nuevo:
// Legacy and deprecated, no shutdown date yet
const directionsService = new google.maps.DirectionsService();
const distanceMatrixService = new google.maps.DistanceMatrixService();
Si un mapa de calor o una herramienta de dibujo de tu web dejó de funcionar en mayo de 2026, esta es la causa. Arregla eso primero. Después planifica la migración del enrutamiento, antes de que Google anuncie una fecha.
Los sustitutos de Google
Para el enrutamiento, Google ofrece dos caminos, y ninguno te obliga a llevar la lógica a un servidor:
- En el navegador: la biblioteca Routes de la Maps JavaScript API. La clase
Routesustituye a DirectionsService y la claseRouteMatrixsustituye a DistanceMatrixService. - En un servidor: la Routes API, un servicio REST con los métodos Compute Routes y Compute Route Matrix.
Para la visualización, Google ya no ofrece una opción propia. Su página del mapa de calor remite a deck.gl, y su página de deprecaciones menciona Terra Draw para dibujar.
Lista de comprobación para la migración
1. Audita tu código
Busca las clases afectadas:
# Find all files using deprecated Google Maps services
grep -rn "DirectionsService\|DistanceMatrixService\|DirectionsRenderer\|HeatmapLayer\|DrawingManager" \
--include="*.js" --include="*.ts" --include="*.tsx" --include="*.jsx" src/
Anota cada archivo y cada componente que las use. Indica si cada llamada se ejecuta en el navegador o en un servidor.
2. Documenta cómo usas cada servicio
Para cada uso, anota:
- Modos de transporte (coche, a pie, bicicleta, transporte público)
- Puntos intermedios (de A a B o rutas con varias paradas)
- Campos de respuesta que lees (distancia, duración, polilínea, pasos, tarifa)
- Volumen (peticiones por día y por mes, para estimar costes)
- Requisitos de latencia (en tiempo real para el usuario o por lotes)
3. Elige un destino
Opción A: quedarte con Google. Pasa a las clases Route y RouteMatrix o a la Routes API. Compara los campos que usas con las guías de migración, porque los formatos de respuesta cambian respecto a las clases Legacy.
Opción B: cambiar de proveedor. Vas a reescribir la integración de todos modos, así que compara proveedores con los requisitos del paso 2.
4. Ejecuta lo antiguo y lo nuevo en paralelo
No migres sobre la marcha. Ejecuta ambas implementaciones durante un tiempo y compara distancia, duración y latencia en tus rutas reales antes de cambiar.
5. Actualiza la gestión de errores
Las clases Legacy devolvían los errores mediante un valor de estado en un callback. Los sustitutos REST devuelven códigos de estado HTTP y cuerpos de error, así que tu gestión de errores tiene que cambiar.
Sustituir cada servicio con MapAtlas
Los endpoints de enrutamiento de MapAtlas son servicios REST en gateway.mapmetrics-atlas.net, documentados en la documentación de la API de MapAtlas. Reciben un token como parámetro de consulta, así que llámalos desde tu backend.
De DirectionsService a la Directions API de MapAtlas
Antes (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);
}
}
);
Después (Directions API de MapAtlas):
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);
Diferencias principales:
- Llamada REST en lugar de una clase de JavaScript, hecha desde tu backend.
- Los puntos intermedios son las entradas centrales de
locations. - Otra forma de respuesta. Los totales están en
trip.summaryy cada tramo tiene una polilínea codificada enshape. Decodifícala antes de dibujar la ruta (consulta el ejemplo de backend más abajo).
De DistanceMatrixService a la Matrix API de MapAtlas
La Matrix API de MapAtlas devuelve el tiempo y la distancia de viaje para cada par de origen y destino en una sola llamada. Nuestro endpoint limita cada par a 400 km de distancia por carretera, por eso el ejemplo usa lugares alrededor de Berlín.
Antes (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);
});
});
}
}
);
Después (Matrix API de MapAtlas):
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");
});
});
La cuadrícula equivale a rows y elements de Google. Las distancias llegan como números en la unidad que pidas y los tiempos en segundos, así que el formato corre de tu cuenta.
Sustituto de la Heatmap Layer
Opción 1: una capa de mapa de calor en MapLibre GL JS (código abierto)
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,
},
});
});
El mapa de calor se dibuja en la propia biblioteca de mapas, sin un servicio adicional.
Opción 2: la HeatmapLayer de deck.gl, que sugiere la documentación de Google:
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,
});
Las dos funcionan. MapLibre GL JS lee directamente los estilos de MapAtlas, y deck.gl se puede superponer a cualquier mapa base.
Sustituto de la Drawing Library
DrawingManager permitía a los usuarios dibujar marcadores, líneas, círculos y polígonos en el mapa. Terra Draw, la biblioteca que menciona la página de deprecaciones de Google, ofrece los mismos modos y tiene adaptadores para varias bibliotecas de mapas, entre ellas Google Maps y MapLibre GL JS. Las formas se devuelven en GeoJSON, que puedes guardar o enviar sin cambios a un servicio de geocercas o de isócronas.
Comparación de costes
Los precios de lista de Google son los mismos para los SKU Legacy de enrutamiento y para los SKU Essentials de sus sustitutos. Todos los precios de abajo proceden de la página de precios de Google Maps Platform y de la página de precios de MapAtlas, comprobadas el 26 de septiembre de 2026.
| Servicio | Google, gratis al mes | Google, por 1.000 a partir de ahí (primer tramo) | MapAtlas, gratis al mes | MapAtlas, por 1.000 a partir de ahí (primer tramo) |
|---|---|---|---|---|
| Directions (Legacy) o Compute Routes Essentials | 10.000 | 5,00 $ | 100.000 peticiones de navegación | 1,53 € |
| Directions Advanced (Legacy) o Compute Routes Pro | 5.000 | 10,00 $ | Mismo tramo de navegación | 1,53 € |
| Distance Matrix (Legacy) o Compute Route Matrix Essentials | 10.000 elementos | 5,00 $ | Consulta la página de precios | Consulta la página de precios |
| Geocoding | 10.000 | 5,00 $ | 25.000 | 3,90 € |
Google factura en dólares estadounidenses y MapAtlas en euros, así que convierte antes de comparar. Ambos aplican tarifas más bajas a volúmenes mayores. Como ejemplo, con 500.000 peticiones de ruta al mes:
- Google Compute Routes Essentials: 10.000 gratis, 90.000 a 5,00 $ y 400.000 a 4,00 $, es decir, 2.050 $.
- Navegación de MapAtlas: 100.000 gratis y 400.000 a 1,53 €, es decir, unos 610 €.
Ejemplo completo: ruta de backend y mapa
Este patrón mantiene tu clave de API en el servidor y solo envía al navegador la geometría de la ruta.
Ruta de API en el backend (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,
});
}
Componente de mapa (React y 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" }} />;
}
Cómo probar la migración
Paso 1: compara ambas implementaciones
Durante un periodo de prueba, llama al servicio antiguo y al nuevo con las mismas peticiones. Registra la distancia y la duración de ambos y revisa las diferencias en tus rutas reales, sobre todo en ferris, peajes y vías restringidas.
Paso 2: usa un feature flag
Pon el proveedor detrás de un flag para poder volver atrás sin desplegar:
const useMapAtlasDirections = process.env.FEATURE_MAPATLAS_DIRECTIONS === "true";
const directions = useMapAtlasDirections
? await fetchMapAtlasDirections(origin, destination)
: await fetchGoogleDirections(origin, destination);
Paso 3: vigila las tasas de error
Después del cambio, vigila:
- Tasas de HTTP 4xx y 5xx de la nueva API
- Cambios de latencia
- Campos que faltan y que tu interfaz lee
- Diferencias de ruta en casos límite como ferris, carreteras de peaje y zonas restringidas
Paso 4: prueba de carga antes de salir a producción
Si tu tráfico tiene picos, por ejemplo durante el reparto de la mañana en una plataforma logística, haz una prueba de carga de la nueva API al doble de tu volumen máximo antes de depender de ella.
Qué hacer ahora
- Si todavía cargas la Heatmap Layer o la Drawing Library: sustitúyelas primero. Ya no funcionan en las versiones actuales de la Maps JavaScript API.
- Audita el código de enrutamiento: localiza cada llamada a DirectionsService, DirectionsRenderer y DistanceMatrixService.
- Elige el sustituto: las clases
RouteyRouteMatrix, la Routes API u otro proveedor. - Migra un servicio cada vez, detrás de un feature flag, y compara resultados antes de cambiar.
- Sigue la página de deprecaciones de Google por si aparece una fecha de cierre. Google se ha comprometido a avisar con al menos 12 meses para los servicios Legacy.
Para probar las API de enrutamiento de MapAtlas, crea una cuenta gratuita y consulta la documentación de la API. Si necesitas ayuda con una migración, usa la página de contacto.
Preguntas frecuentes
¿Sigue disponible la Drawing Library de Google Maps?
No. Google declaró obsoleta la Drawing Library, incluido DrawingManager, en agosto de 2025, y su página de deprecaciones la indica como no disponible desde mayo de 2026. Las notas de la versión 3.65.3b de la Maps JavaScript API (4 de junio de 2026) confirman que DrawingManager ya no está disponible. Google menciona Terra Draw como alternativa.
¿Sigue disponible la Heatmap Layer?
No. Google declaró obsoleta la Heatmap Layer el 27 de mayo de 2025, y la versión 3.65.1c del 20 de mayo de 2026 indica que se ha retirado. Google sugiere en su lugar bibliotecas de terceros como la HeatmapLayer de deck.gl.
¿Cuándo dejarán de funcionar DirectionsService y DistanceMatrixService?
Google no ha fijado fecha. Ambos tienen estado Legacy desde el 1 de marzo de 2025 y están obsoletos desde el 25 de febrero de 2026. Según las notas de versión de Google, no está prevista su retirada, seguirán recibiendo correcciones de regresiones graves y habrá al menos 12 meses de aviso antes de que termine el soporte. Los servicios Legacy no están disponibles para proyectos de Cloud nuevos, así que un proyecto nuevo necesita las clases Route y RouteMatrix u otro proveedor.
¿Puedo migrar por partes o tengo que cambiarlo todo a la vez?
Puedes migrar un servicio cada vez. Cada clase obsoleta es independiente. Sustituye primero la Drawing Library y la Heatmap Layer, porque ya no funcionan, y después DirectionsService y DistanceMatrixService según tu propio calendario.
¿Admite MapAtlas puntos intermedios y varios modos de transporte?
Sí. La Directions API de MapAtlas recibe una lista ordenada de ubicaciones, de modo que cada punto entre el primero y el último es un punto intermedio, y admite los modelos de coste auto, bicycle, pedestrian, bus, truck, taxi y motor_scooter. Es un endpoint REST, así que llámalo desde tu backend para que la clave de API no llegue al cliente.

