كيفية إنشاء خرائط باستخدام React و Leaflet

نشرت: 2022-03-10
ملخص سريع ↬ يعتبر Leaflet أداة قوية للغاية ، ويمكننا إنشاء العديد من أنواع الخرائط المختلفة. سيساعدك هذا البرنامج التعليمي على فهم كيفية إنشاء خريطة متقدمة بمساعدة React و Vanilla JS.

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

في هذا البرنامج التعليمي ، سنستخدم الأدوات التالية:

  • منشور
    مكتبة JavaScript للخرائط التفاعلية
  • تتفاعل
    مكتبة JavaScript لبناء واجهات المستخدم
  • رد فعل النشرة
    مكونات التفاعل لخرائط المنشورات

ما هو الكتيب؟

تعد Leaflet.js ، التي يبلغ عددها حوالي 27 ألف نجمة ، واحدة من مكتبات JavaScript مفتوحة المصدر الرائدة للخرائط التفاعلية المتوافقة مع الجوّال. يستفيد من HTML5 و CSS3 على المتصفحات الحديثة بينما يمكن الوصول إليه على المتصفحات القديمة أيضًا. الكل في الكل ، يدعم جميع منصات سطح المكتب والأجهزة المحمولة الأساسية.

نشرة تزن حوالي 38 كيلو بايت وتعمل بشكل مثالي مع الأشياء الأساسية. للحصول على امتدادات إضافية ، يمكن تمديدها بكمية كبيرة من المكونات الإضافية.

تستخدم الكثير من الصحف ، بما في ذلك NPR ، و Washington Post ، و Boston Globe ، وغيرها من المنظمات ، Leaflet لمشاريع البيانات المتعمقة الخاصة بهم.

على سبيل المثال ، قامت صحيفة سان فرانسيسكو كرونيكل بمشروع يسمى California Fire tracker - خريطة تفاعلية توفر معلومات عن حرائق الغابات المشتعلة في جميع أنحاء كاليفورنيا ، باستخدام Leaflet. لم يقتصر الأمر على تحديد مصدر الحريق فحسب ، بل أظهروا لنا أيضًا مساره.

نظرًا لأن هذا برنامج تعليمي تمهيدي ، سنقوم فقط بتحديد مواقع حوادث الحريق وعرض بعض التفاصيل عنها.

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

المزيد بعد القفز! أكمل القراءة أدناه ↓

أولاً ، لنقم بإنشاء ملفات index.html و app.js في مجلد /project وربط الأخير بملف index.html الخاص بنا.

لبدء استخدام Leaflet ، نحتاج إلى ربط Leaflet CSS و Leaflet JS في علامات الرأس الخاصة بنا. شيء واحد يجب مراعاته هو أن Leaflet CSS يأتي قبل Leaflet JS. هذا كل شيء من أجل المنشور.

هناك شيء آخر نحتاج إلى إضافته إلى ملف index.html - حاوية ستحتوي على خريطتنا.

 <div></div>

قبل أن ننسى ، دعونا نعطي ارتفاعًا لـ div.

 #mapid { height: 1000px; }

الآن يأتي الجزء الممتع. سواء قررت إنشاء ملف JavaScript جديد أو الاستمرار في علامات البرنامج النصي ، تأكد من إضافة <div id="mapid"> إلى dom قبل استدعاء L.map('mapid') .

ربما تسأل "ولكن ، لماذا؟" حسنًا ، لأنه سيعطيك خطأ إذا ربطت الخريطة بحاوية غير موجودة بعد.

 Uncaught Error: Map container not found

إنشاء خريطة

الآن ، في الجزء الممتع. لتهيئة الخريطة ، نقوم بتمرير div إلى L.map() مع بعض الخيارات.

 const myMap = L.map('mapid', { center: [37.7749, -122.4194], zoom: 13 })

دعنا نذهب خطوة بخطوة لفهم ما حدث للتو. نحن نستخدم فئة Map of Leaflet API لإنشاء خريطة على الصفحة. نقوم بتمرير معاملين لهذه الفئة:

  1. مررنا متغير سلسلة يمثل معرف DOM
  2. كائن اختياري حرفي مع خيارات الخريطة

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

بالنسبة للمركز ، مررنا إحداثيات سان فرانسيسكو. هناك الكثير من الأماكن التي يمكننا فيها إجراء الترميز الجغرافي للأمام والعكس ، ولكن بالنسبة للبحث الأساسي مثل هذا ، يمكننا البحث في google.

عادةً ما تعتمد قيمة التكبير على ما تريد عرضه. هل تريد إظهار مدينة أم ولاية؟ بلد أم قارة؟ انطلق والعب مع قيمة التكبير / التصغير للحصول على فكرة أفضل. في هذا المثال ، اخترنا 13 لأنه يظهر المدينة بأكملها.

هناك طريقة أخرى لتهيئة الخريطة وهي استخدام setView (). يأخذ في مجموعة من الإحداثيات وعدد صحيح لمستوى التكبير / التصغير.

 const myMap = L.map('map').setView([37.7749, -122.4194], 13);

بشكل افتراضي ، يتم تمكين جميع تفاعلات الماوس واللمس على الخريطة ، ولديها عناصر تحكم في التكبير / التصغير والإسناد.

خلق طبقة

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

لإنشاء طبقة تجانب ، نحتاج إلى تعيين قالب عنوان URL لصورة التجانب ونص الإحالة ومستوى التكبير الأقصى للطبقة. قالب URL هو ما يمنحنا الوصول إلى طبقة التجانب المرغوبة من مزود الخدمة. نظرًا لأننا نستخدم واجهة برمجة تطبيقات Static Tiles من Mapbox ، فسنحتاج إلى طلب رمز وصول.

 L.tileLayer('https://api.mapbox.com/styles/v1/{id}/tiles/{z}/{x}/{y}?access_token={accessToken}', { attribution: 'Map data © <a href="https://www.openstreetmap.org/">OpenStreetMap</a> contributors, <a href="https://creativecommons.org/licenses/by-sa/2.0/">CC-BY-SA</a>, Imagery (c) <a href="https://www.mapbox.com/">Mapbox</a>', maxZoom: 18, id: 'mapbox/streets-v11', accessToken: 'your.mapbox.access.token' }).addTo(mymap);

في هذه المرحلة ، إذا فتحنا index.html في متصفح ، ينبغي أن نتمكن من رؤية خريطة سان فرانسيسكو. دعنا نضع دبوسًا على الخريطة.

علامات ودوائر

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

لتثبيت موقع ، نقوم بإنشاء مثيل للعلامة باستخدام فئة Marker ، وتمرير الإحداثيات ، وإضافتها إلى الخريطة. نحن هنا نستخدم إحداثيات Twin Peaks في المدينة.

 const marker = L.marker([37.7544, -122.4477]).addTo(mymap);

وبالمثل ، يمكننا ربط الدائرة بالخريطة باستخدام فئة Circle . نمرر في عدد قليل من الخيارات الاختيارية ، مثل نصف القطر واللون وما إلى ذلك. بالنسبة لعلامة circle ، نقوم بتمرير إحداثيات Point Bonita Lighthouse.

 const circle = L.circle([37.8157, -122.5295], { color: 'gold', fillColor: '#f03', fillOpacity: 0.5, radius: 200 }).addTo(mymap);

تظهر ظهور مفاجئ

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

 circle.bindPopup("I am pointing to Point Bonita Lighthouse"); marker.bindPopup("I am pointing to Twin Peaks");

تأخذ طريقة bindPopup محتوى HTML محددًا وتقوم بإلحاقه بالعلامة ، بحيث تظهر النافذة المنبثقة عند النقر فوق العلامة.

رد فعل النشرة

الآن نحن نعرف كيفية إنشاء خريطة وإضافة علامات باستخدام Leaflet و Vanilla JavaScript. دعونا نرى كيف يمكننا تحقيق نفس النتائج باستخدام React. لن نقوم بعمل نفس التطبيق ولكن بدلاً من ذلك سنقوم بعمل تطبيق متقدم.

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

الوصول إلى مفتاح API

  1. قم بإنشاء حساب وقم بتسجيل الدخول إلى البوابة.
  2. انقر فوق ارتباط الإدارة باتجاه أسفل اليمين.
  3. انقر فوق إنشاء مفتاح API جديد وقم بتسميته.
  4. انسخ معرّف المفتاح وسر المفتاح. ستحتاج هذا للوصول إلى البيانات.

لهذا ، سوف نستخدم React-Leaflet - مكونات تفاعل لخرائط المنشورات. لنقم بإنشاء تطبيق يتفاعل.

 npx create-react-app react-fire-incidents cd react-fire-incidents

ثم دعنا react-leaflet التفاعلية والنشرة عن طريق تشغيل الأمر التالي في جهازك:

 npm install react-leaflet leaflet

App.js

لنقم بإنشاء مجلد /components داخل src . داخل المكونات ، دعنا ننشئ ملفًا باسم Map.js. هذا هو المكان الذي ستعيش فيه خريطتنا. الآن دعنا نعدل App.js عن طريق إزالة الكود غير الضروري واستيراد الوحدات من محاور react-leaflet axios و Map.js المنشأة حديثًا.

 import React, { Component, Fragment } from 'react'; import axios from 'axios'; import Map from './components/Map'

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

 class App extends Component { state = { incidents: [], } render() { return ( <div> </div> ); } } export default App;

بعد ذلك ، سنقوم بتقديم طلب GET عند تركيب المكون. لدينا رمز التطبيق ، لكننا ما زلنا بحاجة إلى نقطة نهاية. أين نجد نقطة النهاية؟

دعنا نتوجه إلى البوابة ونضغط على تصفح البيانات. في شريط البحث ، دعنا نبحث عن حوادث الحريق. النتيجة الأولى التي تظهر هي ما نبحث عنه. بمجرد النقر فوق الارتباط ، يمكننا الحصول على عنوان URL عن طريق النقر فوق الزر API في أعلى اليمين.

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

بمجرد أن نحصل على البيانات ، نقوم بتحديث حالتنا.

 async componentDidMount() { const res = await axios.get('https://data.sfgov.org/resource/wr8u-xric.json', { params: { "$limit": 500, "$$app_token": YOUR_APP_TOKEN } }) const incidents = res.data; this.setState({incidents: incidents }); };

هذا ما يجب أن يبدو عليه App.js.

 class App extends Component { state = { incidents: [], } async componentDidMount() { const res = await axios.get('https://data.sfgov.org/resource/wr8u-xric.json', { params: { "$limit": 500, "$$app_token": YOUR_APP_TOKEN } }) const incidents = res.data; this.setState({incidents: incidents }); }; render() { return ( <Map incidents={this.state.incidents}/> ); } } export default App;

Map.js

نظرًا لأننا نعرف بالفعل كيفية إنشاء خريطة Leaflet ، فسيكون هذا الجزء سهلاً نسبيًا. سنقوم باستيراد مكونات Map و TileLayer و Marker و Popup من react-leaflet .

 import React, { Component } from 'react' import { Map, TileLayer, Marker, Popup } from 'react-leaflet'

إذا تذكرنا من المثال السابق ، فنحن بحاجة إلى إحداثيات ومستوى تكبير / تصغير لتهيئة الخريطة. في فئة Map الخاصة بنا ، نحددها في حالتنا باستخدام متغيرات lat و lng و zoom .

 export default class Map extends Component { state = { lat: 37.7749, lng: -122.4194, zoom: 13, } render() { return ( <div></div> ) } }

ثم سوف نتحقق مما إذا كانت مجموعة الحوادث لدينا فارغة. إذا كانت فارغة ، فسنقوم بإرجاع رسالة مفادها "يتم تحميل البيانات" ؛ خلاف ذلك ، سنعود خريطة.

في مكون Map react-leaflet بنا ، سنقوم بتمرير إحداثيات المركز ومستوى التكبير مع بعض التصميم. في مكون TileLayer بنا ، سنمرر الإحالة وعنوان URL مشابهًا لمثالنا السابق.

 render() { return ( this.props.incidents ? <Map center={[this.state.lat, this.state.lng]} zoom={this.state.zoom} style={{ width: '100%', height: '900px'}} > <TileLayer attribution='&copy <a href="https://osm.org/copyright">OpenStreetMap</a> contributors' url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png" /> </Map> : 'Data is loading...' ) } }

بعد ذلك ، نعقد حلقة على props.incident إحداثيات كل حادث إلى مكون Marker. نظرًا لأن React تحذرنا من تمرير مفتاح لكل عنصر في مصفوفة ، فسنمرر مفتاحًا إلى Marker أيضًا.

داخل مكون Marker ، نقوم بتمرير مكون Popup . لقد أضفت بعض المعلومات حول الحادث داخل النافذة المنبثقة.

 <Map center={[this.state.lat, this.state.lng]} zoom={this.state.zoom} style={{ width: '100%', height: '900px'}}> <TileLayer attribution='&copy <a href="https://osm.org/copyright">OpenStreetMap</a> contributors' url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png" /> { this.props.incidents.map(incident => { const point = [incident['point']['coordinates'][1], incident['point']['coordinates'][0]] return ( <Marker position={point} key={incident['incident_number']} > <Popup> <span>ADDRESS: {incident['address']}, {incident['city']} - {incident['zip_code']}</span> <br/> <span>BATTALION: {incident['battalion']}</span><br/> </Popup> </Marker> ) }) } </Map>

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

تغليف

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