
في مقال سابق ، تحدثت عن كيفية إنشاء طالب ويب بسرعة. ولكن ماذا لو قمت بتعيين مهمة أكثر طموحًا - لتجميع التطبيق الخاص بك ببطاقة ، دون إعلانات وبلاك جاك؟ وإذا في بضعة أيام فقط؟
لنقم بذلك! أطلب القط.
أولاً ، دعونا نتعرف على ما يتعين علينا القيام به. في الإخراج ، نريد الحصول على تطبيق يحتوي على بيانات مرجعية وخريطة. والعمل دون اتصال. كمطور ، أنا مهتم في المقام الأول بالخريطة فقط ، لأننا نعرف بالفعل كيفية عرض البيانات المرجعية. يعد وضع عدم الاتصال حصرًا قويًا للغاية في هذه الحالة ، لأنه لا توجد العديد من المكتبات الجيدة التي تدعم الدعم في وضع عدم الاتصال. لذلك ، في المقال سنركز على الخريطة ، لكن دعنا نتحدث عن الدليل أثناء المرور.
اختيار محرك خريطة
أول شيء فعله هو الحصول على بيانات التطبيق. هناك العديد من المصادر في السوق ، مجانية وليست جدا. لبداية ، OpenStreetMap مناسب تمامًا لنا كمصدر مفتوح لبيانات الخريطة. هناك يمكنك أن تأخذ عددًا معينًا من النقاط المهمة لدليلنا .
والخطوة التالية هي اختيار محرك بطاقة. على الإنترنت ، يوجد عدد قليل منهم ، حتى عدد أقل منها مجانًا ، وبدعم غير متصل بالإنترنت ، يوجد عدد قليل منهم عمومًا. أقترح استخدام خيار رائع جدا - mapsforge / vtm . هذا محرك OpenGL متجه ، ذكي للغاية ، ويدعم في وضع عدم الاتصال ، Android ، iOS ، مصادر بيانات مختلفة ، تصميم مخصص ، تراكبات ، علامات ، نماذج ثلاثية الأبعاد وحتى ثلاثية الأبعاد للكائنات! رائع جدا
يحتوي المستودع على العديد من الأمثلة للبدء السريع ، وهناك بطاقات جاهزة ، وهناك مكون إضافي يسمح لك بتجميع البطاقة الخاصة بك من البيانات بتنسيق OSM. لذلك ، دعونا نبدأ!
MapView mapView = findViewById(R.id.map_view); this.map = mapView.map(); File baseMapFile = getMapFile("cyprus.map"); MapFileTileSource tileSource = new MapFileTileSource(); tileSource.setMapFile(baseMapFile.getAbsolutePath()); VectorTileLayer layer = this.map.setBaseMap(tileSource); MapInfo info = tileSource.getMapInfo(); if (info != null) { MapPosition pos = new MapPosition(); pos.setByBoundingBox(info.boundingBox, Tile.SIZE * 4, Tile.SIZE * 4); this.map.setMapPosition(pos); } this.map.setTheme(VtmThemes.DEFAULT); this.map.layers().add(new BuildingLayer(this.map, layer)); this.map.layers().add(new LabelLayer(this.map, layer));
قم بإنشاء مصدر بيانات MapFileTileSource ، وحدد موقع ملف الخريطة. بالإضافة إلى ذلك ، نضع أنفسنا في وسط المربع المحيط الذي يهمنا ، حتى لا نكون في مكان ما خارج الموقع المحدد عند بدء تشغيل التطبيق. تثبيت السمة الافتراضية. أضف طبقة من المنازل وطبقة من التواقيع. هذا كل شيء. نطلق - المعجزات!
يبدو أسرع وأسهل ولا يمكن أن يكون.
نحن نفعل الترميز الجغرافي
الخطوة المهمة التالية هي تنفيذ الترميز الجغرافي. البطاقة نفسها جيدة بالفعل ، لكن التفاعل ضروري. نريد الاستفادة من الخريطة ومشاهدة المعلومات حول الكائن الذي ضربناه. وهناك بعض الصعوبة. إلى حد كبير ، لا يوجد الترميز الجغرافي الكامل في مكتبتنا. ربما هذا هو أكبر ناقص. إذا لم يخترع أي شيء ، فيمكننا الاستفادة من الوظائف الحالية.
اتضح مطول نسبيا. تحتاج إلى العثور على مربع ، والحصول على طرق (في مصطلحات OSM ، هذا كائن خطي) ، ويمكنك استخراج بعض السمات منها. بالإضافة إلى الطرق ، من الممكن الحصول على نقطة اهتمام ، لكن هذا كله. سوف تضطر إلى إنهاء بقية المنطق بنفسك: اختر "الصحيح" من مجموعة الكائنات بأكملها التي تضغط عليها النقرة ، قم بالتصفية حسب مستويات التكبير / التصغير. وأكثر شيء واحد. في الواقع ، فإننا نفقد المعلومات المتعلقة بالهندسة الأصلية ونستجيب للبحث فقط بمجموعة من الخطوط. إذا كنت تريد أيضًا إنشاء محرر جغرافي ، فمن الواضح أن هذا لن يكون كافيًا.
ولكن لإظهار النهج ، كل شيء يناسبنا.
الترميز الجغرافي المتقدم
بشكل عام ، هناك خيار أكثر تقدما. لهذا نحن بحاجة إلى قاعدتنا الخاصة. على وجه الخصوص ، يمكنك استخدام سكليتي. صحيح أن SQLite القياسي لن يكون كافياً بالنسبة لنا ، وسيتعين علينا بناء منطقتنا من خلال توصيل المكوّن الإضافي RTree للبحث الجغرافي به. كيفية القيام بذلك ، قلت بالفعل في المقال ، قسم "القيام بعملية بحث جيدة".
في هذه الحالة ، نتحكم بشكل كامل في البيانات ، ويمكننا حفظ كل ما هو مطلوب ، وفي التنسيق الصحيح. يمكننا أيضًا ربط "بحث النص الكامل" والبحث عن الكائنات الجغرافية والشركات لدينا بالاسم والعنوان والسمات الأخرى.
الاتجاه هو:
- نحن نصنع الجداول:
- نحن نملأ كل شيء بالبيانات.
- عند النقر فوق الخريطة ، نحصل على GeoPoint وننفذ الطلب:
SELECT id FROM geo_index WHERE minX>=-81.08 AND maxX<=-80.58 AND minY>=35.00 AND maxY<=35.44
- الخطوة الأخيرة: تصفية وتحديد الكائن المناسب.
يمكن الاطلاع على أحد خيارات التنفيذ في المستودع .
نتيجة لذلك ، نحن نعرف بالفعل كيفية عرض الخريطة والتعامل مع النقرات. ليس سيئا
أضف أشياء صغيرة مهمة.
دعنا نضيف اثنين من الميزات المهمة.
لنبدأ بالموقع الحالي. في mapsforge / vtm هناك فقط خاص لهذا الغرض. طبقة طبقة الموقع. استخدام بسيط للغاية.
LocationLayer locationLayer = new LocationLayer(this.map); locationLayer.setEnabled(true);
هناك عيب واحد فقط - إنه تموج ثابت لـ "النقطة الزرقاء" على حدود الشاشة عندما يكون الموقع الحالي خارج الخريطة. على الأرجح ، في عملية الاستخدام ، نادراً ما تجد نفسك في مثل هذا الموقف ، ولكن هذا يؤدي إلى إعادة تقديم مستمرة ، وبالتالي ، فإنه يقوم بتحميل المعالج قليلاً. للتخلص من هذا هو أكثر صعوبة ، تحتاج إلى الدخول في التظليل وإصلاحه. ولكن هذا هو بالفعل بالنسبة للكمال. كيف تفعل - يمكنك أن ترى هنا .
لذلك ، فإن الموقف هو. حان الوقت لإضافة زر التنقل إلى الموضع الحالي ، كما هو الحال في جميع تطبيقات رسم الخرائط التي تحترم نفسها.
View vLocation = findViewById(R.id.am_location); vLocation.setOnClickListener(v -> this.map.animator().animateTo(initialGeoPoint));
نحن بحاجة أيضا أزرار التكبير.
View vZoomIn = findViewById(R.id.am_zoom_in); vZoomIn.setOnClickListener(v -> this.map.animator().animateZoom(500, 2, 0, 0)); View vZoomOut = findViewById(R.id.am_zoom_out); vZoomOut.setOnClickListener(v -> this.map.animator().animateZoom(500, 0.5, 0, 0));
والكرز على الكعكة هو بوصلة.
View vCompass = findViewById(R.id.am_compass); vCompass.setVisibility(View.GONE); vCompass.setOnClickListener(v -> { MapPosition mapPosition = this.map.getMapPosition(); mapPosition.setBearing(0); this.map.animator().animateTo(500, mapPosition); vCompass.animate().setListener(new Animator.AnimatorListener() { @Override public void onAnimationStart(Animator animation) { } @Override public void onAnimationEnd(Animator animation) { vCompass.setVisibility(View.GONE); } @Override public void onAnimationCancel(Animator animation) { } @Override public void onAnimationRepeat(Animator animation) { } }).setDuration(500).rotation(0).start(); }); this.map.events.bind((e, mapPosition) -> { if (e == Map.ROTATE_EVENT) { vCompass.setRotation(mapPosition.getBearing()); vCompass.setVisibility(View.VISIBLE); } });
الاستيلاء على العالم
أصدقاء ، نحن في خط النهاية. يبقى لإضافة اللمسات الأخيرة. نحن نخطط للاستيلاء على العالم ، مما يعني أننا بحاجة إلى حشره بطريقة ما في تطبيقنا.
والأشياء من هذا القبيل مع محركنا هو أسهل بكثير مما يبدو.
نحتاج إلى تعديل طريقة تحميل الخريطة قليلاً عن طريق إضافة MultyMapTileSource إليها. هذا بشكل أساسي عبارة عن غلاف لأي مصادر تجانب أخرى ، مما يسمح لك بعرض كل ما يضاف إليها على الخريطة في وقت واحد. مجرد ميزة القاتل. ونتيجة لذلك ، يبقى لنا إعداد خريطة للعالم بأقل قدر من التفاصيل ، وإضافتها أولاً إلى غلافنا ، ورسم الباقي على القمة. علاوة على ذلك ، يمكننا على الفور إضافة جميع البطاقات التي لدينا في الكتالوج مع خرائط للتطبيق! رائع ، فقط رائع. ولا تنسى أنه غير متصل :)
ربما نحن مستعدون للافراج عنهم. نقوم بجمع البناء ، ووضعه في السوق والحصول على النجوم بجدارة :)
زوجان من ملاعق القطران في برميل ضخم من العسل
يتطور محرك المصدر المفتوح بنشاط ، لكن فريقه بصراحة متواضع إلى حد ما. بشكل عام ، هذا شخص واحد تحت اسم devemux86 . واثنين من الرجال أكثر من وقت لآخر.
في بعض الأحيان ، هناك بعض القطع الأثرية في التقديم ، بعضها وميض وخز. لكنني لم أواجه أي مشاكل حرجة وأي حوادث أخرى لا يمكن إلا أن أفرح.
هناك فارق بسيط آخر قد لا يكون لطيفًا. هذا هو رسم من الشرائح والدوائر. مثال على كيفية ظهور هذا في لقطة الشاشة:
إذا كان هناك الكثير من النقاط في الشكل الهندسي الأولي (التقريب سلسة) ، فيمكنك على الخريطة أن ترى دائرة "زاويّة" تحتوي على العديد من الانتفاخات والتقرحات الصغيرة. من الواضح أن هذا يتم من أجل الأداء وحجم ملف الخريطة ، لكنه لا يبدو جيدًا للغاية.
ربما هذا هو كل السلبيات لهذا اليوم. عليك أن تقرر ما إذا كان يمكنك العيش معهم أم لا. في هذه الأثناء ، نستخدم هذه المكتبة منذ أكثر من 1.5 عام ، فالطيران ممتاز ، على الأقل على نظام Android.
النتائج
لقد أوضحت في هذه المقالة أنه حتى هذه المشكلة غير التافهة يمكن حلها بسرعة نسبية. لديك هيكل عظمي جاهز ، يمكنك من خلاله وضع نموذج أولي لأي مشروع يتضمن استخدام خريطة غير متصلة بالإنترنت في أقل وقت ممكن.
إذا كان هناك اهتمام ، فسأعرض في المقالة التالية كيفية جعل الطوابق 2GIS. وهذا في الواقع أبسط بكثير مما يبدو :)