نحن نربط الخرائط عبر الإنترنت مع المستكشف على الهاتف الذكي. الجزء 2 - بطاقات مكافحة ناقلات

نكتب تطبيق خادم سيؤدي إلى إنشاء مربعات خطوط المسح PNG استنادًا إلى خرائط المتجهات عبر الإنترنت. استخدم تجريف الويب باستخدام Puppeteer للحصول على بيانات الخريطة.


المحتويات:


1 - مقدمة. الخرائط النقطية القياسية
2 - استمرار. كتابة أداة تنقيط بسيطة لخرائط المتجهات
3 - حالة خاصة. نقوم بتوصيل بطاقة OverpassTurbo


تمديد


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


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


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


ومع ذلك ، إذا لم تنجح هذه الخدعة ، لكنك لا تزال ترغب حقًا في الحصول على هذه البطاقة ، علاوة على ذلك ، ليس في متصفح الهاتف الذكي ، وبالتحديد في متصفحك ، فهناك طريقة.


الفكرة الرئيسية


سوف ننطلق من حقيقة أننا نريد الحصول على خريطة يمكن فتحها في أي من الملاحين. ثم نحتاج إلى محول - وهو نوع من الوسيط الذي سينشئ مربعات لنا بتنسيق PNG.


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


لالتقاط لقطات شاشة ، سأستخدم "متصفح التحكم عن بعد" - Headless Chrome. يمكنك التحكم فيه باستخدام العقدة js library Puppeteer . يمكنك التعرف على أساسيات العمل مع هذه المكتبة من هذه المقالة .


مرحبا العالم! أو إنشاء وتخصيص مشروع


إذا لم تقم بتثبيت Node.js بعد ، فانتقل إلى هذه الصفحة أو هذه الصفحة ، وحدد نظام التشغيل الخاص بك وقم بإجراء التثبيت وفقًا للتعليمات.


قم بإنشاء مجلد جديد للمشروع وافتحه في الجهاز.


$ cd /Mapshoter_habr 

نبدأ مدير إنشاء مشروع جديد


 $ npm init 

هنا يمكنك تحديد اسم المشروع ( اسم الحزمة ) ، واسم الملف لإدخال التطبيق ( نقطة الدخول ) واسم المؤلف ( المؤلف ). بالنسبة لجميع الطلبات الأخرى ، نحن نوافق على المعايير الافتراضية: لا ندخل شيئًا ونضغط فقط على Enter . في النهاية - اضغط y وأدخل .


بعد ذلك ، قم بتثبيت الأطر اللازمة للعمل. Express لإنشاء خادم و Puppeteer للعمل مع متصفح.


 $ npm install express $ npm i puppeteer 

نتيجة لذلك ، تظهر حزمة ملف تكوين المشروع في مجلد المشروع. في حالتي ، هذا:


 { "name": "mapshoter_habr", "version": "1.0.0", "description": "", "main": "router.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "author": "nnngrach", "license": "ISC", "dependencies": { "express": "^4.17.1", "puppeteer": "^1.18.1" } } 

سأضيف سطر البدء إلى قسم البرامج النصية لبدء تطبيقنا بشكل أكثر ملاءمة.


 "scripts": { "start": "node router.js", "test": "echo \"Error: no test specified\" && exit 1" }, 

الآن قم بإنشاء ملفين باستخدام الوظيفة الأساسية. الملف الأول هو نقطة الدخول إلى التطبيق. في حالتي ، router.js . سيقوم بإنشاء خادم والقيام بالتوجيه.


 //        const express = require( 'express' ) const mapshoter = require( './mapshoter' ) //  ,       const PORT = process.env.PORT || 5000 //     const app = express() app.listen( PORT, () => { console.log( '    ', PORT ) }) //       // http://siteName.com/x/y/z app.get( '/:x/:y/:z', async ( req, res, next ) => { //      const x = req.params.x const y = req.params.y const z = req.params.z //      const screenshot = await mapshoter.makeTile( x, y, z ) //        const imageBuffer = Buffer.from( screenshot, 'base64' ) //    res.writeHead( 200, { 'Content-Type': 'image/png', 'Content-Length': imageBuffer.length }) //    res.end( imageBuffer ) }) 

الآن قم بإنشاء ملف ثاني. سوف يتحكم في المتصفح ويأخذ لقطات شاشة. لدي ما يسمى mapshoter.js .


 const puppeteer = require( 'puppeteer' ) async function makeTile( x, y, z ) { //   const browser = await puppeteer.launch() //       const page = await browser.newPage() await page.goto( 'https://www.google.ru/' ) //    const screenshot = await page.screenshot() //      await browser.close() return screenshot } module.exports.makeTile = makeTile 

تشغيل البرنامج النصي لدينا والتحقق من أدائها. للقيام بذلك ، اكتب في وحدة التحكم:


$ npm start


تظهر رسالة تقول "تم إنشاء الخادم على المنفذ 5000". افتح الآن متصفحًا على جهاز الكمبيوتر الخاص بك وانتقل إلى العنوان المحلي لخادمنا. بدلاً من إحداثيات x و y و z ، يمكنك إدخال أي أرقام. دخلت 1 و 2 و 3.


http://localhost:5000/1/2/3


إذا تم كل شيء بشكل صحيح ، فستظهر لقطة شاشة لموقع Google.


صورة


اضغط على Ctrl + C في وحدة التحكم لإيقاف البرنامج النصي لدينا.


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


احسب الاحداثيات


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


لكن أولاً ، تحتاج إلى حساب إحداثيات مركز التجانب استنادًا إلى الرقم التسلسلي. سأفعل ذلك بناءً على صيغة العثور على الزاوية اليسرى العليا. أضعه في وظيفة getCoordinates () .


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


 //   -   function getCoordinates( x, y, z ) { const n = Math.pow( 2, z ) const lon = x / n * 360.0 - 180.0 const lat = 180.0 * ( Math.atan( Math.sinh( Math.PI * ( 1 - 2 * y / n) ) ) ) / Math.PI return { lat: lat, lon: lon } } //          function getCenter( left, rigth, top, bottom ) { let lat = ( left + rigth ) / 2 let lon = ( top + bottom ) / 2 return { lat: lat, lon: lon } } //        function getAllCoordinates( stringX, stringY, stringZ ) { //      const x = Number( stringX ) const y = Number( stringY ) const z = Number( stringZ ) //     //    -  -  const topLeft = getCoordinates( x, y, z ) const bottomRight = getCoordinates( x+1, y+1, z ) //   const center = getCenter( topLeft.lat, bottomRight.lat, topLeft.lon, bottomRight.lon ) //   const bBox = { latMin: bottomRight.lat, lonMin: topLeft.lon, latMax: topLeft.lat, lonMax: bottomRight.lon } return { bBox: bBox, center: center } } module.exports.getAllCoordinates = getAllCoordinates 

نحن الآن على استعداد لبدء تنفيذ البرنامج النصي للعمل مع المتصفح. دعونا نلقي نظرة على سيناريوهات قليلة حول كيفية القيام بذلك.


السيناريو 1 - بحث API


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


https://nakarte.me/#m=5/50.28144/89.30666&l=O/Wp


دعنا ننظر إلى البرنامج النصي. فقط قم باستبدال وحذف المحتويات الكاملة لملف mapshoter.js والصق الكود أدناه.


في هذا الإصدار ، عند بدء تشغيل المتصفح ، نحدد معلمات إضافية تسمح له بالبدء والعمل على خوادم Linux ، مثل Heroku. أيضًا ، سنقوم أيضًا بتقليل حجم النافذة بحيث يتناسب عدد قليل من مربعات الخريطة على الشاشة. وبالتالي ، نحن زيادة سرعة تحميل الصفحة.


بعد ذلك ، نحسب إحداثيات مركز البلاط المطلوب. نلصقها في عنوان URL ونضغط عليها. يظهر التجانب تمامًا في وسط الشاشة. قص قطعة من 256 × 256 بكسل. سيكون هذا البلاط الذي نحتاجه. يبقى فقط لإعادته إلى المستخدم.


قبل الانتقال إلى التعليمات البرمجية ، لاحظ أنه من أجل الوضوح ، تمت إزالة كل معالجة الأخطاء من البرنامج النصي.


 const puppeteer = require( 'puppeteer' ) const geoTools = require( './geoTools' ) async function makeTile( x, y, z ) { //    ,    Heroku const herokuDeploymentParams = {'args' : ['--no-sandbox', '--disable-setuid-sandbox']} const browser = await puppeteer.launch( herokuDeploymentParams ) //        //       const page = await browser.newPage() await page.setViewport( { width: 660, height: 400 } ) //         URL const coordinates = geoTools.getAllCoordinates( x, y, z ) const centerCoordinates = `${z}/${coordinates.center.lat}/${coordinates.center.lon}&l=` const pageUrl = 'https://nakarte.me/#m=' + centerCoordinates + "O/Wp" //   URL  ,    await page.goto( pageUrl, { waitUntil: 'networkidle0', timeout: 20000 } ) //    const cropOptions = { fullPage: false, clip: { x: 202, y: 67, width: 256, height: 256 } } const screenshot = await page.screenshot( cropOptions ) //      await browser.close() return screenshot } module.exports.makeTile = makeTile 

الآن قم بتشغيل السيناريو الخاص بنا وشاهد خريطة هذا القسم.


http://localhost:5000/24/10/5


إذا تم كل شيء بشكل صحيح ، فيجب أن يعرض الخادم مثل هذا التجانب:



للتأكد من أننا لا نخلط أي شيء عند الاقتصاص ، قارن بين البلاط والأصل من OpenStreetMaps.org



السيناريو 2 - البحث باستخدام واجهة الموقع


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


ألاحظ أنه عادة بعد البحث يتم ضبط نفس المقياس. 15 ، على سبيل المثال. في مثالنا ، هذا لا يحدث دائمًا. لذلك ، سوف نتعرف على مستوى التكبير / التصغير من معلمات عناصر html في الصفحة.


في هذا المثال أيضًا ، سنبحث عن عناصر واجهة باستخدام محددات XPath. ولكن كيف تتعرف عليهم؟


للقيام بذلك ، افتح الصفحة المطلوبة في المستعرض وافتح شريط أدوات المطور ( Ctll + Alt + I لـ Google Chrome). اضغط على الزر لتحديد العناصر. نضغط على العنصر الذي تهتم به (لقد قمت بالنقر فوق حقل البحث).



يتم تمرير قائمة العناصر إلى العنصر الذي نقرت عليه وسيتم تمييزه باللون الأزرق. انقر على الزر مع ثلاث نقاط على يسار الاسم.


من القائمة المنبثقة ، حدد نسخ. بعد ذلك ، إذا كنت بحاجة إلى محدد عادي ، فانقر فوق نسخ محدد . ولكن على سبيل المثال ، سنستخدم عنصر نسخ XPath .



استبدل الآن محتويات ملف mapshoter.js بهذا الرمز. في ذلك ، لقد جمعت بالفعل محددات لجميع عناصر الواجهة الضرورية.


 const puppeteer = require( 'puppeteer' ) const geoTools = require( './geoTools' ) async function makeTile( x, y, z ) { //      const searchFieldXPath = '//*[@id="map"]/div[1]/div[1]/div/input' const zoomPlusXPath = '//*[@id="map"]/div[2]/div[2]/div[4]/div[1]/a[1]' const zoomMinusXPath = '//*[@id="map"]/div[2]/div[2]/div[4]/div[1]/a[2]' const directionButonXPath = '//*[@id="gtm-poi-card-get-directions"]' const deletePinButonXPatch = '//*[@id="map"]/div[1]/div/div/div[1]/div[2]/div/div[4]/div/div[4]' //         () const coordinates = geoTools.getAllCoordinates( x, y, z ) const centerCoordinates = `lat=${coordinates.center.lat} lng=${coordinates.center.lon}` //      const herokuDeploymentParams = {'args' : ['--no-sandbox', '--disable-setuid-sandbox']} const browser = await puppeteer.launch( herokuDeploymentParams ) const page = await browser.newPage() await page.setViewport( { width: 1100, height: 450 } ) //         const pageUrl = 'https://www.waze.com/en/livemap?utm_campaign=waze_website' await page.goto( pageUrl, { waitUntil: 'networkidle2', timeout: 10000 } ) //    ,      await click( searchFieldXPath, page ) //        await page.keyboard.type( centerCoordinates ) //  Enter    page.keyboard.press( 'Enter' ); //  500     await page.waitFor( 500 ) //       //       await click( directionButonXPath, page ) await page.waitFor( 100 ) await click( deletePinButonXPatch, page ) await page.waitFor( 100 ) //       //        while( z > await fetchCurrentZoom( page )) { await click( zoomPlusXPath, page ) await page.waitFor( 300 ) } while( z < await fetchCurrentZoom( page )) { await click( zoomMinusXPath, page ) await page.waitFor( 300 ) } //    const cropOptions = { fullPage: false, clip: { x: 422, y: 97, width: 256, height: 256 } } const screenshot = await page.screenshot( cropOptions ) //   await browser.close() return screenshot } //  : //        async function click( xPathSelector, page ) { await page.waitForXPath( xPathSelector ) const foundedElements = await page.$x( xPathSelector ) if ( foundedElements.length > 0 ) { await foundedElements[0].click() } else { throw new Error( "XPath element not found: ", xPathSelector ) } } //         html  async function fetchCurrentZoom( page ) { const xPathSelector = '//*[@id="map"]/div[2]' await page.waitForXPath( xPathSelector ) const elems = await page.$x(xPathSelector) const elementParams = await page.evaluate((...elems) => { return elems.map(e => e.className); }, ...elems); const zoom = elementParams[0].split('--zoom-').pop() return zoom } module.exports.makeTile = makeTile 

قم بتشغيل البرنامج النصي لدينا واتبع الرابط. إذا تم كل شيء بشكل صحيح ، فسيعود إلينا النص البرمجي بشيء من هذا القبيل.


http://localhost:5000/1237/640/11



الأمثل


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


ولكن هناك أيضا عيوب. وأهمها هو سرعة العمل. فقط قارن. في المتوسط ​​، يستغرق تنزيل بلاط نقطي واحد منتظم حوالي 0.5 ثانية. أثناء تلقي بلاطة واحدة من البرنامج النصي لدينا في الوقت الراهن يستغرق حوالي 8 ثوان.


لكن هذا ليس كل شيء! إننا نستخدم js عقدة مفردة ، وستعمل طلباتنا الطويلة على حظر الخيط الرئيسي ، والذي سيبدو من الخارج كقائمة انتظار متزامنة منتظمة. وعندما نحاول تنزيل الخريطة للشاشة بأكملها (على سبيل المثال ، يتم وضع 24 مربعًا) ، فهناك خطر حدوث مشكلة.


وأكثر شيء واحد. لدى بعض الملاحين مهلة: سيتوقفون عن التحميل بعد 30 ثانية. وهذا يعني أنه مع التطبيق الحالي فقط 3-4 بلاط سيكون لديك الوقت لتحميل. حسنًا ، دعنا نرى ما يمكننا فعله حيال ذلك.


ربما تكون الطريقة الأكثر وضوحًا هي زيادة عدد الخوادم التي سيتم تشغيل البرنامج النصي لدينا عليها. على سبيل المثال ، إذا كان لدينا 10 خوادم ، فسيكون لديهم وقت لمعالجة البلاط للشاشة بأكملها في 30 ثانية. (إذا كنت لا ترغب في دفع الكثير من المال ، يمكنك الحصول عليه عن طريق تسجيل العديد من الحسابات المجانية على Heroku)


ثانياً ، لا يزال من الممكن تطبيق تعدد العمليات على العقدة js باستخدام الوحدة النمطية worker_threads . وفقًا لملاحظاتي ، في خادم به معالج أحادي النواة على حساب Heroku مجاني ، أتمكن من بدء تشغيل ثلاثة مؤشرات ترابط. ثلاثة تدفقات مع متصفح منفصل في كل منها ، والتي يمكن أن تعمل في وقت واحد دون حظر بعضها البعض. في الإنصاف ، ألاحظ أنه نتيجة لزيادة الحمل على المعالج ، زادت سرعة تنزيل أحد البلاط قليلاً. ومع ذلك ، إذا حاولت تنزيل خريطة للشاشة بأكملها ، فبعد 30 ثانية سيكون هناك أكثر من نصف الخريطة ليتم تحميلها. أكثر من 12 البلاط. بالفعل أفضل.


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


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


$ npm install node-worker-threads-pool --save


صحح ملف router.js. أضف إلى ذلك إنشاء تجمع مؤشر ترابط. المواضيع ستكون 3 قطع. سيتم وصف الكود الخاص بهم في ملف worker.js ، وسوف ننظر إليه لاحقًا. في غضون ذلك ، احذف إطلاق وحدة لقطة الشاشة مباشرةً. بدلاً من ذلك ، سنضيف مهمة جديدة إلى تجمع مؤشرات الترابط. سيبدأون بمعالجتها عند تحرير أي من سلاسل الرسائل.


 const express = require( 'express' ) const PORT = process.env.PORT || 5000 const app = express() app.listen( PORT, () => { console.log( '    ', PORT ) }) //   . const { StaticPool } = require( 'node-worker-threads-pool' ) const worker = "./worker.js" const workersPool = new StaticPool({ size: 3, task: worker, workerData: "no" }) app.get( '/:x/:y/:z', async ( req, res, next ) => { const x = req.params.x const y = req.params.y const z = req.params.z //       //       const screenshot = await workersPool.exec( { x, y, z } ) const imageBuffer = Buffer.from( screenshot, 'base64' ) res.writeHead( 200, { 'Content-Type': 'image/png', 'Content-Length': imageBuffer.length }) res.end( imageBuffer ) }) 

الآن نلقي نظرة على ملف worker.js . في كل مرة تصل مهمة جديدة ، سيتم تشغيل الأسلوب parentPort.on () . لسوء الحظ ، لا يمكنها معالجة وظائف المزامنة / الانتظار. لذلك سوف نستخدم وظيفة المحول في شكل طريقة doMyAsyncCode () .


في ذلك في شكل مناسب للقراءة ، سنضع منطق العامل. أي ، قم بتشغيل المستعرض (إذا لم يكن قيد التشغيل بالفعل) وقم بتنشيط طريقة التقاط لقطة شاشة. عند بدء التشغيل ، سننقل إلى هذه الطريقة رابطًا إلى المتصفح قيد التشغيل.


 const { parentPort, workerData } = require( 'worker_threads' ); const puppeteer = require( 'puppeteer' ) const mapshoter = require( './mapshoter' ) //     var browser = "empty" //         //    ,     parentPort.on( "message", ( params ) => { doMyAsyncCode( params ) .then( ( result) => { parentPort.postMessage( result ) }) }) //  ,    async/aswit //     async function doMyAsyncCode( params ) { //      await prepareEnviroment() //     const screenshot = await mapshoter.makeTile( params.x, params.y, params.z, browser ) return screenshot } //  .     ,    async function prepareEnviroment( ) { if ( browser === "empty" ) { const herokuDeploymentParams = {'args' : ['--no-sandbox', '--disable-setuid-sandbox']} browser = await puppeteer.launch( herokuDeploymentParams ) } } 

للتوضيح ، دعنا نعود إلى الإصدار الأول من mapshoter.js . لن تتغير كثيرا. الآن في معلمات الإدخال ، سيتم قبول رابط إلى المتصفح ، وعندما ينتهي البرنامج النصي ، لن يتم إيقاف تشغيل المتصفح ، ولكن ببساطة يغلق علامة التبويب التي تم إنشاؤها.


 const puppeteer = require( 'puppeteer' ) const geoTools = require( './geoTools' ) async function makeTile( x, y, z, browserLink ) { //      const browser = await browserLink //      const page = await browser.newPage() await page.setViewport( { width: 660, height: 400 } ) const coordinates = geoTools.getAllCoordinates( x, y, z ) const centerCoordinates = `${z}/${coordinates.center.lat}/${coordinates.center.lon}&l=` const pageUrl = 'https://nakarte.me/#m=' + centerCoordinates + "O/Wp" await page.goto( pageUrl, { waitUntil: 'networkidle0', timeout: 20000 } ) const cropOptions = { fullPage: false, clip: { x: 202, y: 67, width: 256, height: 256 } } const screenshot = await page.screenshot( cropOptions ) //   .   . await page.close() return screenshot } module.exports.makeTile = makeTile 

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


استنتاج


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


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


أيضًا ، تتضمن مزايا هذا البرنامج النصي أنه من السهل التعامل معه. من السهل الكتابة. والأهم من ذلك ، يمكن إعادة توصيل أي بطاقة أخرى عبر الإنترنت بسهولة بالغة.


حسنًا ، في المقالة التالية سأتعامل مع ذلك تمامًا. سوف أقوم بتحويل البرنامج النصي إلى نوع من واجهة برمجة التطبيقات للعمل مع الخريطة التفاعلية OverpassTurbo.

Source: https://habr.com/ru/post/ar461053/


All Articles