يُشدد Google من قيوده على Maps Platform منذ سنوات، لكن موجة الإيقافات في مايو 2026 هي الأكثر تأثيراً حتى الآن. أربع ميزات واسعة الاستخدام ستُحذف من Maps JavaScript API: Heatmap Layer وDrawing Library وDirectionsService وDistanceMatrixService. إن كان تطبيقك يعتمد على أيٍّ منها، فأمامك أسابيع قليلة للترحيل قبل أن يتوقف كودك عن العمل.
هذا ليس إيقافاً ناعماً تبقى فيه نقاط النهاية القديمة لسنوات. حدد Google تواريخ إزالة صارمة، وتعرض وحدة تحكم المطورين بالفعل تحذيرات. يشرح هذا الدليل بالتفصيل ما الذي يتغير ولماذا، وكيفية ترحيل كل خدمة مع أمثلة كود عملية. إن كنت تبحث عن بديل جاهز، سنوضح لك كيف تتوافق واجهات API في MapAtlas مع كل خدمة Google موقوفة.
ما الذي يُوقف ومتى
فيما يلي الجدول الزمني الكامل للخدمات المتأثرة:
| الخدمة | تاريخ الإيقاف | تاريخ الإزالة | البديل (Google) |
|---|---|---|---|
| Heatmap Layer (Maps JS API) | أغسطس 2025 | مايو 2026 | Maps Datasets API + deck.gl |
| Drawing Library (Maps JS API) | مايو 2025 | مايو 2026 | Extended Component Library |
| DirectionsService (Maps JS API) | 25 فبراير 2026 | مايو 2026 | Routes API (REST) |
| DistanceMatrixService (Maps JS API) | 25 فبراير 2026 | مايو 2026 | Routes API (REST) |
التفصيل المهم: لا يُزيل Google التوجيه وحسابات المسافة كلياً. ما يُزيله هو فئات JavaScript من جانب العميل التي يستخدمها المطورون منذ أكثر من عقد، مع إجبارهم على الترحيل إلى Routes API الأحدث وهي نقطة نهاية REST من جانب الخادم. هذا ليس مجرد تحديث إصدار، بل تغيير معماري ينقل منطق التوجيه من المتصفح إلى الخادم الخلفي.
ما الذي سيتوقف عن العمل
إن كان كودك يحتوي على أيٍّ مما يلي، فسيتعطل بعد تاريخ الإزالة:
// All of these will stop working in May 2026
const directionsService = new google.maps.DirectionsService();
const distanceMatrixService = new google.maps.DistanceMatrixService();
const heatmap = new google.maps.visualization.HeatmapLayer({ data: points });
const drawingManager = new google.maps.drawing.DrawingManager();
بعد الإزالة ستُطلق هذه الدوال البانية أخطاء. لا يوجد سلوك احتياطي ولا تدهور تدريجي. ستُحمَّل الخريطة، لكن أي ميزة تعتمد على هذه الفئات ستتعطل تماماً.
لماذا يفرض Google هذه التغييرات
المبرر الرسمي لدى Google هو الأداء والتحديث. تدعم Routes API الخدمةُ البديلة عن DirectionsService وDistanceMatrixService ميزات أحدث كالتوجيه الصديق للبيئة، والتوجيه للمركبات ذات العجلتين، وتقدير تكاليف الرسوم التي لا تستطيع الفئات القديمة استيعابها.
الدافع الحقيقي هو التحكم بالتسعير. بنقل التوجيه إلى REST API من جانب الخادم، يحقق Google فوترة أكثر دقة وتتبعاً أفضل للاستخدام. كان DirectionsService القائم على JavaScript يتيح أنماط المعالجة الدُّفعية من جانب العميل التي يصعب على Google قياسها بدقة. تضمن Routes API مرور كل طلب عبر نقطة نهاية مقيسة.
أما بالنسبة لـ Heatmap Layer وDrawing Library، فيدفع Google المطورين نحو Extended Component Library وأدوات التصور من جهات خارجية مثل deck.gl. هذا جزء من نمط أشمل: يحتفظ Google بعرض خرائط التجانب داخلياً ويُحيل الباقي إلى المنظومة البيئية.
التأثير العملي على المطورين: المزيد من البنية التحتية للخادم الخلفي، ومفاتيح API أكثر للإدارة، وفي أغلب الحالات تكاليف أعلى لكل طلب في مستويات التسعير الجديدة.
قائمة مرجعية للترحيل
قبل كتابة أي كود ترحيل، اعمل بهذه القائمة المرجعية:
1. مراجعة قاعدة الكود
ابحث في قاعدة كودك عن الإشارات إلى الفئات الموقوفة:
# Find all files using deprecated Google Maps services
grep -rn "DirectionsService\|DistanceMatrixService\|HeatmapLayer\|visualization.HeatmapLayer\|drawing.DrawingManager\|DrawingManager" \
--include="*.js" --include="*.ts" --include="*.tsx" --include="*.jsx" src/
وثِّق كل ملف ومكوِّن يستخدم هذه الخدمات. لاحظ ما إذا كانت الاستدعاءات تتم من جانب العميل (المتصفح) أو في سياق مُقدَّم من الخادم مثل مسارات API في Next.js.
2. تحديد أنماط الاستخدام
لكل استخدام، وثِّق:
- أوضاع السفر المستخدمة (القيادة والمشي وركوب الدراجات والعبور)
- دعم نقاط الطريق (مسار بسيط من A إلى B، أو مسارات متعددة المحطات)
- حقول الاستجابة المستهلكة (المسافة والمدة والخط متعدد الأضلاع والخطوات والأجرة)
- الحجم (الطلبات يومياً/شهرياً لتقدير التكلفة)
- متطلبات زمن الاستجابة (وقت فعلي للمستخدم أو معالجة دُفعية)
3. اختيار هدف الترحيل
لديك خياران:
الخيار أ: البقاء مع Google. الترحيل من فئات JavaScript الموقوفة إلى Google Routes API الجديدة (REST). يستلزم هذا تغييرات في الخادم الخلفي وأذونات مفتاح API جديدة وتحديث الفوترة.
الخيار ب: التحويل إلى مزود آخر. الترحيل إلى واجهة API للتوجيه من جهة خارجية. هذا الوقت المناسب لتقييم البدائل لأنك ستُعيد كتابة كود التكامل على أي حال.
4. إعداد بيئة موازية
لا تُرحِّل في مكانك أبداً. شغِّل التطبيقين القديم والجديد جنباً إلى جنب لأسبوعين على الأقل، وقارن النتائج من حيث الدقة وزمن الاستجابة قبل التحويل.
5. تحديث معالجة الأخطاء
كانت الخدمات الموقوفة تُعيد الأخطاء عبر دوال رد الاتصال. تُعيد بدائل REST API رموز حالة HTTP. يجب تغيير منطق معالجة الأخطاء وفقاً لذلك.
دليل الاستبدال API بـ API
من DirectionsService إلى MapAtlas Directions API
قبل (Google، موقوفة):
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);
}
}
);
بعد (MapAtlas Directions API):
const response = await fetch(
"https://api.mapatlas.com/v1/directions?" +
new URLSearchParams({
origin: "52.52,13.405",
destination: "48.8566,2.3522",
waypoints: "50.9375,6.9603",
mode: "driving",
key: process.env.MAPATLAS_API_KEY!,
})
);
const data = await response.json();
if (data.status === "OK") {
const leg = data.routes[0].legs[0];
console.log("Distance:", leg.distance.text);
console.log("Duration:", leg.duration.text);
// Polyline for map rendering
const polyline = data.routes[0].overview_polyline;
}
الفروق الرئيسية:
- نقطة نهاية REST بدلاً من فئة JavaScript. تأتي الاستدعاءات من خادمك الخلفي لا من المتصفح.
- مصادقة أبسط. مفتاح API واحد في سلسلة الاستعلام أو الترويسة.
- هيكل الاستجابة نفسه. Routes وlegs وdistance وduration وpolyline جميعها موجودة.
من DistanceMatrixService إلى MapAtlas Matrix API
قبل (Google، موقوفة):
const service = new google.maps.DistanceMatrixService();
service.getDistanceMatrix(
{
origins: [
{ lat: 52.52, lng: 13.405 },
{ lat: 48.1351, lng: 11.582 },
],
destinations: [
{ lat: 48.8566, lng: 2.3522 },
{ lat: 51.5074, lng: -0.1278 },
],
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);
});
});
}
}
);
بعد (MapAtlas Matrix API):
const response = await fetch("https://api.mapatlas.com/v1/matrix", {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${process.env.MAPATLAS_API_KEY}`,
},
body: JSON.stringify({
origins: [
{ lat: 52.52, lng: 13.405 },
{ lat: 48.1351, lng: 11.582 },
],
destinations: [
{ lat: 48.8566, lng: 2.3522 },
{ lat: 51.5074, lng: -0.1278 },
],
mode: "driving",
}),
});
const data = await response.json();
data.rows.forEach((row: any, i: number) => {
row.elements.forEach((element: any, j: number) => {
console.log(`Origin ${i} -> Dest ${j}:`,
element.distance.text, element.duration.text);
});
});
هيكل الاستجابة شبه متطابق. التغيير الرئيسي هو الانتقال من واجهة API للمتصفح تعتمد على رد الاتصال إلى واجهة API للخادم تعتمد على الوعود (promises).
استبدال Heatmap Layer
يعتمد مسار الترحيل لتصور خرائط الحرارة على متطلباتك:
الخيار 1: MapLibre GL JS مع طبقة heatmap (مفتوح المصدر)
import maplibregl from "maplibre-gl";
const map = new maplibregl.Map({
container: "map",
style: "https://api.mapatlas.com/v1/styles/streets?key=YOUR_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,
},
});
});
يمنحك هذا تحكماً كاملاً في عرض خريطة الحرارة دون الاعتماد على خوادم التجانب في Google.
الخيار 2: deck.gl HeatmapLayer (توصية Google الرسمية)
import { Deck } from "@deck.gl/core";
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,
});
كلا الخيارين يعمل. يتكامل MapLibre GL JS بسهولة مع أنماط تجانب MapAtlas، فيما يمكن تراكب deck.gl فوق أي خريطة أساسية.
مقارنة التكاليف: Google Routes API مقابل MapAtlas
الترحيل إلى Routes API الخاصة بـ Google ليس مجرد تغيير في الكود. يأتي معه تسعير جديد:
| الخدمة | Google (legacy، لكل 1,000) | Google Routes API (لكل 1,000) | MapAtlas (لكل 1,000) |
|---|---|---|---|
| Directions (أساسي) | $5.00 | $5.00 | $1.50 |
| Directions (متقدم، نقاط طريق/حركة مرور) | $10.00 | $10.00 | $2.50 |
| Distance Matrix (لكل عنصر) | $5.00 | $5.00 | $1.00 |
| Distance Matrix (متقدم) | $10.00 | $10.00 | $2.00 |
| Geocoding | $5.00 | $5.00 | $1.50 |
عند 100,000 طلب توجيه شهرياً:
- Google Routes API: نحو $500-$1,000 (حسب الميزات المستخدمة)
- MapAtlas Directions API: نحو $150-$250
تتراكم الوفورات بسرعة للتطبيقات التي تجمع التوجيه مع الترميز الجغرافي وحسابات المسافات. منصة لوجستية تعالج 500,000 عنصر مصفوفة شهرياً ستدفع نحو $2,500 مع Google مقابل $500 مع MapAtlas.
للاطلاع على تفاصيل الأسعار الكاملة، راجع أسعار MapAtlas.
أمثلة كاملة على ترحيل الكود: قبل وبعد
مثال ترحيل كامل لمكوِّن React يعرض اتجاهات القيادة على خريطة.
قبل: Google Maps DirectionsService في React
import { useEffect, useRef } from "react";
function DirectionsMap({ origin, destination }: {
origin: google.maps.LatLngLiteral;
destination: google.maps.LatLngLiteral;
}) {
const mapRef = useRef<HTMLDivElement>(null);
useEffect(() => {
const map = new google.maps.Map(mapRef.current!, {
center: origin,
zoom: 7,
});
const directionsRenderer = new google.maps.DirectionsRenderer();
directionsRenderer.setMap(map);
const directionsService = new google.maps.DirectionsService();
directionsService.route(
{
origin,
destination,
travelMode: google.maps.TravelMode.DRIVING,
},
(result, status) => {
if (status === "OK" && result) {
directionsRenderer.setDirections(result);
}
}
);
}, [origin, destination]);
return <div ref={mapRef} style={{ width: "100%", height: "400px" }} />;
}
بعد: MapAtlas Directions API مع 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://api.mapatlas.com/v1/styles/streets?key=YOUR_KEY",
center: [origin.lng, origin.lat],
zoom: 7,
});
map.on("load", async () => {
// Fetch directions from your backend (or directly if CORS allows)
const res = await fetch(
`/api/directions?` +
new URLSearchParams({
origin: `${origin.lat},${origin.lng}`,
destination: `${destination.lat},${destination.lng}`,
mode: "driving",
})
);
const data = await res.json();
if (data.routes?.[0]) {
const coordinates = data.routes[0].geometry.coordinates;
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,
},
});
// Fit map to route bounds
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" }} />;
}
مسار API للخادم الخلفي (Next.js)
// app/api/directions/route.ts
import { NextRequest, NextResponse } from "next/server";
export async function GET(request: NextRequest) {
const { searchParams } = new URL(request.url);
const origin = searchParams.get("origin");
const destination = searchParams.get("destination");
const mode = searchParams.get("mode") || "driving";
const response = await fetch(
`https://api.mapatlas.com/v1/directions?` +
new URLSearchParams({
origin: origin!,
destination: destination!,
mode,
key: process.env.MAPATLAS_API_KEY!,
})
);
const data = await response.json();
return NextResponse.json(data);
}
يحتفظ هذا النمط بمفتاح API في الخادم ولا يكشف للعميل إلا هندسة المسار.
كيفية اختبار الترحيل قبل الموعد النهائي
الخطوة 1: تشغيل كلتا الـ API بشكل متوازٍ
لمدة أسبوعين قبل التحويل، استدعِ كلاً من خدمة Google القديمة والبديل الجديد لكل طلب. سجِّل كلتا الاستجابتين وقارن بينهما:
async function getDirectionsWithComparison(
origin: string,
destination: string
) {
const [googleResult, mapatResult] = await Promise.all([
fetchGoogleDirections(origin, destination),
fetchMapAtlasDirections(origin, destination),
]);
// Compare key metrics
const distanceDelta = Math.abs(
googleResult.distance - mapatResult.distance
);
const durationDelta = Math.abs(
googleResult.duration - mapatResult.duration
);
console.log({
route: `${origin} -> ${destination}`,
googleDistance: googleResult.distance,
mapatDistance: mapatResult.distance,
distanceDeltaPercent: ((distanceDelta / googleResult.distance) * 100).toFixed(1),
googleDuration: googleResult.duration,
mapatDuration: mapatResult.duration,
durationDeltaPercent: ((durationDelta / googleResult.duration) * 100).toFixed(1),
});
// Use MapAtlas result in production, Google as validation
return mapatResult;
}
الخطوة 2: إعداد أعلام الميزات
استخدم علم ميزة للتحكم في الـ API التي يستدعيها تطبيقك. يتيح هذا التراجع الفوري عند ظهور مشكلات:
const useMapAtlasDirections = process.env.FEATURE_MAPATLAS_DIRECTIONS === "true";
const directions = useMapAtlasDirections
? await fetchMapAtlasDirections(origin, destination)
: await fetchGoogleDirections(origin, destination);
الخطوة 3: رصد معدلات الأخطاء
بعد التحويل، راقب:
- معدلات أخطاء HTTP 4xx/5xx من الـ API الجديدة
- زيادات في زمن الاستجابة (قد يكون للمزود الجديد أوقات استجابة مختلفة)
- الحقول المفقودة في الاستجابات التي تعتمد عليها واجهة المستخدم
- دقة المسار لحالات الحافة (مسارات العبّارات والطرق المدفوعة والمناطق المقيدة)
الخطوة 4: اختبار التحميل قبل الإطلاق
إن كان تطبيقك يتعامل مع ذروات مفاجئة كمنصة لوجستية خلال التوزيع الصباحي، اختبر الـ API الجديدة بضعف حجمك في الذروة. تقدم MapAtlas بيئة sandbox لاختبار التحميل دون تكبد فواتير الإنتاج.
الجدول الزمني: ما يجب فعله هذا الأسبوع
إن لم تبدأ الترحيل بعد، فهذا هو ترتيب الأولويات:
- هذا الأسبوع: راجع قاعدة الكود. حدد كل استخدام للخدمات الأربع الموقوفة.
- الأسبوع 2: أنشئ حساباً في MapAtlas واحصل على مفاتيح API. تغطي الطبقة المجانية 10,000 طلب للاختبار.
- الأسبوع 3: نفِّذ البديل للخدمة الأكثر أهمية وعادةً تكون DirectionsService.
- الأسبوع 4: رحِّل الخدمات المتبقية وأجرِ اختبارات موازية.
- قبل موعد مايو النهائي: حوِّل الخدمات وأزل اعتماديات Google API وحدِّث الفوترة.
الترحيل ليس اختيارياً. بعد مايو 2026، ستُزال الفئات الموقوفة من Maps JavaScript API وسيُطلق أي كود يشير إليها أخطاء في وقت التشغيل. ابدأ الآن وسيتسع وقتك للاختبار الصحيح. انتظر حتى الأسبوع الأخير وستجد نفسك تُرمِّم الإنتاج تحت الضغط.
لدعم الترحيل، تقدم MapAtlas مساعدة مباشرة للمطورين. تواصل من خلال صفحة الاتصال أو ابدأ بـ توثيق API لتقييم واجهات API البديلة بحسب متطلباتك.
الأسئلة الشائعة
متى تدخل قرارات إيقاف Google Maps API حيز التنفيذ بالضبط؟
جرى إيقاف Heatmap Layer وDrawing Library في أغسطس ومايو 2025 على التوالي، مع تحديد موعد الإزالة في مايو 2026. أما DirectionsService وDistanceMatrixService فقد أُوقفا في 25 فبراير 2026، وكذلك بموعد إزالة مستهدف في مايو 2026. بعد الإزالة، ستُعيد استدعاءات API لهذه الخدمات أخطاءً.
هل سيظل مفتاح Google Maps JavaScript API الخاص بي يعمل بعد مايو 2026؟
سيظل مفتاح API يعمل للخدمات غير الموقوفة. غير أن أي كود يستدعي DirectionsService أو DistanceMatrixService أو Heatmap Layer أو Drawing Library عبر Maps JavaScript API سيتوقف عن العمل. تحتاج إلى ترحيل تلك الاستدعاءات المحددة إلى واجهات API البديلة قبل الموعد النهائي.
ما هو أرخص بديل لـ Google Maps Directions API؟
تقدم MapAtlas واجهة Directions API بتكلفة أقل بنحو 70% من مكافئها في Google. تتضمن الطبقة المجانية 10,000 طلب شهرياً، وهو ما يكفي لتطوير الترحيل واختباره. تشمل البدائل الأخرى Mapbox وHERE وOpenRouteService، لكن MapAtlas توفر تنسيق الطلب والاستجابة الأقرب إلى Google مما يقلل جهد الترحيل.
هل يمكنني الترحيل بشكل تدريجي أم يجب التحويل دفعة واحدة؟
يمكنك الترحيل بشكل تدريجي. كل خدمة موقوفة مستقلة، فيمكنك استبدال DirectionsService أولاً ثم DistanceMatrixService ثم مكتبات التصور. هذا هو النهج الموصى به فعلاً لأنه يتيح اختبار كل بديل بشكل منفصل قبل الانتقال إلى التالي.
هل تدعم MapAtlas نقاط الطريق وأوضاع السفر نفسها التي تدعمها Google DirectionsService؟
نعم. تدعم MapAtlas Directions API أوضاع القيادة والمشي وركوب الدراجات، إضافة إلى نقاط الطريق المتوسطة. تنسيق الطلب مختلف قليلاً (نقطة نهاية RESTful مقابل فئة JavaScript)، لكن الوظيفة الأساسية وهي التوجيه متعدد المحطات مع اختيار وضع السفر متكافئة.

