هذه قصة حول كيف حاولت حل مشكلة غريبة منعتني بنفسي. في المستقبل ، سأقول إنني راضٍ عن الحل الناتج وأوصل التطبيق إلى نهايته المنطقية. ومع ذلك ، لتشغيلها بالكامل ، تحتاج إلى المزيد من الموارد ، لذلك قررت أن أخذ استراحة وأطلب من الناس ما إذا كان أي شخص آخر بحاجة إليها. لهذا الغرض (وللتحدث فقط) أكتب هنا.
كلمتان عن نفسي: أعيش في دبلن ، أيرلندا ، أعمل كمبرمج. لا يجلس على الفور بالضبط ، لهذا السبب في أوقات فراغي في المنزل رأيت مشاريع مختلفة ، بشكل رئيسي على الطاولة. أكتب لأول مرة على حبري ، رغم أنني قرأت لسنوات عديدة.
المشكلة
منذ فترة طويلة ، عندما اضطررت إلى السفر كثيرًا إلى أماكن غير مألوفة للعمل ، بدأت ألاحظ أن البحث القياسي على أي بطاقات لا ينطبق على السائقين اليوم. انظر: أنت تقود في منطقة غير مألوفة ، ولديك سهم بنزين عند صفر. أفعالك؟ إذا لم أكن وحدي في السيارة في تلك اللحظة ، فأخبر الراكب: "حسنًا ، ابحث عن محطة وقود قريبة بينما أقود." لأنه إذا قمت بذلك بنفسك ، فأنت بحاجة إلى تنفيذ الإجراءات التالية:
- للتوقف
- في تطبيق الخريطة ، أدخل "بنزين" في البحث (أو انقر فوق أحد الأزرار السريعة التي توفرها بعض البطاقات الآن)
- يقوم التطبيق بالبحث ويظهر لك خريطة ضخمة بها عشرات محطات الوقود
- حاولت معرفة أيهما أقرب إليك وانقر فوقه لبناء طريق
في رأيي ، كابوس. أولاً ، تحتاج إلى التوقف أو على الأقل انتظار إشارة ضوئية. لأن البطاقات معقدة والرموز صغيرة. ثانيًا ، لا تخبرك بطاقة nifig عن أقرب محطة وقود. في هذا الصدد ، تعد Google الأسوأ على الإطلاق: حتى في النتائج على شكل قائمة ، فهي تدفع بعناد ليس أقرب مكان ، والأكثر تقييمًا / مع الصور / مدفوعة / ليس لدي أي فكرة.

حسنا ، العامل الثالث: نحن نتحرك. كانت النتيجة أن البطاقات الصادرة كانت ذات صلة بنقطة ما ، لكننا بالفعل بعيدون عنها. هل لاحظت أنه حتى المسار الذي وضعته Google لا يتم تحديثه تلقائيًا إذا كنا قد انتقلنا؟ فقط إذا تم تشغيل الملاحة بالفعل ، فسيتم إعادة البناء.
بشكل عام ، المشكلة واضحة. المنطق الذي يناسب المشاة واحتياجاتهم للسائقين لا يعني شيئًا. لا أهتم بتصنيف المكان ونوع المطبخ هناك - أحتاج ، دون التشتيت عن الطريق ، في الوقت الحقيقي إلى الحصول على طريق إلى أقرب محطة وقود ، ورسوم ، ومواقف ، وصراف آلي ، وما إلى ذلك.
فكرة
الآن دعنا نحاول تحديد سيناريو البحث المثالي. المعيار كما يلي:
- التفاعل قصير وواضح حتى لا يصرف السائق
- الإخراج الشفاف القائم على المسافة
- تحديث في الوقت الحقيقي
أول ما يستدعي الأمر هو استبدال البحث القياسي لمرة واحدة بمسح ضوئي. أي أن سلسلة الإجراءات هي شيء من هذا القبيل:
- أطلقت فحصًا
- ركوب ، والنظر في النتائج المحدثة في الوقت الحقيقي
- عندما أعجبت بشيء ما ، نقرت الطريق ورصفته
أنت تحدد معايير البحث مقدمًا - في الواقع ، هذا هو نوع المكان ونصف قطر الفحص. علاوة على ذلك ، أثناء تحرك السيارة ، يعمل التطبيق مثل الرادار. سرعان ما أصبح من الواضح أن نصف القطر ليس مستديرًا أولاً ، ولكن في شكل عزلة. ثانيًا ، يجب ألا يتم بناؤه بالمسافة ، ولكن بالوقت ، لأن الدقائق أسهل بكثير من إدراكها من الكيلومترات.
ثم فكرت في طريقة لإخراج النتائج. بتعبير أدق ، هل أحتاج إلى بطاقة على الإطلاق؟ ينظر السائق إلى التطبيق بعين واحدة ويتفاعل بإصبع واحد - فهو لا يحتاج إلى خريطة ، ولكن إلى نص كبير وأزرار كبيرة. لذلك ، قررت على الفور أن الشاشة الرئيسية ستكون قائمة ، وقد أضيف خريطة في البداية ثم أرى ما إذا كان يجب أن أتركها.
الخطة
بمعرفة خصوصيتي الخاصة بتمديد المشاريع ، قررت أن أعطي نفسي شهرين لكل شيء - في النهاية التقيت في 3. من حيث المبدأ ، التطبيق بسيط للغاية:
- عميل من زوج من الشاشات (بحث ، قائمة وخريطة)
- يرسل بشكل دوري إلى الخادم إحداثياته ، نصف قطر البحث ونوع الأماكن
- يقوم الخادم ببناء عزلة في الوقت المناسب (بالمناسبة ، يكون اسمه باللغة الإنجليزية - isochrone) ، ويقوم بالبحث في الأماكن وإرجاع قائمة
يبدو وكأنه رئة أخف. لدي بالفعل بعض الخبرة السابقة في رسم الخرائط (قبل بضع سنوات قمت بعمل بوابة عقارات حيث كان البحث على الخريطة) ، لذلك كان المكدس الموجود على الواجهة الخلفية واضحًا على الفور:
- استيراد البيانات من OpenStreetMaps إلى Elasticsearch
- OpenTripPlanner لبناء ملامح
على العميل ، فكرت ، قررت استخدام الإطار الجديد من Google - Flutter. إنه متعدد المنصات ومرن تمامًا ويسمح لك بإنشاء تطبيقات كاملة بحد أدنى من التعليمات البرمجية. بالطبع ، إنه خام وليس من الواضح ما هو في الإنتاج ، لكنه يبدو مثاليًا للنماذج الأولية. يجب توضيح أنه في هذه المرحلة كانت لدي خبرة في التنمية المحلية لنظام android (كنت قائد فريق) وقررت ، إذا جاز التعبير ، مواجهة العدو. لم يكن العدو مخيفًا جدًا.
التنفيذ
كان أول تطبيق للنموذج الأولي جاهزًا بسرعة كبيرة - Flutter له عتبة دخول منخفضة وفلسفة شبيهة بمفهوم الاختزال. من الغريب أن الوصف التعريفي للواجهات كان لطيفًا أيضًا ، بالإضافة إلى إعادة التشغيل الساخن (React Native ، صورتك النقطية). بشكل عام ، كان الانطباع أن Google هي المسؤولة عن معظم الأمراض الخلقية من المحاولات السابقة. ومع ذلك ، أفهم الأشخاص الذين قد لا يرغبون في الدخول إليها - شخص لا يحب السهام ، وعدد محدود من الأدوات ، و "التصحيح المرئي" الذي يتم تقديمه هنا هو شيء خام للغاية.
في الخلفية ، فعلت ما يلي:
- تم تسليم Nominatim ، تم تحميل مقتطف بيانات OpenStreetMaps (مأخوذ هنا ) في قاعدة بياناته ، باستخدام الأداة المساعدة القياسية osm2pgsql. لماذا أنتقل إلى الفوتون الصغير الحامض المفتوح لكن الفوتون . في وقت سابق ، استخدمته بالفعل في اثنين من المشاريع - يقوم بإنشاء فهرس Elasticsearch ، واستيراد البيانات من قاعدة بيانات Nominatim هناك ، والبحث عن هذا الفهرس. أحبها بسرعة ورسم خرائط نقية (على سبيل المثال ، جربت Pelias وأعجبني أقل). مشكلتها الرئيسية هي النسخة القديمة من المرونة ، ولكن في حالتي لم أكن بحاجة إلى وظيفة المبرمج الجغرافي نفسه ، فقط البيانات ، لذلك بعد الاستيراد ، قمت بتحويل الفهرس إلى تثبيت أحدث نسخة مرنة مع روح نقية. بالمناسبة ، لماذا اخترت Elasticsearch؟ إنه سريع جدًا ، وله وظيفة البحث عن الإحداثيات عن طريق المضلع.
- مكب النفايات - الملقب isochrone - أنشأ في البداية OpenTripPlanner بالنسبة لي. هذا مخطط مسار مفتوح جيد جدًا. يعمل على النحو التالي: يأخذ نفس استخراج OpenStreetMaps ويجمعه في رسم بياني كبير للطريق ، والذي يتم حفظه ، ككائن منفصل ، على القرص. عندما يبدأ الخادم ، يتم تحميل هذا الرسم البياني في ذاكرة الوصول العشوائي ويتم البحث في جميع المسارات من خلاله. الإيجابيات: انتقاء سريع ، وظائف غنية (على سبيل المثال ، يولد ملامح من الصندوق) وسرعة جيدة. السلبيات: تعتمد هذه السرعة على مقدار ذاكرة الوصول العشوائي ، والوثائق مثيرة للاشمئزاز للغاية. مجرد وثائق وحشية. ذكريات الفيتنامية.
- لقد قمت برمي واجهة برمجة تطبيقات صغيرة على الثعبان ، والتي تأخذ نوع المكان ونصف قطر البحث في ثوانٍ ، وتطلب مضلعًا من OpenTripPlanner ، ثم يبحث عنه في Elasticsearch. يطلب مسارًا إلى كل موقع موجود (مرة أخرى من OpenTripPlanner) ، ويأخذ طوله ووقته. بعد ذلك ، يتم حزم جميع البيانات التي تم جمعها بشكل جميل وإعادتها.
قمت بتحديث النتائج بتحويل إحداثيات الجهاز بمقدار 5 أمتار. الخريطة ثابتة - لقد استخدمت واجهة برمجة تطبيقات خرائط Google الثابتة (كما ترون ، هذا هو المكان الوحيد الذي زحفت فيه الشركة مع ذلك إلى عالم المصدر المفتوح المريح). بدا التنفيذ الأول على النحو التالي:




بعد اللعب مع التطبيق ، قررت إخفاء الخريطة. لقد قامت بعمل جيد لفهم المضلع الذي تم البحث عنه ، وبدت مسلية - كان من المثير للاهتمام مشاهدة كيف يتغير شكل هذا الأخطبوط في الوقت الحقيقي. ومع ذلك ، فإن هذا الترفيه لم يساعد التطبيق على أداء وظائفه واحتل ثلث الشاشة.
كما حدث لي إضافة سهم يشير إلى الاتجاه لكل نتيجة. عملت مثل هذا:
- تذكر إحداثياتك السابقة
- عند التحول ، نضع الطريق من الموضع السابق إلى التيار
- نأخذ الجزء الأخير من مسارنا ونقارنه بالجزء الأول من المسار لكل نتيجة. نظرًا لوضعها على نفس شبكة الطرق ، مع احتمال بنسبة 99٪ ، تكون الزاوية بينهما قريبة من 0 أو 180.
هذه الحيلة البسيطة للغاية تسهل إلى حد كبير فهم ما إذا كنا نتجه بالفعل نحو المكان أو ما إذا كان من الضروري العودة.


في هذه المرحلة ، كنت سعيدًا جدًا بالتطبيق الناتج وقررت محاولة نشره في العديد من البلدان. ومع ذلك ، فإن أيرلندا دولة صغيرة جدًا ، على التوالي ، وكان المؤشر المرن والرسم البياني للطريق صغيرًا. للاختبار ، قررت ربط المملكة المتحدة المجاورة. وهي أكبر بحوالي 4 أضعاف ولديها شبكة طرق أكثر كثافة (خاصة العاصمة والمدن الكبيرة). ثم نشأت مشكلة.
توقعت شركة Elasticsearch توقع زيادة المؤشر بشكل جيد ، ولكن مع OpenTripPlanner كان هناك فشل كامل. إنه مكتوب بلغة جافا ، وكما قلت أعلاه ، يولد رسمًا بيانيًا للطرق ، وذلك بعد تحميله في ذاكرة الوصول العشوائي. كان الرسم البياني لأيرلندا 1 غيغابايت ، بالنسبة للمملكة المتحدة كان بالفعل 5. وكان من الممكن ، بالطبع ، تقسيمه إلى بلدان ومناطق وحتى مناطق ، ثم إعادة التوجيه إلى الرسم البياني المطلوب اعتمادًا على إحداثيات المستخدم. ومع ذلك ، فقد جعل ذلك من المستحيل وضع طرق بين المناطق ، والأهم من ذلك ، لم يحل الحاجة إلى الاحتفاظ بجميع هذه الرسوم البيانية في الذاكرة. أخيرًا ، استغرق تجميع كل كائن من هذا القبيل موارد كثيرة جدًا واستمر إلى الأبد. من أجل المتعة ، أطلقت على جهازي (إطارات 16 جيجابايت) تجميع كونت فرنسا ، انتظرت يومًا وألغيت.
من الواضح أن التكنولوجيا التي أثبتت نفسها بشكل جيد في المهام الصغيرة لم يتم تصميمها للتطوير على الإطلاق (على الأقل ليس مع مواردي). لذلك يجب إما أن تعترف بالهزيمة ، أو الزحف إلى تقنية أخرى. أخذت قسطًا من الراحة لبضعة أيام وبدأت في دراسة الحلول الأخرى مفتوحة المصدر الموجودة في العالم. اتضح أن هناك أساسًا اثنان منهم:
إذا كانت الأولى مكتوبة بلغة Java وتحمل الرسم البياني للطريق إلى ذاكرة الوصول العشوائي ، فإن OSRM - Open Source Routing Machine - مكتوب بالفعل في الإيجابيات ويحافظ على ملفاته المتوسطة (التي لا تقل وحشية) على القرص. وبالتالي ، تم استبدال الحاجة إلى وجود كمية كبيرة من ذاكرة الوصول العشوائي بمتطلب قرص كبير وسريع. هذا أكثر واقعية.
خط النهاية
بعد بضع ليال من اختيار الوثائق ، تم نقل كل كود الخادم إلى حل جديد. عملت حقا ، وعملت بشكل جيد. كان من الممكن ربط عدة بلدان ، وزادت سرعة البحث. كانت المبادئ العامة متشابهة: من ملف OpenStreetMaps لاستخراج الملفات الوسيطة تم تجميعها لملف تعريف "الجهاز" (ملف التعريف عبارة عن مجموعة من الأوزان والتعليمات لحواف الرسم البياني - هناك ملفات تعريف "سيرًا على الأقدام" و "دراجة" وما إلى ذلك). ثم تم وضع هذه الملفات في دليل ، وقراءة OSRM api بالفعل من القرص. بالمناسبة ، تبين أن Api كبير إلى حد ما - فقد تم دعم كل من الخطوط التخطيطية والمسار مع الفروق الدقيقة المختلفة ، حتى أنه كان هناك جيل من البلاط للخريطة. قررت أن أتطرق إلى هذا الأخير بمزيد من التفصيل.
بالعودة إلى التطبيق واستمرارًا في اختباره ، أدركت شيئين آخرين:
- القائمة في الأعلى ليست جيدة ، تصل إلى مسافة بعيدة
- الخريطة العامة غير مطلوبة بالتأكيد ، فهي تربطني فقط بجوجل
- بطاقات النتيجة مملة ورتيبة
لقد أسعد بسعادة خريطة جوجل (بسرعة ، 100٪ مفتوحة المصدر وبياناته) ، وبسط القائمة ، وانتقل إلى أسفل. بدأت التفكير في ما يجب القيام به مع البطاقات. ثم ظهر بلاط api في الوقت المناسب ، والذي ذكرته أعلاه. يسمح لك بإنشاء مربع متجه للإحداثيات ومستوى التكبير. يتم إصدار النتيجة في شكل نقطة ثنائية من النوع application / x-protobuf - وهو نوع بيانات غير مناسب للتلاعب. لن أخوض في التفاصيل (كان علي أن أتعرق قليلاً) ، ولكن باختصار بدت أفعالي كما يلي:
- خذ خط الطريق الذي تم إنشاؤه إلى النقطة في شكل متعدد الخطوط
- الخطوط المتعددة -> GeoJSON
- احصل على الصندوق المحيط بهذا الشكل
- اطلب كل المربعات الملتقطة بواسطة الصندوق المحيط
- تحويل بيانات البلاط من تنسيق ثنائي إلى GeoJSON
- بلاط الغراء ، تقليم بواسطة الصندوق المحيط ، يتحد مع خط المسار ، التلوين
- تحويل GeoJSON الناتج إلى صورة نقطية
في سياق الإجراء ، كانت هناك فروق دقيقة مختلفة ، على سبيل المثال ، المسافة البادئة للمربع المحيط أو وضع علامات على الحلقات الملونة (وجعل نصف قطرها ثابتًا لجميع مستويات التكبير / التصغير). بدت الصورة الناتجة كالتالي:


اللمسات الأخيرة
عندما أرفقت مسارًا مرئيًا لكل نتيجة ، بدأت القائمة تتألق بألوان جديدة. بالإضافة إلى ذلك ، أدركت أن كل صورة ، بشكل افتراضي ، تسير على الجانب الشمالي ، جعلتها تدور فيما يتعلق بالبوصلة. وهكذا ، بالإضافة إلى التأثير البصري ، أصبحت هذه الشريحة أيضًا وظيفية - لتحل محل سهم الاتجاه. الآن بعد أن تقود ، يمكنك أن ترى على وجه اليقين أي جانب من هذه النتيجة أو تلك.
انتهى الشهر الثالث من التطوير ، وكان من الضروري تقريبًا اختتامه. كلما أضفت أكثر ، كلما أردت أكثر ، لذا في مرحلة ما تحتاج فقط إلى تجميع نفسك وترك المشروع. لقد قمت بتعديل الواجهة ورسمها أكثر قليلاً ، ولشعور بالإنجاز ، قمت برسم شعار التطبيق:


وصفحة مقدمة:


وأخيرًا ، النسخة النهائية من التطبيق:





الملخص
حسنًا ، شكرًا على المشاهدة. آمل أن يكون تيار الوعي هذا مثيرًا للاهتمام لشخص ما ، وربما يكون مفيدًا أيضًا. في هذه المرحلة ، أعتقد أن التطبيق جاهز: إنه سريع ، بدون أخطاء خاصة ويمكنه العمل في أي بلد في العالم. بالمناسبة ، ربما لاحظت أن لقطات الشاشة كانت من كل من iPhone و Android ، لأنه بفضل Flutter ، يعمل التطبيق تمامًا على كلا النظامين الأساسيين.
ومع ذلك ، حتى الآن قررت تجميد كل شيء - غيرت وظيفتي ، ظهرت مخاوف جديدة. بعد شهرين ، قمت بإزالة الغبار وقررت أن أكتب بأثر رجعي. رأيك مثير للاهتمام: هل تريده ، استخدمه ، ما الذي يمكن تغييره.
PS بالطبع ، حول جاهزية التطبيق هراء. إنه جاهز كنموذج أولي - إذا اقتربت من إنتاج جاد ، فستحتاج إلى إنشاء برامج نصية لمزامنة البيانات مع OpenStreetMaps ، والتحقق من العملية في حديقة الحيوانات للأجهزة ، وتوطين الواجهات ، إلخ. تقع الواجهة الخلفية نفسها على العقدة والبيثون تحت أي حمل خطير.