Files
maps-saas/docs/full_project_documentation.md

125 lines
8.1 KiB
Markdown

# 🌍 التوثيق الشامل لمشروع الخرائط (Map SaaS Complete Documentation)
هذا المستند يعتبر المرجع الشامل لكامل البنية التحتية لمشروع الخرائط. يشرح جميع الأجزاء (الخريطة، البحث، التوجيه)، كيف تتصل ببعضها البعض، وكيف يتم تكاملها مع واجهات الفلاتر (Flutter) الخاصة بالمستخدم.
---
## 🏛️ البنية التحتية للمشروع (Architecture Overview)
يتكون النظام من ثلاثة محركات رئيسية تعمل بتناغم تام لتقديم تجربة تضاهي جوجل ماب:
1. **محرك رسم الخرائط (Tile Server - Martin)**: مسؤول عن رسم الخريطة الجغرافية والشوارع.
2. **محرك البحث الجغرافي (Geocoding Engine)**: مسؤول عن البحث، الإكمال التلقائي، وتصحيح الأخطاء.
3. **محرك التوجيه والمسارات (Routing Engine - GraphHopper)**: مسؤول عن رسم مسار الرحلات واحتساب الوقت مع الأخذ بعين الاعتبار الازدحام المروري.
---
## 🗺️ 1. محرك رسم الخرائط (Martin Tile Server)
### ❓ ما هو Martin وكيف يعمل؟
Martin هو خادم سريع جداً (مكتوب بلغة Rust) متصل مباشرة بقاعدة بيانات `PostgreSQL / PostGIS`. وظيفته هي تحويل الإحداثيات والحدود الجغرافية الموجودة في قاعدة البيانات إلى **Vector Tiles** (مربعات شعاعية بصيغة `pbf`).
### 🎨 كيف ترسم الخريطة (Rendering)؟
بدلاً من إرسال صور (Images) ثقيلة للمستخدم كما في الخرائط القديمة، يرسل Martin بيانات هندسية خفيفة جداً (أرقام وإحداثيات).
المتصفح أو تطبيق الجوال (Flutter) يقوم بأخذ هذه البيانات، ويمررها إلى كرت الشاشة (GPU) ليقوم برسم الشوارع، المباني، والحدائق بسلاسة فائقة بمعدل 60 إطاراً في الثانية (60fps) بناءً على ملف تصميم يسمى `style.json`.
### 📱 استخدام الخريطة في Flutter
لرسم الخريطة داخل تطبيق Flutter الخاص بك، ستحتاج إلى مكتبة [maplibre_gl](https://pub.dev/packages/maplibre_gl):
```dart
MaplibreMap(
styleString: 'http://SERVER_IP:3000/public/style.json', // رابط الـ Style الخاص بالخادم
initialCameraPosition: CameraPosition(
target: LatLng(31.95, 35.91), // عمّان
zoom: 14.0,
),
onMapCreated: (MaplibreMapController controller) {
// الخريطة أصبحت جاهزة للرسم عليها
},
)
```
---
## 🔍 2. محرك البحث الجغرافي (Geocoding Engine)
محرك ذكي جداً مكتوب بـ `NestJS`، يتفهم لغة الشارع العربي والأخطاء الإملائية.
### أ. الإكمال التلقائي (Autocomplete API)
يستدعى مع كل حرف يكتبه المستخدم لتقديم اقتراحات فورية بأقل من 50 ملي ثانية (باستخدام فهارس `varchar_pattern_ops`).
- **الرابط:** `GET /api/geocoding/autocomplete?q=مستش`
- **الاستخدام في Flutter:** ربطه بـ `TypeAheadField`، لعرض أسماء المستشفيات بمجرد كتابة "مستش".
### ب. البحث الدقيق (Search API)
يستدعى عند ضغط زر "بحث" أو اختيار نتيجة. يقوم بترتيب الأماكن بناءً على خوارزمية ذكية: (50% تشابه نصي، 30% أهمية/شعبية المكان، 20% المسافة).
- **الرابط:** `GET /api/geocoding/search?q=السيتي مول`
- **ميزات النظام الداخلية المدعومة هنا:**
1. **التطبيع العربي:** يعالج (إ/ا/أ/ة/ه).
2. **الاستعلامات النسبية:** يفهم (قرب المستشفى، بجانب البنك).
3. **بوابات المجمعات (POI Gates):** للمولات الكبرى، يرجع المحرك إحداثيات (البوابة الرئيسية، بوابة الطوارئ) ضمن مصفوفة `gates`.
4. **هل تقصد (Did you mean):** إذا كتب المستخدم مصطلحاً خاطئاً بالكامل ("السمساني") وعاد بـ 0 نتائج، يقوم المحرك ببحث `KNN` لإرجاع اقترح للتصحيح.
**الاستخدام في Flutter:**
عند نجاح البحث، نأخذ الـ `latitude` و `longitude` للنتيجة، ونقوم بتحريك الكاميرا:
```dart
controller.animateCamera(CameraUpdate.newLatLng(LatLng(lat, lng)));
```
وإذا كان الرد يحتوي على `gates`، يمكن إظهار نافذة منبثقة للسائق: *"أي بوابة تقصد؟"*.
---
## 🚗 3. محرك التوجيه والمسارات (Routing & Traffic Engine)
يعتمد هذا النظام على محرك `GraphHopper` المدموج بتعديلات `NestJS` لحساب الازدحام المروري وتوفير المسار الأسرع.
### طلب مسار (Route API)
يقوم هذا الـ API بحساب المسار من نقطة (A) إلى نقطة (B)، مع إرجاع المسار البديل وتوجيهات الملاحة، مع أخذ "عامل الازدحام" بعين الاعتبار حسب ساعة ويوم الطلب.
- **الرابط:** `GET /api/maps/route?start=lat,lng&end=lat,lng&profile=car`
- **شكل الرد (Response Overview):**
يُرجع الرد المسار الأفضل `points` مشفراً بصيغة (Polyline)، والوقت `duration`، والمسافة `distance`.
### 📱 رسم المسار في Flutter
بما أننا ألغينا توليد الـ `GeoJSON` الثقيل من السيرفر لتخفيف العبء، فإن تطبيق الفلاتر سيتكفل بفك التشفير.
1. استقبل الـ `points` (الـ Polyline) من الـ API.
2. استخدم مكتبة مثل [flutter_polyline_points](https://pub.dev/packages/flutter_polyline_points) لفك تشفيرها إلى قائمة من إحداثيات (List of LatLng).
3. ارسمها على خريطة MapLibre كـ `LineLayer`:
```dart
// فك التشفير
List<PointLatLng> decodedPoints = polylinePoints.decodePolyline(apiResponse['points']);
// تحويلها لصيغة يفهمها MapLibre
final lineString = {
"type": "Feature",
"geometry": {
"type": "LineString",
"coordinates": decodedPoints.map((p) => [p.longitude, p.latitude]).toList()
}
};
// رسم المسار بخط أزرق على الخريطة
await controller.addSource("route-source", GeojsonSourceProperties(data: lineString));
await controller.addLineLayer(
"route-source",
"route-layer",
LineLayerProperties(
lineColor: "#007AFF",
lineWidth: 5.0,
lineJoin: "round",
),
);
```
---
## 🔄 ملخص دورة حياة الطلب (User Flow in App)
1. **الفتح الأولي:**
يفتح الراكب تطبيق Flutter ⬅️ تقوم أداة `MapLibre` بتحميل خريطة الـ Vector السريعة من خادم `Martin`.
2. **البحث:**
يكتب الراكب في صندوق البحث ⬅️ يتم مناداة `/autocomplete` لعرض الاقتراحات فوراً.
3. **الاختيار:**
يختار الراكب (مثلاً: سيتي مول) ⬅️ يتم مناداة `/search` ⬅️ الخادم يرجع البوابات المتاحة للمول ⬅️ الراكب يختار "البوابة الرئيسية".
4. **طلب الرحلة:**
التطبيق يرسل موقع الراكب وموقع البوابة إلى `/api/maps/route` ⬅️ الخادم يحسب المسار، يطبق خوارزمية الازدحام المروري، ويرجع خط سير (Polyline) ⬅️ الفلاتر يفك تشفير الخط ويرسمه على الخريطة باللون الأزرق.
بذلك نكون قد غطينا البنية المعمارية الكاملة بأدق تفاصيلها من السيرفر وحتى واجهة التطبيق! 🚀