> ## Content Index
> Fetch the complete content index at: https://arabroot.io/llms.txt
> Use this file to discover other available public pages before exploring further.

# كيف تبني تطبيق خرائط وملاحة مثل هدهد وGoogle Maps؟
- URL: https://arabroot.io/articles/كيف-تبني-تطبيق-خرائط-وملاحة-مثل-هدهد-و-google-maps/
- Published: 2026-10-09T14:30:00.000Z
- Updated: 2026-10-14T07:41:32.000Z
- Description: ما الذي يقف خلف كل ميزة في تطبيقات الخرائط، وكيف تبني نسخة أولى منها بأدوات مفتوحة المصدر وبيانات مرخصة بشكل صحيح.
- Author: فريق عرب رووت
- Tags: الأدلة التقنية, مجالات متخصصة, تطبيقات لفريقك, السيادة الرقمية والامتثال, الاستضافة الذاتية

في المقالات الأربعة السابقة من هذه المرحلة بنينا القطع واحدة بعد الأخرى: [الخريطة من سيرفرك](https://arabroot.io/articles/%D8%AE%D8%B1%D8%A7%D8%A6%D8%B7%D9%83-%D8%B9%D9%84%D9%89-%D8%B3%D9%8A%D8%B1%D9%81%D8%B1%D9%83-%D9%85%D8%B9-openstreetmap-%D9%88-maplibre/)، ثم [البحث عن العناوين](https://arabroot.io/articles/%D8%A7%D9%84%D8%A8%D8%AD%D8%AB-%D8%B9%D9%86-%D8%A7%D9%84%D8%B9%D9%86%D8%A7%D9%88%D9%8A%D9%86-geocoding-%D9%85%D8%B9-nominatim/)، ثم [أقصر طريق وزمن الوصول](https://arabroot.io/articles/%D8%AD%D8%B3%D8%A7%D8%A8-%D8%A7%D9%82%D8%B5%D8%B1-%D8%B7%D8%B1%D9%8A%D9%82-%D9%88%D8%B2%D9%85%D9%86-%D8%A7%D9%84%D9%88%D8%B5%D9%88%D9%84-%D9%85%D8%B9-osrm-%D9%88-valhalla/)، ثم [كيف تعرف الخرائط الزحام](https://arabroot.io/articles/%D9%83%D9%8A%D9%81-%D8%AA%D8%B9%D8%B1%D9%81-%D8%A7%D9%84%D8%AE%D8%B1%D8%A7%D8%A6%D8%B7-%D8%A7%D9%84%D8%B2%D8%AD%D8%A7%D9%85-%D9%88%D8%B3%D8%B1%D8%B9%D8%A9-%D8%A7%D9%84%D8%B7%D8%B1%D9%8A%D9%82/). والسؤال الذي يأتي بعد ذلك من كل فريق تقني رأى هذه القطع تعمل: هل نستطيع أن نبني تطبيق خرائط وملاحة كاملاً مثل Google Maps أو Waze أو هدهد؟ والإجابة الصادقة أن القطع التقنية كلها متاحة بأدوات مفتوحة المصدر، وأن ما يفصل نسخة تجريبية عن تطبيق يعتمد عليه الناس كل صباح ليس الكود، وإنما البيانات والمستخدمون وفريق التشغيل، وهذا ما سوف نفصله.

وهذا هو المقال الخامس في مرحلة [الخدمات المتخصصة: الخرائط والبث المباشر ومعالجة الوسائط](https://arabroot.io/start/#stage-special)، وسوف نناقش فيه ما يلي:

- ما هو هدهد، وما الذي يقدمه بحسب صفحاته الرسمية فقط.
- ميزات تطبيقات الخرائط الثلاثة، وما الذي يجب أن يقف خلف كل ميزة.
- المعمارية الكاملة بأدوات مفتوحة المصدر: المكونات ونموذج البيانات والواجهات وتقدير السعة ومراحل النمو، وماذا يحدث عندما يتعطل كل جزء، ثم تفاصيل البيانات والمربعات والبحث والمسارات والملاحة على الهاتف والزحام.
- أصعب مشكلة في المشروع كله: بيانات الأماكن Points of Interest، مع فحص صغير لتغطيتها في الرياض.
- الرخص عندما تخلط بياناتك ببيانات OpenStreetMap، والفريق، وخطة نسخة أولى MVP.

## ما هو هدهد؟

هدهد تطبيق خرائط وملاحة سعودي، والناشر في [صفحته على App Store](https://apps.apple.com/sa/app/id6477774492?ref=arabroot.io) هو شركة Hudhud Information Technology Company، والتطبيق بالعربية والإنجليزية، وتاريخ أول إصدار على المتجر كما تعيده [واجهة البحث في المتجر نفسه](https://itunes.apple.com/lookup?id=6477774492&country=sa&ref=arabroot.io) هو 8 مايو 2025، وعنوان الشركة على [موقعها الرسمي](https://hudhud.sa/ar/?ref=arabroot.io) في حي الرائد بالرياض. ولن نكتب عن هدهد إلا ما يذكره موقعه وصفحته في المتجر، والميزات المعلنة هناك هي:

- توجيه خطوة بخطوة يحسب الزحام والإغلاقات والحوادث، ويستخدم الرحلات السابقة للمستخدم في اختيار الطريق.
- إرشاد المسارات Lane Guidance وصور إرشادية للتقاطعات الصعبة تظهر في الوقت المناسب.
- تنبيه قبل كاميرات المخالفات للسرعة والحزام والجوال، وتنبيهات لحظية عن المطبات والتحويلات والزحام والحوادث.
- بلاغات من المستخدمين، وإضافة الأماكن المفقودة من الخريطة.
- دعم CarPlay وAndroid Auto، وتجول افتراضي بصور بانورامية من مستوى الشارع، وعرض المداخل والعنوان الوطني ومسارات المترو، وعرض بأبعاد ثلاثية.

والأهم لموضوعنا هو [بوابة المطورين في هدهد](https://developers.hudhud.sa/?ref=arabroot.io)، لأنها تكشف ما بنته الشركة خلف التطبيق: واجهات للخريطة الأساسية والصور الفضائية والخرائط الثابتة والتجول الافتراضي، والبحث عن العناوين في الاتجاهين مع دعم العناوين السعودية والرموز البريدية القصيرة، والبحث عن الأماكن والإكمال التلقائي وتفاصيل المكان، والاتجاهات مع زمن الوصول بحسب الزحام حتى 25 نقطة توقف، ومصفوفة المسافات، وتحسين المسارات، وحزمة ملاحة بتعليمات صوتية بالعربية والإنجليزية. وتذكر الصفحة نفسها أن البيانات تجمع بنظام مسح ليزري متنقل LiDAR من نوع CHCNAV AU20 بسرعة أربعة ملايين نقطة في الثانية، مع تصوير بانورامي بدقة 72 ميغابكسل، ومعالجة بنماذج ذكاء اصطناعي مطورة داخلياً. أي أن الشركة لم تكتف بالبيانات المفتوحة، وإنما تجمع بياناتها بنفسها في الشارع، وهذه هي النقطة التي سوف نعود إليها في كل قسم.

## الميزات وما يقف خلفها

إذا وضعت Google Maps وWaze وهدهد بجانب بعض فسوف تجد القائمة نفسها تقريباً بتركيز مختلف: Google Maps تبدأ من الأماكن والبحث، فتسمح للمستخدم [بكتابة التقييمات](https://support.google.com/maps/answer/6230175?ref=arabroot.io) و[إضافة مكان مفقود](https://support.google.com/maps/answer/6320846?ref=arabroot.io)، وتعرض [طبقة الزحام](https://support.google.com/maps/answer/3093389?ref=arabroot.io)، وتتيح [الخرائط دون اتصال](https://support.google.com/maps/answer/6291838?ref=arabroot.io) مع ملاحة بالسيارة إذا كان المسار كله داخل المنطقة المحملة، وبلا زحام ولا مسارات بديلة في هذه الحالة كما تقول صفحة المساعدة. وWaze يبدأ من السائق وبلاغاته كما شرحنا في مقال الزحام. وهدهد يبدأ من الملاحة المحلية: الكاميرات والمطبات والعنوان الوطني والتقاطعات المحلية. والصورة التالية تبين كل ميزة وما يجب أن يوجد خلفها:

![هاتف في الوسط عليه خريطة ومسار وتعليمة انعطاف وزمن وصول، وعلى يساره ثماني ميزات يراها المستخدم: الخريطة بالعربية والإنجليزية، والبحث، والأماكن مع الساعات والتقييمات والصور، والمسار والبدائل، والملاحة خطوة بخطوة والصوت وCarPlay، والزحام وزمن الوصول، والبلاغات، والخرائط دون اتصال. وعلى يمينه ما يجب بناؤه لكل ميزة: مسار بيانات وسيرفر مربعات، ومحرك بحث مع العنوان الوطني، وقاعدة أماكن وهي الأصعب والأغلى، ومحرك مسارات، وحزمة ملاحة على الهاتف مثل Ferrostar، ونظام بيانات مسبار يحتاج ملايين المستخدمين، ونظام بلاغات مع درجات ثقة وفريق تشغيل، وحزم مناطق تحمل على الجهاز](https://arabroot.io/content/images/2026/10/build-map-app-01-features.webp)

كل ميزة تبدو بسيطة على الشاشة، وخلفها نظام كامل، والبرتقالي منها يحتاج بيانات ومستخدمين وليس كوداً فقط

ولاحظ أن خمس قطع من الثماني لها أدوات مفتوحة المصدر جاهزة شرحناها أو سوف نشرحها، أما الثلاث الباقية، الأماكن والزحام والبلاغات، فهي ما يجعل الناس يفتحون تطبيقاً ويتركون آخر، ولا توجد أداة تثبتها فتحصل عليها.

## المعمارية الكاملة: كيف تتصل الأجزاء ببعضها؟

الصورة التالية تبين معمارية مقترحة من أربع طبقات: مصادر البيانات، ثم بناء دوري يحولها إلى ما تحتاجه الخدمات، ثم الخدمات خلف واجهة API واحدة، ثم التطبيقات:

![مخطط من أربعة أعمدة: البيانات وفيها OpenStreetMap برخصة ODbL وOverture Maps للأماكن برخصة CDLA-P 2.0 مع معرفات GERS والعنوان الوطني من واجهة سبل وبياناتك الخاصة وبلاغات المستخدمين ونقاط المسبار. ثم البناء المجدول: دمج المصادر وفحص الجودة، وPlanetiler إلى PMTiles، وفهرس البحث، ورسم المسارات البياني، ومهمة الزحام كل خمس دقائق، وحزم المناطق دون اتصال. ثم الخدمات: بوابة API للمفاتيح والحصص والسجلات، والمربعات، والبحث، وواجهة الأماكن على PostgreSQL مع PostGIS، والمسارات، والزحام، والبلاغات والمراجعة، واستقبال نقاط المسبار. ثم التطبيقات: تطبيق الهاتف مع MapLibre Native وFerrostar، والويب مع MapLibre GL JS، والسيارة مع CarPlay وAndroid Auto](https://arabroot.io/content/images/2026/10/build-map-app-02-architecture.webp)

البيانات تبنى على جدول ثابت، والخدمات تقرأ ما بني، والهاتف يرسل نقاطه لتعود زحاماً

والصورة أعلاه تبين من أين تأتي البيانات وكيف تبنى، أما أثناء العمل فتحتاج أن تعرف أي طلب يذهب إلى أي خدمة، وأين تحفظ كل معلومة، وأي عمل ينتظره المستخدم وأي عمل يحدث في الخلفية دون أن ينتظره أحد. والصورة التالية تبين النظام نفسه أثناء العمل في خمسة أعمدة: الكلاينت Clients، ثم الحافة Edge، ثم الخدمات Services، ثم البيانات Data، ثم المهام المجدولة Jobs والمزودين الخارجيين:

![خمسة أعمدة: في الكلاينت تطبيق الهاتف مع MapLibre Native وFerrostar وCarPlay، والويب مع MapLibre GL JS. وفي الحافة CDN للمربعات بطلبات أجزاء الملف، وبوابة API للمفاتيح والحصص والسجلات. وفي الخدمات البحث والإكمال التلقائي بال Photon أو Pelias أو Nominatim، وواجهة الأماكن، وواجهة البلاغات، ومحرك المسارات OSRM أو Valhalla والرسم البياني في الذاكرة، واستقبال نقاط المسبار. وفي البيانات ناتج البناء من ملف PMTiles والرسم البياني، وفهرس البحث، وPostgreSQL مع PostGIS للأماكن والبلاغات، وسجل نقاط المسبار الذي يحفظ مدة قصيرة. وفي العمود الأخير OpenStreetMap وOverture كمصدر خارجي، والبناء المجدول بال Planetiler وosrm-extract، وواجهة العنوان الوطني من سبل كمزود خارجي، ومهمة الزحام كل خمس دقائق التي ترسل سرعات القطع إلى محرك المسارات. الأسهم الخضراء للمربعات، والرمادية طلبات متزامنة، والبنفسجية المتقطعة بيانات تبنى أو تجمع في الخلفية، والبرتقالية مصدر أو مزود خارجي](https://arabroot.io/content/images/2026/10/build-map-app-05-system-architecture.webp)

المربعات لا تمر بأي خدمة، والزحام يصل إلى محرك المسارات من مهمة في الخلفية وليس من طلب المستخدم

وفي الصورة أعلاه لاحظ ثلاث قواعد: أن أكثر الطلبات عدداً، وهي المربعات، لا تمر بأي خدمة كتبتها، فهي أجزاء من ملف PMTiles ثابت تخدمه ال CDN كما في [مقال الخريطة](https://arabroot.io/articles/%D8%AE%D8%B1%D8%A7%D8%A6%D8%B7%D9%83-%D8%B9%D9%84%D9%89-%D8%B3%D9%8A%D8%B1%D9%81%D8%B1%D9%83-%D9%85%D8%B9-openstreetmap-%D9%88-maplibre/#pmtiles). وأن محرك المسارات لا يقرأ قاعدة بيانات أثناء الطلب، وإنما رسماً بيانياً Graph محملاً في الذاكرة، والزحام يصل إليه من مهمة كل خمس دقائق تكتب السرعات ثم تطبقها، فلا ينتظر السائق أي حساب للزحام. وأن البلاغات واستقبال النقاط ملونة بلون مختلف، لأنها الأجزاء التي لا تعمل إلا بمستخدمين وفريق تشغيل، كما سنرى في [قسم الزحام](https://arabroot.io/articles/%D9%83%D9%8A%D9%81-%D8%AA%D8%A8%D9%86%D9%8A-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82-%D8%AE%D8%B1%D8%A7%D8%A6%D8%B7-%D9%88%D9%85%D9%84%D8%A7%D8%AD%D8%A9-%D9%85%D8%AB%D9%84-%D9%87%D8%AF%D9%87%D8%AF-%D9%88-google-maps/#traffic).

### المكونات ومسؤولية كل منها

| المكون                  | مسؤوليته                                                                                    | أداة مفتوحة المصدر مقترحة                                                                                                                                                                                                                  | لماذا هي                                                                                                                                                                                                                                                                                                 |
| ----------------------- | ------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| تطبيق الهاتف            | رسم الخريطة، والملاحة خطوة بخطوة، وإرسال نقاط المسبار دفعات                                 | [MapLibre Native](https://github.com/maplibre/maplibre-native?ref=arabroot.io) (BSD-2-Clause) و[Ferrostar](https://github.com/stadiamaps/ferrostar?ref=arabroot.io) (BSD-3-Clause)                                                         | حلقة الملاحة تعمل على الهاتف، ولا تعود إلى السيرفر إلا عند الخروج عن المسار                                                                                                                                                                                                                              |
| الويب                   | الخريطة والبحث والمسار في المتصفح                                                           | [MapLibre GL JS](https://github.com/maplibre/maplibre-gl-js?ref=arabroot.io) (BSD-3-Clause)                                                                                                                                                | نفس المكتبة التي استخدمناها في مقال الخريطة، وتقرأ PMTiles                                                                                                                                                                                                                                               |
| المربعات وال CDN        | تخزين المربعات المتجهة وخدمتها بطلبات أجزاء الملف Range Requests                            | [PMTiles](https://github.com/protomaps/PMTiles?ref=arabroot.io) (BSD-3-Clause) على nginx أو أي CDN                                                                                                                                         | ملف واحد ثابت، فلا يوجد خادم مربعات تشغله وتراقبه                                                                                                                                                                                                                                                        |
| البناء المجدول          | تحويل البيانات إلى مربعات ورسم بياني للمسارات وفهرس بحث                                     | [Planetiler](https://github.com/onthegomap/planetiler?ref=arabroot.io) (Apache-2.0) وأدوات المحرك نفسه                                                                                                                                     | يبني مربعات مدينة أو دولة على سيرفر واحد                                                                                                                                                                                                                                                                 |
| بوابة ال API Gateway    | مفاتيح ال API، والحصص Quotas لكل مفتاح، والسجلات                                            | [Apache APISIX](https://github.com/apache/apisix?ref=arabroot.io) (Apache-2.0)                                                                                                                                                             | التحقق بالمفتاح وعد الطلبات لكل مفتاح موجودان فيه كإضافات Plugins جاهزة                                                                                                                                                                                                                                  |
| البحث والإكمال التلقائي | العناوين والأماكن أثناء الكتابة، والبحث العكسي من الإحداثيات                                | [Photon](https://github.com/komoot/photon?ref=arabroot.io) (Apache-2.0) أو [Pelias](https://github.com/pelias/pelias?ref=arabroot.io) (MIT)، وNominatim للعناوين                                                                           | Photon مبني للإكمال السريع، وPelias يجمع أكثر من مصدر                                                                                                                                                                                                                                                    |
| واجهة الأماكن والبلاغات | تفاصيل المكان وساعاته، والإضافة والتعديل، وبلاغات السائقين بدرجات ثقة                       | كودك على [PostgreSQL](https://www.postgresql.org/about/licence/?ref=arabroot.io) مع [PostGIS](https://github.com/postgis/postgis?ref=arabroot.io) (GPL-2.0)                                                                                | الاستعلام بالمسافة والمضلعات بفهرس مكاني، وهذا ما يحتاجه «أقرب صيدلية»                                                                                                                                                                                                                                   |
| محرك المسارات           | المسار وزمن الوصول والخطوات، وإعادة التوجيه                                                 | [OSRM](https://github.com/Project-OSRM/osrm-backend?ref=arabroot.io) (BSD-2-Clause) أو [Valhalla](https://github.com/valhalla/valhalla?ref=arabroot.io) (MIT) أو [GraphHopper](https://github.com/graphhopper/graphhopper?ref=arabroot.io) | قارناها في مقال المسارات، وOSRM يطبق سرعات جديدة دون بناء الرسم من الصفر                                                                                                                                                                                                                                 |
| استقبال نقاط المسبار    | استقبال دفعات النقاط من الهواتف، وحذف بدايات الرحلات ونهاياتها                              | كودك، ومعه [Apache Kafka](https://github.com/apache/kafka?ref=arabroot.io) (Apache-2.0) كسجل للنقاط                                                                                                                                        | النقاط تحفظ أياماً قليلة، وتقرؤها مهمة الزحام بالترتيب                                                                                                                                                                                                                                                   |
| مهمة الزحام             | مطابقة النقاط مع الطريق، وسرعة لكل قطعة مرت عليها 5 مركبات على الأقل، ثم تطبيقها على المحرك | كود مقال الزحام مع osrm-customize                                                                                                                                                                                                          | نفس الملفات التي في [تجربة الزحام](https://arabroot.io/articles/%D9%83%D9%8A%D9%81-%D8%AA%D8%B9%D8%B1%D9%81-%D8%A7%D9%84%D8%AE%D8%B1%D8%A7%D8%A6%D8%B7-%D8%A7%D9%84%D8%B2%D8%AD%D8%A7%D9%85-%D9%88%D8%B3%D8%B1%D8%B9%D8%A9-%D8%A7%D9%84%D8%B7%D8%B1%D9%8A%D9%82/#lab)                                    |
| المصادر الخارجية        | الطرق والأماكن المفتوحة، والعنوان الوطني                                                    | OpenStreetMap وOverture، وواجهة العنوان الوطني من سبل بحساب                                                                                                                                                                                | لكل منها رخصة أو شروط استخدام، كما في [قسم الرخص](https://arabroot.io/articles/%D9%83%D9%8A%D9%81-%D8%AA%D8%A8%D9%86%D9%8A-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82-%D8%AE%D8%B1%D8%A7%D8%A6%D8%B7-%D9%88%D9%85%D9%84%D8%A7%D8%AD%D8%A9-%D9%85%D8%AB%D9%84-%D9%87%D8%AF%D9%87%D8%AF-%D9%88-google-maps/#licensing) |

### نموذج البيانات

هذا المقال ليس فيه تجربة لها قاعدة بيانات خاصة، لكن مقالات المرحلة السابقة فيها أسماء نبني عليها: ملف `riyadh.osm.pbf` كمصدر، وملف مربعات PMTiles للمدينة، وملفات `riyadh.osrm` للمحرك، وملف `speeds.csv` الذي تكتبه مهمة الزحام بسطر لكل قطعة. أما ما يحتاج قاعدة بيانات فهو الأماكن والبلاغات، وهذا شكلها في PostgreSQL مع PostGIS:

```sql
-- Places and reports (PostgreSQL + PostGIS). Your places are a separate layer: see the licensing section.
CREATE EXTENSION IF NOT EXISTS postgis;

CREATE TABLE places (
  id            bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
  source        text NOT NULL CHECK (source IN ('survey', 'owner', 'overture')),   -- no OSM places in this layer
  gers_id       text UNIQUE,                       -- Overture GERS id when the place came from Overture
  name_ar       text NOT NULL,
  name_en       text,
  category      text NOT NULL,                     -- pharmacy, restaurant, cafe ...
  geom          geometry(Point, 4326) NOT NULL,
  opening_hours text,                              -- same syntax as the OSM opening_hours tag
  phone         text,
  short_address text,                              -- national address short code: 4 letters + 4 digits
  check_date    date,                              -- when someone last confirmed the place still exists
  updated_at    timestamptz NOT NULL DEFAULT now()
);
CREATE INDEX places_geom ON places USING gist (geom);

CREATE TABLE reports (
  id          bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
  request_key text NOT NULL UNIQUE,                -- Idempotency-Key from the app: one tap, one report
  user_id     uuid NOT NULL,
  kind        text NOT NULL CHECK (kind IN ('accident', 'closure', 'camera', 'speed_bump', 'missing_place', 'wrong_place')),
  geom        geometry(Point, 4326) NOT NULL,
  trust       real NOT NULL DEFAULT 0,             -- grows when other drivers confirm it
  status      text NOT NULL DEFAULT 'pending' CHECK (status IN ('pending', 'live', 'expired', 'rejected')),
  created_at  timestamptz NOT NULL DEFAULT now(),
  expires_at  timestamptz
);
CREATE INDEX reports_geom ON reports USING gist (geom);

-- Probe stream (Kafka topic probes), kept a few days:
--   {"device": "<id that changes every day>", "ts": 1791509936, "lat": 24.70, "lon": 46.68, "speed": 13.9, "heading": 90}
-- Traffic job output, every 5 minutes, as in the traffic lab (segments seen by K = 5 vehicles or more):
--   speeds.csv: from_osm_id,to_osm_id,speed_km_h
```

في الإعداد أعلاه لاحظ التالي:

- القيد على `source` لا يسمح بأماكن من OpenStreetMap في هذا الجدول، وهذا تطبيق في قاعدة البيانات للقرار الذي شرحناه في [قسم الرخص](https://arabroot.io/articles/%D9%83%D9%8A%D9%81-%D8%AA%D8%A8%D9%86%D9%8A-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82-%D8%AE%D8%B1%D8%A7%D8%A6%D8%B7-%D9%88%D9%85%D9%84%D8%A7%D8%AD%D8%A9-%D9%85%D8%AB%D9%84-%D9%87%D8%AF%D9%87%D8%AF-%D9%88-google-maps/#licensing): طبقة الأماكن التي تملكها لا تخلط بأماكن OpenStreetMap، فلا تصبح قاعدة مشتقة.
- العمود `check_date` هو نفس الوسم الذي وجدناه على 71 عنصراً فقط في الرياض، وهنا يصبح عموداً تملؤه المراجعة الميدانية أو صاحب المكان.
- معرف الجهاز في نقاط المسبار يتغير كل يوم، والنقاط تحذف بعد أيام، كما شرحنا في [نقاط GPS بيانات شخصية](https://arabroot.io/articles/%D9%83%D9%8A%D9%81-%D8%AA%D8%B9%D8%B1%D9%81-%D8%A7%D9%84%D8%AE%D8%B1%D8%A7%D8%A6%D8%B7-%D8%A7%D9%84%D8%B2%D8%AD%D8%A7%D9%85-%D9%88%D8%B3%D8%B1%D8%B9%D8%A9-%D8%A7%D9%84%D8%B7%D8%B1%D9%8A%D9%82/#privacy).

### الواجهات الرئيسية

| الطريقة والمسار                        | ما تفعله                                                        | ملاحظات                                                                 |
| -------------------------------------- | --------------------------------------------------------------- | ----------------------------------------------------------------------- |
| GET /tiles/riyadh.pmtiles              | جزء من ملف المربعات بترويسة Range                               | من ال CDN، والملف يتغير اسمه مع كل بناء حتى لا يختلط القديم بالجديد     |
| GET /search?q=&lat=&lon=               | الإكمال التلقائي أثناء الكتابة، والأقرب إلى موقع المستخدم أولاً | أكثر واجهة طلباً بعد المربعات، فحصة كل مفتاح تحسب عليها                 |
| GET /reverse?lat=&lon=                 | العنوان من الإحداثيات                                           | يستخدم عند الضغط على الخريطة وعند حفظ مكان                              |
| GET /places/{id}                       | تفاصيل المكان وساعاته وهاتفه                                    | تحفظ في Cache لدقائق                                                    |
| GET /route?from=&to=&alternatives=true | المسار والبدائل والخطوات وزمن الوصول، بصيغة يقرؤها Ferrostar    | إعادة التوجيه هي نفس الطلب من موقع السائق الحالي                        |
| POST /reports                          | بلاغ عن حادث أو إغلاق أو مكان مفقود                             | ترويسة Idempotency-Key، وحد للبلاغات لكل مستخدم                         |
| POST /places/{id}/edits                | اقتراح تعديل على مكان، يذهب إلى المراجعة                        | لا يغير المكان مباشرة، والسبب أن أول تعديل كاذب يرسل الناس إلى محل مغلق |
| POST /probes                           | دفعة نقاط GPS كل 30 ثانية أثناء الملاحة                         | يرد 202 فوراً، وإذا فشل الإرسال يحتفظ الهاتف بالنقاط ويعيدها            |

### تقدير السعة

لنفرض تطبيقاً في مدينة واحدة مثل الرياض، وكل رقم هنا افتراض نكتبه صراحة، إلا ما أخذناه من مختبرات المرحلة، فغيره بأرقامك:

- افتراض: 50,000 مستخدم نشط في اليوم، يفتح كل منهم الخريطة مرتين، و15% من الجلسات تقع في ساعة الذروة.
- افتراض: كل جلسة تطلب 40 مربعاً، ومتوسط المربع 30 كيلوبايت، وبحثاً واحداً من 6 طلبات إكمال تلقائي.
- افتراض: 30% من الجلسات تطلب مساراً، و10% منها تعيد التوجيه مرة.
- افتراض: كل مستخدم يقود مع الملاحة 20 دقيقة في اليوم، و3,000 سائق في نفس الوقت في الذروة، ونقطة GPS كل 5 ثوان بحجم 50 بايت، ودفعة كل 30 ثانية.
- من المختبرات: ملف مربعات الرياض 17 ميغابايت، ومحرك المسارات قرابة 100 ميغابايت من الذاكرة، وفي مستطيل الرياض 14,630 عنصراً من أنواع الأماكن في OpenStreetMap.

```text
map sessions          50,000 x 2                          = 100,000 per day, 15,000 in the peak hour
tile requests         15,000 x 40 / 3600 s                = ~167 requests/s at the peak (4 million per day)
tile bandwidth        167 x 30 KB                         = ~5 MB/s = ~40 Mbit/s at the peak, ~120 GB per day
autocomplete          15,000 x 6 / 3600 s                 = ~25 requests/s at the peak
routes                15,000 x 30% x 1.1 / 3600 s         = ~1.4 requests/s at the peak (33,000 per day)
probe points          3,000 drivers / 5 s                 = 600 points/s at the peak
probe uploads         3,000 / 30 s                        = 100 requests/s at the peak
probe storage         50,000 x 1,200 s / 5 s x 50 bytes   = 12 million points = ~600 MB per day, ~4.2 GB for 7 days
places                even 500,000 places x ~1 KB         = ~0.5 GB in PostgreSQL
```

ولاحظ أن الخادم الذي يتعب أولاً هو ال CDN والبحث، وأن المسارات أقل من طلبين في الثانية لمدينة كاملة، وأن قاعدة الأماكن كلها أقل من جيجابايت، فالمشكلة التي لا تحلها السيرفرات هي ما رأيناه في [قسم الأماكن](https://arabroot.io/articles/%D9%83%D9%8A%D9%81-%D8%AA%D8%A8%D9%86%D9%8A-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82-%D8%AE%D8%B1%D8%A7%D8%A6%D8%B7-%D9%88%D9%85%D9%84%D8%A7%D8%AD%D8%A9-%D9%85%D8%AB%D9%84-%D9%87%D8%AF%D9%87%D8%AF-%D9%88-google-maps/#poi): من أين تأتي هذه الأماكن الخمسمئة ألف، ومن يتأكد كل شهر أنها ما زالت مفتوحة. ولاحظ أيضاً أن 600 نقطة في الثانية من 3,000 سائق رقم صغير لقاعدة البيانات، لكنه كما رأينا في مقال الزحام قد يغطي جزءاً صغيراً فقط من شوارع المدينة، لأن كل قطعة تحتاج 5 مركبات مختلفة قبل أن تظهر سرعتها.

### مراحل النمو

| الجزء                | MVP: سيرفر أو اثنان                        | النمو: آلاف المستخدمين في نفس الوقت                | الحجم الكبير: ملايين المستخدمين                             |
| -------------------- | ------------------------------------------ | -------------------------------------------------- | ----------------------------------------------------------- |
| المربعات             | ملف PMTiles للمدينة على nginx              | CDN أمام الملف، وبناء أسبوعي                       | ملفات لكل منطقة، وتحديثات يومية، وحزم للعمل دون اتصال       |
| البحث                | Photon أو Nominatim واحد                   | نسخ خلف البوابة، وCache للاستعلامات الشائعة        | فهرس خاص يرتب بحسب ما يختاره الناس، ومعالجة للعربية الدارجة |
| المسارات             | OSRM واحد للمدينة بسرعات الطرق الفارغة     | نسختان أو أكثر، وتطبيق سرعات الزحام كل خمس دقائق   | مجموعات بحسب المنطقة، وزمن وصول بنموذج تعلم آلي             |
| الأماكن والبلاغات    | أماكنك فقط كطبقة مستقلة، والتعديل من فريقك | بلاغات بدرجات ثقة، وفريق مراجعة، واقتراح التعديلات | لوحة لأصحاب الأماكن، ومسح ميداني مستمر                      |
| نقاط المسبار والزحام | بلا زحام في البداية، أو من أسطولك فقط      | Kafka ومهمة الزحام كل خمس دقائق                    | الزحام التاريخي مع اللحظي، ونماذج توقع                      |

ولا تقم ببناء الزحام من مستخدمين لا تملكهم في اليوم الأول، فقد رأينا أن 393 سيارة في ساعة غطت 14% فقط من الشوارع، ولا تبدأ بالتوزيع على عدة مناطق، ولا بنموذج لزمن الوصول، والسبب أن كلاً منها يحتاج بيانات ومستخدمين لا يوجدون في البداية، وهذا هو ترتيب [خطة النسخة الأولى](https://arabroot.io/articles/%D9%83%D9%8A%D9%81-%D8%AA%D8%A8%D9%86%D9%8A-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82-%D8%AE%D8%B1%D8%A7%D8%A6%D8%B7-%D9%88%D9%85%D9%84%D8%A7%D8%AD%D8%A9-%D9%85%D8%AB%D9%84-%D9%87%D8%AF%D9%87%D8%AF-%D9%88-google-maps/#mvp) في آخر المقال.

### ماذا لو تعطل جزء؟

| ماذا لو تعطل...؟              | الأثر                                                            | كيف تخففه                                                                                                                                                                                                                                                                                                                        |
| ----------------------------- | ---------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| محرك المسارات                 | لا يستطيع أحد أن يطلب مساراً جديداً أو يعاد توجيهه               | نسختان على الأقل، ومن بدأ الملاحة يكمل لأن حلقة Ferrostar تعمل على الهاتف بالمسار الذي معه                                                                                                                                                                                                                                       |
| ملف السرعات من مهمة الزحام    | سطر خاطئ يوقف osrm-customize كله، فتتوقف السرعات عن التحديث بصمت | افحص الملف قبل التطبيق، وراقب رمز الخروج، وأبق آخر سرعات صحيحة، كما في [تجربة السطر الفارغ](https://arabroot.io/articles/%D9%83%D9%8A%D9%81-%D8%AA%D8%B9%D8%B1%D9%81-%D8%A7%D9%84%D8%AE%D8%B1%D8%A7%D8%A6%D8%B7-%D8%A7%D9%84%D8%B2%D8%AD%D8%A7%D9%85-%D9%88%D8%B3%D8%B1%D8%B9%D8%A9-%D8%A7%D9%84%D8%B7%D8%B1%D9%8A%D9%82/#break) |
| ال CDN أو ملف المربعات        | الخريطة فارغة في المناطق التي ليست في ذاكرة الهاتف               | نسختان من الملف على مصدرين، والهاتف يحتفظ بما شاهده من المربعات                                                                                                                                                                                                                                                                  |
| البحث                         | لا يجد المستخدم العنوان الذي يكتبه                               | نسخ متعددة، والأماكن الأخيرة والمحفوظة على الهاتف                                                                                                                                                                                                                                                                                |
| استقبال النقاط أو مهمة الزحام | الزحام يتوقف عند آخر قيمة ويصبح قديماً                           | بعد مدة محددة يعود المحرك إلى سرعات الطرق الفارغة بدل زحام قديم، والهاتف يعيد إرسال النقاط                                                                                                                                                                                                                                       |
| واجهة العنوان الوطني          | البحث بالعنوان المختصر يفشل                                      | احفظ النتائج بحسب شروط الخدمة، وارجع إلى البحث في بياناتك عند الفشل                                                                                                                                                                                                                                                              |

## تفاصيل كل طبقة

وفيما يلي تفصيل كل طبقة في المعمارية أعلاه: من أين تأتي البيانات، ثم المربعات والبحث والمسارات، ثم الملاحة على الهاتف، ثم الزحام والبلاغات.

### البيانات: OpenStreetMap وOverture

القاعدة هي [OpenStreetMap](https://www.openstreetmap.org/copyright?ref=arabroot.io) برخصة ODbL كما شرحنا في مقال الخريطة، وفيها الطرق والأسماء والحدود. والمصدر الثاني المهم هو [Overture Maps](https://overturemaps.org/?ref=arabroot.io)، وهو مشروع يجمع بيانات من شركات وجهات مختلفة في مخطط Schema واحد، ويعطي كل جسم معرفاً ثابتاً اسمه [GERS](https://docs.overturemaps.org/gers/?ref=arabroot.io) تستطيع أن تربط به بياناتك. والرخص تختلف بحسب الموضوع كما في [صفحة النسبة في توثيقه](https://docs.overturemaps.org/attribution/?ref=arabroot.io): الطرق Transportation والمباني والحدود برخصة ODbL لأنها مبنية على OpenStreetMap، والأماكن Places برخصة CDLA Permissive 2.0 لأغلب مصادرها، وبيانات Foursquare فيها برخصة Apache 2.0، ولكل مصدر نص نسبة يجب أن يظهر. والعناوين في السعودية مصدرها الرسمي هو [العنوان الوطني من البريد السعودي سبل](https://splonline.com.sa/ar/national-address-1/?ref=arabroot.io): رقم المبنى من أربعة أرقام، واسم الشارع، والرقم الإضافي من أربعة أرقام، والحي، والرمز البريدي من خمسة أرقام، والمدينة، مع عنوان مختصر من أربعة حروف وأربعة أرقام. وتوفر سبل [واجهة برمجة للعنوان الوطني](https://splonline.com.sa/ar/national-address-api?ref=arabroot.io) عبر [بوابة المطورين](https://api.address.gov.sa/?ref=arabroot.io)، وفي توثيقها البحث النصي والتحقق من العنوان وتحويله إلى إحداثيات والبحث عن الأماكن وقوائم المناطق والمدن والأحياء، وتحتاج حساباً في البوابة.

### المربعات والبحث والمسارات

- **المربعات:** [Planetiler](https://github.com/onthegomap/planetiler?ref=arabroot.io) يبني المربعات المتجهة من البيانات، وPMTiles تجعلها ملفاً واحداً على سيرفر ويب أو CDN كما في [مقال الخريطة](https://arabroot.io/articles/%D8%AE%D8%B1%D8%A7%D8%A6%D8%B7%D9%83-%D8%B9%D9%84%D9%89-%D8%B3%D9%8A%D8%B1%D9%81%D8%B1%D9%83-%D9%85%D8%B9-openstreetmap-%D9%88-maplibre/#pmtiles)، و[OpenMapTiles](https://github.com/openmaptiles/openmaptiles?ref=arabroot.io) مخطط Schema آخر مشهور لطبقات المربعات. وإذا جمعت بياناتك الخاصة فهي تدخل هنا كطبقات إضافية.
- **البحث:** Nominatim كما في [مقاله](https://arabroot.io/articles/%D8%A7%D9%84%D8%A8%D8%AD%D8%AB-%D8%B9%D9%86-%D8%A7%D9%84%D8%B9%D9%86%D8%A7%D9%88%D9%8A%D9%86-geocoding-%D9%85%D8%B9-nominatim/) جيد للعناوين، ولكنه لا يعطيك إكمالاً تلقائياً سريعاً أثناء الكتابة، و[Photon](https://github.com/komoot/photon?ref=arabroot.io) برخصة Apache-2.0 مبني لهذا الغرض، و[Pelias](https://github.com/pelias/pelias?ref=arabroot.io) برخصة MIT يجمع مصادر متعددة منها OpenStreetMap وملفات عناوين أخرى. وفي السعودية يحتاج البحث أن يفهم العنوان المختصر للعنوان الوطني، وقد رأينا في مقال Nominatim أن الهمزة والتاء المربوطة والأسماء الدارجة تحتاج معالجة في تطبيقك.
- **المسارات:** OSRM أو Valhalla أو [GraphHopper](https://github.com/graphhopper/graphhopper?ref=arabroot.io) كما قارنا في [مقال المسارات](https://arabroot.io/articles/%D8%AD%D8%B3%D8%A7%D8%A8-%D8%A7%D9%82%D8%B5%D8%B1-%D8%B7%D8%B1%D9%8A%D9%82-%D9%88%D8%B2%D9%85%D9%86-%D8%A7%D9%84%D9%88%D8%B5%D9%88%D9%84-%D9%85%D8%B9-osrm-%D9%88-valhalla/#engines)، وأي منها يكفي للنسخة الأولى، والفرق يظهر في الزحام ومناطق الوصول وتغيير التكلفة لكل مستخدم.

### الملاحة على الهاتف

المسار الذي يعيده المحرك قائمة خطوات، أما الملاحة فهي ما يحدث في الهاتف كل ثانية أثناء القيادة، والصورة التالية تبين هذه الحلقة:

![حلقة من أربع خطوات داخل الهاتف تقريباً مرة كل ثانية: نقطة GPS بالموقع والاتجاه والدقة، ثم ربطها بالمسار وحساب المسافة الباقية وزمن الوصول والخطوة الحالية، ثم التعليمة التالية والصوت مثل بعد 300 متر انعطف يميناً في المسافة المناسبة بالعربية، ثم سؤال هل خرج السائق عن المسار لعدة نقاط، فإذا خرج يرسل طلب إعادة توجيه إلى محرك المسارات على السيرفر الذي يعيد مساراً جديداً بصيغة Valhalla أو OSRM مع الخطوات والتعليمات الصوتية. وفي الأسفل حزمة الملاحة Ferrostar بلغة Rust وMapLibre Native ترسم الخريطة تحتها](https://arabroot.io/content/images/2026/10/build-map-app-03-navigation.webp)

الهاتف يتابع المسار بنفسه، ويعود إلى السيرفر فقط عندما يخرج السائق عنه

وكتابة هذه الحلقة بنفسك أصعب مما تبدو، لأن نقاط GPS تقفز في الأنفاق وبين الأبراج، والقرار «خرج عن المسار» إذا جاء مبكراً أزعج السائق بإعادة توجيه لا داعي لها، وإذا جاء متأخراً فاته المخرج التالي. والأداة المفتوحة لهذا هي [Ferrostar](https://github.com/stadiamaps/ferrostar?ref=arabroot.io) من Stadia Maps برخصة BSD، وبحسب [توثيقها](https://stadiamaps.github.io/ferrostar/?ref=arabroot.io) فنواتها مكتوبة بلغة Rust مع مكتبات لـ iOS وAndroid ومكونات ويب، وفيها واجهة ملاحة جاهزة مبنية على MapLibre كما في [صفحة المزودين](https://stadiamaps.github.io/ferrostar/vendors.html?ref=arabroot.io)، وتقرأ ردود Valhalla أو أي محرك يعيد صيغة OSRM كما في [صفحة مزودي المسارات](https://stadiamaps.github.io/ferrostar/route-providers.html?ref=arabroot.io)، وآخر إصدار عند كتابة المقال 0.57.0 في 22 سبتمبر 2026، والتوثيق يصف حالتها على الهاتف بأنها Beta مستخدمة في الإنتاج. والخريطة نفسها في التطبيق ترسمها [MapLibre Native](https://github.com/maplibre/maplibre-native?ref=arabroot.io) برخصة BSD-2-Clause، وهي النسخة المخصصة للهواتف من المكتبة التي استخدمناها في المتصفح.

والتعليمات الصوتية بالعربية نقطة يجب أن تفحصها بنفسك قبل أي وعد للعميل. ففي مقال المسارات رأينا أن ترجمة Valhalla الإصدار 3.9.1 ناقصة، وأن الجملة تخرج نصفها إنجليزي. وOSRM لا يكتب الجمل في الخادم، وإنما يعيد نوع المناورة واسم الشارع، والجمل تبنيها مكتبة [osrm-text-instructions](https://github.com/Project-OSRM/osrm-text-instructions?ref=arabroot.io) وفيها ملف عربي. فأخذنا ملف `route.json` من مختبر مقال المسارات، أي مسار المصمك والمطار، وطبقنا عليه المكتبة بالملف التالي:

```javascript
// ar.mjs: turn OSRM's steps into Arabic sentences with osrm-text-instructions
import { readFileSync } from 'node:fs';
import { createRequire } from 'node:module';
const require = createRequire(import.meta.url);
const instructions = require('osrm-text-instructions')('v5');
const route = JSON.parse(readFileSync('/w/route.json', 'utf8')).routes[0];
for (const step of route.legs[0].steps.slice(0, 6)) console.log(instructions.compile('ar', step));
```

```bash
docker run --rm --name lab-routing-oti -v "$PWD/oti:/w" -w /w node:22-alpine sh -c \
  'npm install --silent osrm-text-instructions >/dev/null 2>&1; npm ls osrm-text-instructions; node ar.mjs'
```

والمخرج سوف يكون كما يلي:

```text
w@ /w
`-- osrm-text-instructions@0.15.0

توجّه شرقاً
انعطف يسارا
انعطف يمينا على الإمام تركي بن عبدالله
اسلك مسار الدوران يسارا على الملك فيصل بن عبدالعزيز
اسلك مسار الدوران يمينا على الناصرية
اسلك مسار الدوران يسارا على شارع عبد العزيز بن مساعد بن جلوي
```

الجمل هنا عربية كاملة، وهذا أفضل من Valhalla، ولكن لاحظ أن الخطوات من الرابعة إلى السادسة في رد OSRM انعطافات عادية من نوع `turn` مع `left` أو `right`، والمكتبة كتبتها «اسلك مسار الدوران» كأن السائق يدخل دواراً، والسبب في [ملف الترجمة ar.json](https://github.com/Project-OSRM/osrm-text-instructions/blob/master/languages/translations/ar.json?ref=arabroot.io) نفسه: النص المقابل للإنجليزية «Turn left onto {way\_name}» مكتوب «اسلك مسار الدوران يسارا على {way\_name}»، أما الانعطاف دون اسم شارع فمترجم صحيحاً «انعطف يسارا». ولاحظ أيضاً أن الكتابة غير موحدة بين «شرقاً» و«يسارا»، وأن [آخر إصدار للمكتبة في سجل npm](https://registry.npmjs.org/osrm-text-instructions?ref=arabroot.io) هو 0.15.0 وآخر تعديل عليه في مارس 2024\. والسائق الذي يسمع «اسلك مسار الدوران» عند تقاطع عادي سوف يبحث عن دوار غير موجود، لذلك خطط لأن يراجع شخص يعرف كيف يتكلم السائقون في مدينتك كل جملة قبل أن تصل إلى الصوت، وقد طلب أحد مستخدمي هدهد في تقييمات App Store صوتاً بلهجة سعودية، أي أن المستخدم يلاحظ هذه التفاصيل.

### الزحام والبلاغات

شرحنا في [مقال الزحام](https://arabroot.io/articles/%D9%83%D9%8A%D9%81-%D8%AA%D8%B9%D8%B1%D9%81-%D8%A7%D9%84%D8%AE%D8%B1%D8%A7%D8%A6%D8%B7-%D8%A7%D9%84%D8%B2%D8%AD%D8%A7%D9%85-%D9%88%D8%B3%D8%B1%D8%B9%D8%A9-%D8%A7%D9%84%D8%B7%D8%B1%D9%8A%D9%82/) كيف تتحول نقاط المسبار إلى سرعات وكيف تعود إلى المحرك، ورأينا أن الكود نفسه بضع مئات من الأسطر، وأن المشكلة الحقيقية عدد المستخدمين: 393 سيارة اصطناعية في ساعة غطت 14% فقط من الشوارع التي مرت عليها. لذلك لا يبدأ تطبيق جديد بالزحام، وإنما يبدأ بأسطول يملكه أو يشاركه، مثل شركة توصيل أو أجرة، ثم يتوسع مع المستخدمين. والبلاغات تحتاج نظام درجات ثقة مثل الذي وصفناه في Waze، وهو العمود `trust` في جدول `reports` في [نموذج البيانات](https://arabroot.io/articles/%D9%83%D9%8A%D9%81-%D8%AA%D8%A8%D9%86%D9%8A-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82-%D8%AE%D8%B1%D8%A7%D8%A6%D8%B7-%D9%88%D9%85%D9%84%D8%A7%D8%AD%D8%A9-%D9%85%D8%AB%D9%84-%D9%87%D8%AF%D9%87%D8%AF-%D9%88-google-maps/#arch-data)، وفريقاً يراجع ما يصل، لأن أول بلاغ كاذب عن حادث يغير طريق آلاف السائقين.

## بيانات الأماكن: أصعب مشكلة في المشروع

المستخدم لا يبحث عن إحداثيات، وإنما عن «أقرب صيدلية مفتوحة الآن» أو اسم مطعم سمعه من صديق، وهذا يحتاج قاعدة أماكن فيها الاسم بالعربية والإنجليزية والتصنيف وساعات العمل والهاتف، ودقيقة وحديثة. وقد يتساءل البعض: أليست هذه البيانات موجودة في OpenStreetMap؟ والإجابة في فحص صغير على ملف الرياض نفسه الذي استخدمناه في المختبرات، بأداة osmium:

```bash
docker run --rm --name lab-routing-osmium -v lab-routing-data:/osm debian:13.1 bash -c '
  apt-get update -qq >/dev/null && apt-get install -y -qq osmium-tool >/dev/null 2>&1 && cd /tmp &&
  osmium tags-filter -R /osm/riyadh.osm.pbf nwr/amenity,shop,tourism,leisure,office,healthcare -o poi.pbf -O &&
  osmium tags-filter -R poi.pbf nwr/name -o named.pbf -O &&
  osmium tags-filter -R named.pbf nwr/opening_hours -o hours.pbf -O &&
  osmium tags-filter -R named.pbf nwr/phone,contact:phone -o phone.pbf -O &&
  osmium tags-filter -R named.pbf nwr/name:en -o en.pbf -O &&
  osmium tags-filter -R named.pbf nwr/check_date -o checked.pbf -O &&
  osmium tags-filter -R poi.pbf nwr/shop -o shop.pbf -O &&
  osmium tags-filter -R poi.pbf nwr/amenity=restaurant,cafe,fast_food -o food.pbf -O &&
  for f in poi named hours phone en checked shop food; do echo "$f $(osmium fileinfo -e -g data.count.nodes $f.pbf) $(osmium fileinfo -e -g data.count.ways $f.pbf) $(osmium fileinfo -e -g data.count.relations $f.pbf)"; done'
```

والمخرج سوف يكون كما يلي، والأعمدة عدد النقاط ثم الطرق والمضلعات ثم العلاقات:

```text
poi 7357 7234 39
named 4044 1715 14
hours 943 64 4
phone 878 61 2
en 1109 425 9
checked 46 25 0
shop 1724 261 1
food 1306 124 1
```

في المخرج أعلاه لاحظ التالي:

- في المستطيل كله 14,630 عنصراً من أنواع الأماكن، منها 5,773 فقط لها اسم، والباقي أغلبه عناصر مثل المواقف والمرافق بلا اسم.
- من الأماكن ذات الاسم، 1,011 فقط عليها ساعات العمل `opening_hours`، أي قرابة 17%، و941 عليها رقم هاتف، و1,543 عليها اسم إنجليزي.
- المطاعم والمقاهي والوجبات السريعة معاً 1,431 عنصراً في مدينة يسكنها ملايين، والمحلات 1,986.
- 71 عنصراً فقط عليها الوسم `check_date` الذي يقول متى تأكد أحد أن المكان ما زال موجوداً، أي أنك لا تعرف عمر أغلب هذه البيانات.

وهذه الأرقام لا تقلل من OpenStreetMap، فهي بيانات متطوعين ممتازة للطرق والأسماء الكبيرة، ولكنها تبين المشاكل الثلاث التي يواجهها كل تطبيق خرائط في الأماكن:

- **التغطية Coverage:** عدد الأماكن الموجودة فعلاً أكبر بكثير مما في أي مصدر مفتوح، وأكثرها المحلات الصغيرة في الأحياء.
- **الحداثة Freshness:** المطاعم والمحلات تفتح وتغلق وتغير ساعاتها كل شهر، ومكان مغلق يظهر مفتوحاً في التطبيق أسوأ من مكان غير موجود، لأن المستخدم يذهب إليه.
- **قوائم الأعمال Business Listings:** صاحب المحل هو أدق مصدر لبياناته، لذلك تبني التطبيقات الكبيرة لوحة يطالب فيها صاحب المكان بمكانه ويعدله، ومعها نظام تحقق يمنع غيره من سرقته، ونظام مراجعة للتقييمات المزيفة.

وهنا تفهم لماذا تجمع هدهد بياناتها بمسح ميداني، ولماذا تسمح كل التطبيقات الثلاثة للمستخدم بإضافة مكان مفقود: البيانات المفتوحة نقطة بداية ممتازة للطرق، أما الأماكن فهي عمل مستمر لا ينتهي.

## الرخص: متى تلزمك ODbL بنشر بياناتك؟

هذا القسم يقرره الفريق التقني مع المستشار القانوني قبل كتابة الكود، لأن تغييره بعد ذلك مكلف. ورخصة [ODbL](https://opendatacommons.org/licenses/odbl/summary/?ref=arabroot.io) فيها شرط المشاركة بالمثل Share-Alike: إذا عدلت قاعدة البيانات أو بنيت عليها قاعدة مشتقة Derivative Database واستخدمتها علناً فيجب أن تتيحها بالرخصة نفسها. ومؤسسة OpenStreetMap نشرت إرشادات تبين أين يقع هذا الخط، والصورة التالية تلخص أهم ثلاث حالات:

![ثلاث لوحات: الأولى مطاعم OpenStreetMap مع مطاعمك التي لا توجد فيه في طبقة مطاعم واحدة، وهذه قاعدة مشتقة Derivative Database ويجب أن تشارك مطاعمك برخصة ODbL. والثانية OpenStreetMap للطرق والخريطة وكل المطاعم من بياناتك بعد استبعاد مطاعم OpenStreetMap، طبقتان لا تتفاعلان، وهذه قاعدة مجمعة Collective Database وطبقتك تبقى برخصتك. والثالثة ما يراه المستخدم من خريطة مرسومة ومربعات ولقطات شاشة مع نسبة OpenStreetMap ظاهرة، وهذا عمل منتج Produced Work بأي رخصة مع النسبة](https://arabroot.io/content/images/2026/10/build-map-app-04-licensing.webp)

الخط الفاصل هو نوع الجسم: إذا اختلطت بياناتك ببيانات OpenStreetMap في النوع نفسه فقد أصبحت مشتقة

- **الطبقات الأفقية:** [إرشاد الطبقات الأفقية](https://osmfoundation.org/wiki/Licence/Community%5FGuidelines/Horizontal%5FMap%5FLayers%5F-%5FGuideline?ref=arabroot.io) يقول إن نوع الجسم Feature Type الذي تأتي كل بياناته من غير OpenStreetMap لا تنطبق عليه المشاركة بالمثل، أما إذا استخدمت بيانات OpenStreetMap مع بياناتك لنوع الجسم نفسه فتنطبق. ومثاله الصريح: إذا أضفت المطاعم التي لا توجد في OpenStreetMap فقط حتى تكمل طبقتك طبقته، فقد تفاعلت الطبقتان ويجب أن تشارك مطاعمك. أما إذا بذلت جهداً معقولاً لاستبعاد كل مطاعم OpenStreetMap واستخدمت مطاعمك وحدها فوق خريطة OpenStreetMap فلا تنطبق.
- **العمل المنتج Produced Work:** بحسب [إرشاد العمل المنتج](https://osmfoundation.org/wiki/Licence/Community%5FGuidelines/Produced%5FWork%5F-%5FGuideline?ref=arabroot.io) فإن الصور والخرائط المرسومة عمل منتج تستطيع أن ترخصه كما تريد مع النسبة، إلا إذا كان الهدف من النتيجة استخراج البيانات منها، فهي عندئذ قاعدة بيانات. وإذا كانت الخريطة مرسومة من قاعدة مشتقة فيجب أن تتيح تلك القاعدة.
- **Overture:** أماكن Overture برخصة CDLA Permissive 2.0 التي لا تشترط المشاركة بالمثل، ولكن طرقه ومبانيه برخصة ODbL، ولكل مصدر نسبة يجب أن تظهر.

والنتيجة العملية: اجعل قاعدة الأماكن التي تملكها وتبيعها طبقة مستقلة لا تأخذ من أماكن OpenStreetMap، كما يفرض القيد على العمود `source` في [نموذج البيانات](https://arabroot.io/articles/%D9%83%D9%8A%D9%81-%D8%AA%D8%A8%D9%86%D9%8A-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82-%D8%AE%D8%B1%D8%A7%D8%A6%D8%B7-%D9%88%D9%85%D9%84%D8%A7%D8%AD%D8%A9-%D9%85%D8%AB%D9%84-%D9%87%D8%AF%D9%87%D8%AF-%D9%88-google-maps/#arch-data)، أو قرر من البداية أنك سوف تشارك ما تضيفه، وكلا الطريقين مشروع، أما الخلط دون قرار فهو ما يسبب المشاكل لاحقاً. وهذا تبسيط من الإرشادات وليس استشارة قانونية.

## التكلفة والفريق

التكلفة الكبيرة في هذا المشروع ليست السيرفرات. ففي مختبرات هذه المرحلة احتاجت مدينة الرياض كاملة ملف مربعات بحجم 17 ميغابايت، وخادم مسارات بقرابة 100 ميغابايت من الذاكرة، وقاعدة Nominatim بأقل من نصف جيجابايت في جدول الأماكن، والدولة أو المنطقة أكبر من ذلك بكثير ولكنها تبقى في حدود سيرفرات عادية، كما في [تقدير السعة](https://arabroot.io/articles/%D9%83%D9%8A%D9%81-%D8%AA%D8%A8%D9%86%D9%8A-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82-%D8%AE%D8%B1%D8%A7%D8%A6%D8%B7-%D9%88%D9%85%D9%84%D8%A7%D8%AD%D8%A9-%D9%85%D8%AB%D9%84-%D9%87%D8%AF%D9%87%D8%AF-%D9%88-google-maps/#arch-capacity). والتكلفة الحقيقية في ثلاثة أماكن:

- **البيانات:** مسح ميداني، أو شراء بيانات أماكن، أو فريق يدقق ويضيف، وهذه تكلفة مستمرة لأن المدينة تتغير.
- **المستخدمون:** الزحام والبلاغات لا تعمل إلا بعدد كبير منهم، والوصول إليهم تسويق وليس هندسة.
- **الفريق:** مهندس بيانات جغرافية GIS للبيانات والدمج، ومطور خلفي Backend للخدمات والبناء الدوري، ومطورا هواتف لـ iOS وAndroid لأن الملاحة تحتاج عملاً أصلياً Native، ومن يراجع النصوص العربية والصوت، وفريق تشغيل للبلاغات وبيانات الأماكن. وفي فريق صغير يجمع شخص واحد أكثر من دور، ولكن الأدوار نفسها لا تختفي.

ولاحظ أن البديل ليس مجانياً أيضاً، فـ [منصة Google Maps](https://developers.google.com/maps/billing-and-pricing/pricing?ref=arabroot.io) تحسب على كل طلب، وبوابة هدهد للمطورين تقدم واجهات مشابهة محلياً، فالسؤال الحقيقي ليس «هل نبني أم لا؟» وإنما «أي القطع نبنيها وأيها نشتريها؟».

## خطة نسخة أولى MVP

لا تبدأ بمنافسة Google Maps، وإنما بمشكلة محددة لجمهور محدد تملك بياناته، مثل تطبيق لمندوبي شركتك، أو خريطة فروعك مع الوصول إليها، أو تطبيق لزوار حدث كبير. وخطة معقولة على مراحل، وهي عمود MVP ثم عمود النمو في [جدول مراحل النمو](https://arabroot.io/articles/%D9%83%D9%8A%D9%81-%D8%AA%D8%A8%D9%86%D9%8A-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82-%D8%AE%D8%B1%D8%A7%D8%A6%D8%B7-%D9%88%D9%85%D9%84%D8%A7%D8%AD%D8%A9-%D9%85%D8%AB%D9%84-%D9%87%D8%AF%D9%87%D8%AF-%D9%88-google-maps/#arch-growth):

1. **الخريطة والبحث:** مربعات مدينتك من سيرفرك كما في مقال الخريطة، وبحث بالعناوين والأماكن المهمة لك، مع بياناتك الخاصة كطبقة مستقلة.
2. **المسار وزمن الوصول:** محرك مسارات واحد، مع `radiuses` وفحص مسافة الربط، وزمن وصول تعرضه كتقدير لطرق فارغة دون أن تعد بأكثر من ذلك.
3. **الملاحة:** تطبيق هاتف مع MapLibre Native وFerrostar، وتعليمات عربية يراجعها إنسان، واختبار في الشارع في مدينتك وليس في المكتب.
4. **الزحام من أسطولك:** نقاط مندوبيك تصبح سرعات كما في مقال الزحام، مع سياسة خصوصية مكتوبة وحد أدنى للعدد.
5. **البلاغات والأماكن:** بعد أن يصبح لديك مستخدمون، افتح الإضافة والتعديل مع مراجعة.

وبين كل مرحلة وأخرى قس ما يهم المستخدم فعلاً: هل وجد المكان الذي بحث عنه؟ وكم اختلف زمن الوصول الحقيقي عن المتوقع؟ ومتى ترك التطبيق؟ وهذه الأسئلة أهم من أي اختيار تقني في هذا المقال.

## تنظيف المختبر

الفحصان في هذا المقال استخدما ال Volume والملفات من مختبر [مقال المسارات](https://arabroot.io/articles/%D8%AD%D8%B3%D8%A7%D8%A8-%D8%A7%D9%82%D8%B5%D8%B1-%D8%B7%D8%B1%D9%8A%D9%82-%D9%88%D8%B2%D9%85%D9%86-%D8%A7%D9%84%D9%88%D8%B5%D9%88%D9%84-%D9%85%D8%B9-osrm-%D9%88-valhalla/#cleanup)، فاحذفها بالأوامر هناك، ثم احذف مجلد `oti` الذي فيه حزم npm:

```bash
rm -rf oti
```

## الخلاصة

وصلنا لنهاية الموضوع، وأهم ما فيه:

- هدهد تطبيق خرائط وملاحة سعودي من شركة Hudhud Information Technology Company، أول إصدار له على App Store في مايو 2025، وتذكر بوابة مطوريه أنه يجمع بياناته بمسح ليزري ميداني.
- القطع التقنية كلها مفتوحة المصدر: OpenStreetMap وOverture للبيانات، وPlanetiler وPMTiles للمربعات، وNominatim أو Photon أو Pelias للبحث، وOSRM أو Valhalla أو GraphHopper للمسارات، وMapLibre وFerrostar على الهاتف.
- ما يصنع الفرق هو الأماكن والزحام والبلاغات، وكلها تحتاج بيانات ومستخدمين وفريق تشغيل، وفي الرياض 17% فقط من الأماكن ذات الاسم في OpenStreetMap عليها ساعات العمل.
- التعليمات الصوتية العربية تحتاج مراجعة بشرية، فترجمة Valhalla ناقصة، وosrm-text-instructions تكتب الانعطاف العادي إلى شارع مسمى «اسلك مسار الدوران».
- خلط أماكنك بأماكن OpenStreetMap في الطبقة نفسها يجعل طبقتك مشتقة برخصة ODbL، فقرر ذلك قبل أن تبدأ.
- ابدأ بمشكلة محددة وجمهور تملك بياناته، ثم وسع على مراحل.

[![](https://arabroot.io/content/images/2026/10/self-hosted-maps-cover-cover.webp)دليلخرائطك على سيرفرك مع OpenStreetMap وMapLibreكيف تعرض خريطة تفاعلية لمدينتك من سيرفرك ببيانات OpenStreetMap، بالعربية والإنجليزية ودون مفتاح API أو مزود خارجي.](https://arabroot.io/articles/%D8%AE%D8%B1%D8%A7%D8%A6%D8%B7%D9%83-%D8%B9%D9%84%D9%89-%D8%B3%D9%8A%D8%B1%D9%81%D8%B1%D9%83-%D9%85%D8%B9-openstreetmap-%D9%88-maplibre/)[![](https://arabroot.io/content/images/2026/10/nominatim-cover-cover.webp)دليلالبحث عن العناوين Geocoding مع Nominatimكيف تحول اسم المكان إلى إحداثيات والإحداثيات إلى عنوان على سيرفرك مع Nominatim، وما الذي يجيده بالعربية وما الذي يفوته.](https://arabroot.io/articles/%D8%A7%D9%84%D8%A8%D8%AD%D8%AB-%D8%B9%D9%86-%D8%A7%D9%84%D8%B9%D9%86%D8%A7%D9%88%D9%8A%D9%86-geocoding-%D9%85%D8%B9-nominatim/)[![](https://arabroot.io/content/images/2026/10/routing-cover-cover.webp)دليلحساب أقصر طريق وزمن الوصول مع OSRM وValhallaكيف يحسب محرك المسارات أقصر طريق وزمن الوصول على شوارع مدينتك، وكيف تشغله على سيرفرك وتقيس سرعته بنفسك.](https://arabroot.io/articles/%D8%AD%D8%B3%D8%A7%D8%A8-%D8%A7%D9%82%D8%B5%D8%B1-%D8%B7%D8%B1%D9%8A%D9%82-%D9%88%D8%B2%D9%85%D9%86-%D8%A7%D9%84%D9%88%D8%B5%D9%88%D9%84-%D9%85%D8%B9-osrm-%D9%88-valhalla/)[![](https://arabroot.io/content/images/2026/10/traffic-data-cover-cover.webp)دليلكيف تعرف الخرائط الزحام وسرعة الطريق؟كيف تتحول نقاط GPS من الهواتف إلى سرعة لكل شارع وزمن وصول أدق، وما الذي يحمي خصوصية أصحابها.](https://arabroot.io/articles/%D9%83%D9%8A%D9%81-%D8%AA%D8%B9%D8%B1%D9%81-%D8%A7%D9%84%D8%AE%D8%B1%D8%A7%D8%A6%D8%B7-%D8%A7%D9%84%D8%B2%D8%AD%D8%A7%D9%85-%D9%88%D8%B3%D8%B1%D8%B9%D8%A9-%D8%A7%D9%84%D8%B7%D8%B1%D9%8A%D9%82/)[![](https://arabroot.io/content/images/2026/10/postgres-for-everything-cover-cover.webp)دليلPostgreSQL لكل شيء: متى يكفيك ومتى تحتاج غيره؟كم عملاً يستطيع PostgreSQL واحد أن يقوم به في شركتك الناشئة قبل أن تحتاج Redis أو Elasticsearch، وما الإشارة التي تقول لك إن الوقت قد حان.](https://arabroot.io/articles/postgresql-%D9%84%D9%83%D9%84-%D8%B4%D9%8A%D8%A1-%D9%85%D8%AA%D9%89-%D8%AA%D9%83%D9%81%D9%8A%D9%83-%D9%82%D8%A7%D8%B9%D8%AF%D8%A9-%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D9%88%D8%A7%D8%AD%D8%AF%D8%A9/)[![](https://arabroot.io/content/images/2026/10/open-source-licenses-cover-cover.webp)دليلما هي البرمجيات مفتوحة المصدر؟ القصة وأشهر الرخصكيف بدأت البرمجيات الحرة والمفتوحة، وما الفرق بين الحر والمجاني، وكيف تعرف ما تسمح به رخصة أي مشروع قبل أن تبني عليه.](https://arabroot.io/articles/%D9%85%D8%A7-%D9%87%D9%8A-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A7%D8%AA-%D9%85%D9%81%D8%AA%D9%88%D8%AD%D8%A9-%D8%A7%D9%84%D9%85%D8%B5%D8%AF%D8%B1-%D9%88%D8%AA%D8%B1%D8%A7%D8%AE%D9%8A%D8%B5%D9%87%D8%A7/)

## سجل التحديثات

- أكتوبر 2026: إضافة قسم المعمارية الكاملة: المخطط والمكونات ونموذج البيانات والواجهات وتقدير السعة ومراحل النمو.
- أكتوبر 2026: كتابة المقال، والتحقق من صفحات هدهد الرسمية وصفحته في App Store، وفحص بيانات الأماكن في مستطيل الرياض بأداة osmium 1.18.0 على بيانات Geofabrik حتى 2026-10-08، وفحص الجمل العربية بمكتبة osrm-text-instructions 0.15.0 على مسار من OSRM v26.10.0.