العمل مع GeoJSON في Node.js: مقدمة عملية

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



نقاط - كائنات نقطة


تبدو نقطة GeoJSON كما يلي:

{
  "type": "Point",
  "coordinates": [-80.1347334, 25.7663562]
}

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


نقطة على الخريطة من

المهم ملاحظة أن الإحداثيات في العقارcoordinatesمكتوبة في التنسيق[lng, lat]. يأتي خط الطول في GeoJSON قبل خط العرض . وذلك لأن خط الطول يمثل الاتجاه بين الشرق والغرب (المحورxعلى خريطة نموذجية) ، ويمثل خط العرض الاتجاه بين الشمال والجنوب (المحورyعلى خريطة نموذجية). سعى مؤلفو GeoJSON للحفاظ على تنسيق النظامx, y.

من الأمثلة النموذجية على استخدام نقاط GeoJSON الترميز الجغرافي - ترجمة العناوين مثل "429 Lenox Ave، Miami Beach، FL" إلى إحداثيات يتم التعبير عنها بخط الطول وخط العرض. على سبيل المثال ، نستخدم APIالترميز الجغرافي Mapbox. للوصول إلى واجهة برمجة التطبيقات هذه ، يجب إجراء طلب HTTP إلى نقطة النهاية التالية:

https://api.mapbox.com/geocoding/v5/mapbox.places/429%20lenox%20ave%20miami.json?access_token=pk.eyJ1IjoibWF0dGZpY2tlIiwiYSI6ImNqNnM2YmFoNzAwcTMzM214NTB1NHdwbnoifQ.Or19S7KmYPHW8YjRz82v6g&cachebuster=1581993735895&autocomplete=true

ردًا على ذلك ، سيأتي الرمز التالي:

{"type":"FeatureCollection","query":["429","lenox","ave","miami"],"features":[{"id":"address.8052276751051244","type":"Feature","place_type":["address"],"relevance":1,"properties":{"accuracy":"rooftop"},"text":"Lenox Avenue","place_name":"429 Lenox Avenue, Miami Beach, Florida 33139, United States","center":[-80.139145,25.77409],"geometry":{"type":"Point","coordinates":[-80.139145,25.77409]}, ...}

إذا نظرت عن كثب إلى الإجابة ، اتضح أنها features[0].geometryفي رمز JSON هي نقطة GeoJSON:

{"type":"Point","coordinates":[-80.139145,25.77409]}


إن تصور إحداثيات الخرائط الثابتة

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

const axios = require('axios');

async function search(str) {
  const geocoderUrl = 'https://api.mapbox.com/geocoding/v5/mapbox.places/' +
    encodeURIComponent(str) +
    '.json?access_token=' +
    'pk.eyJ1IjoibWF0dGZpY2tlIiwiYSI6ImNqNnM2YmFoNzAwcTMzM214NTB1NHdwbnoifQ.Or19S7KmYPHW8YjRz82v6g';

  const res = await axios.get(geocoderUrl).then(res => res.data);
  const point = res.features[0].geometry;

  return 'https://api.mapbox.com/styles/v1/mapbox/streets-v11/static/' +
    'pin-l-1+333(' + point.coordinates[0] + ',' + point.coordinates[1] + ')/' +
    point.coordinates[0] + ',' + point.coordinates[1] +
    ',14.25,0,0/600x600/' +
    '?access_token=pk.eyJ1IjoibWF0dGZpY2tlIiwiYSI6ImNqNnM2YmFoNzAwcTMzM214NTB1NHdwbnoifQ.Or19S7KmYPHW8YjRz82v6g';
}

search('429 Lenox Ave, Miami Beach').then(res => console.log(res));


مثال على تصور نقطة على الخريطة

الخطوط - كائنات LineString


في خطوط GeoJSON ، LineStringتمثل الكائنات صفائف من الإحداثيات التي تصف خطًا على الخريطة. فيما يلي كائن GeoJSON LineStringيمثل الحد التقريبي بين ولايتي كاليفورنيا وأوريغون في الولايات المتحدة الأمريكية:

{
  "type": "LineString",
  "coordinates": [[-124.2, 42], [-120, 42]]
}


يتم استخدام عرض LineString على

خطوط الخريطة ، باستخدام واجهة برمجة تطبيقات التنقل مثل Mapbox ، لتقديم مسار خطوة بخطوة بين نقطتين. إحدى الطرق لتمثيل طريق من نقطة[-80.139145,25.77409](مكتب WeWork في ميامي بيتش) إلى نقطة[-80.2752743,25.7938434](مطار ميامي الدولي) هي استخدام كائن GeoJSONLineString:

{
  "type": "LineString",
  "coordinates": [
    [-80.139153, 25.774281],
    [-80.13829, 25.774307],
    [-80.142029, 25.774479],
    [-80.148438, 25.772148],
    [-80.151237, 25.772232],
    [-80.172043, 25.78116],
    [-80.177322, 25.787195],
    [-80.185326, 25.787212],
    [-80.189804, 25.785891],
    [-80.19268, 25.785954],
    [-80.202301, 25.789175],
    [-80.207954, 25.788721],
    [-80.223, 25.782646],
    [-80.231026, 25.78261],
    [-80.238007, 25.784889],
    [-80.246025, 25.784403],
    [-80.249611, 25.785175],
    [-80.253166, 25.786049],
    [-80.259262, 25.786324],
    [-80.264038, 25.786186],
    [-80.264221, 25.787256],
    [-80.264214, 25.791618],
    [-80.264221, 25.792633],
    [-80.264069, 25.795443],
    [-80.263397, 25.795652],
    [-80.263786, 25.794928],
    [-80.267723, 25.794926],
    [-80.271141, 25.794859],
    [-80.273163, 25.795704],
    [-80.275009, 25.796482],
    [-80.277481, 25.796461],
    [-80.278435, 25.795622],
    [-80.278061, 25.794088],
    [-80.275276, 25.793804]
  ]
}

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


المسار من نقطة إلى أخرى

إليك نص برمجي بسيط يعيدLineStringتمثيل المسار بين نقطتين باستخدام واجهة برمجة تطبيقاتdirectionsMapbox.

const axios = require('axios');

async function directions(fromPt, toPt) {
  const fromCoords = fromPt.coordinates.join(',');
  const toCoords = toPt.coordinates.join(',');
  const directionsUrl = 'https://api.mapbox.com/directions/v5/mapbox/driving/' +
    fromCoords + ';' + toCoords + '?' +
    'geometries=geojson&' +
    'access_token=pk.eyJ1IjoibWF0dGZpY2tlIiwiYSI6ImNqNnM2YmFoNzAwcTMzM214NTB1NHdwbnoifQ.Or19S7KmYPHW8YjRz82v6g';

  const res = await axios.get(directionsUrl).then(res => res.data);
  return res.routes[0].geometry;
}

const wework = { type: 'Point', coordinates: [-80.139145,25.77409] };
const airport = { type: 'Point', coordinates: [-80.2752743,25.7938434] };

directions(wework, airport).then(res => {
  console.log(res);
});

المضلعات - كائنات المضلع


تُستخدم مضلعات GeoJSON والأشياء Polygonلوصف المناطق المغلقة على الخرائط. يمكن أن تكون هذه مناطق على شكل مثلث أو مربع أو دوديكاجون أو أي شكل آخر مع عدد ثابت من الجوانب. على سبيل المثال ، يصف كائن GeoJSON التالي تقريبًا حدود ولاية كولورادو في الولايات المتحدة الأمريكية:

{
  "type": "Polygon",
  "coordinates": [[
    [-109, 41],
    [-102, 41],
    [-102, 37],
    [-109, 37],
    [-109, 41]
  ]]
}


تصور مضلع على الخريطة

يمكن استخدام مضلعات GeoJSON لوصف الأشكال المعقدة للغاية. على سبيل المثال ، استخدمت شركة أوبر لبعض الوقت أرض التدريب GeoJSON الوحيدة ، والتي تشمل جميع المطارات الرئيسية الثلاثة في منطقة خليج سان فرانسيسكو.


مضلع GeoJSON المعقد

صحيح ، يجب ملاحظة أن مضلعات GeoJSON لا يمكن أن تمثل دوائر وعلامات حذف.

ما هي المضلعات المستخدمة؟ عادة - لوصف الأسوار الجغرافية . على سبيل المثال ، تخيل أنك تعمل في Uber أو في Lyft. يجب أن تُظهر للمستخدمين الذين يحجزون رحلات من المطار شاشة خاصة. للقيام بذلك ، ستحتاج إلى معرفة ما إذا كانت النقطة التي يتم حجز الرحلة منها تقع ضمن النطاق الذي يصف المطار (أو عدة مطارات كما في الشكل السابق).

تتمثل إحدى طرق التحقق من وجود نقطة GeoJSON داخل المضلع في استخدام وحدة Turpm npm.تتيح لك الوحدة @turf/boolean-point-in-polygon معرفة ما إذا كانت النقطة داخل المضلع.

const pointInPolygon = require('@turf/boolean-point-in-polygon').default;

const colorado = {
  "type": "Polygon",
  "coordinates": [[
    [-109, 41],
    [-102, 41],
    [-102, 37],
    [-109, 37],
    [-109, 41]
  ]]
};

const denver = {
  "type": "Point",
  "coordinates": [-104.9951943, 39.7645187]
};

const sanFrancisco = {
  "type": "Point",
  "coordinates": [-122.4726194, 37.7577627]
};

// true
console.log(pointInPolygon(denver, colorado));

// false
console.log(pointInPolygon(sanFrancisco, colorado));

تتيح لك حزمة Turf معرفة ما إذا كانت النقطة داخل المضلع باستخدام Node.js. ولكن ماذا لو كنا مهتمين بالحصول على نفس المعلومات عن طريق تنفيذ الاستعلامات لقاعدة البيانات؟ في هذه الحالة ، يجب أن تدرك أن عبارة MongoDB المضمنة $geoIntersectsتدعم GeoJSON. لذلك ، على سبيل المثال ، يمكنك كتابة استعلام يتيح لك معرفة الحالة الأمريكية التي تتوافق مع نقطة معينة على الخريطة:

const mongoose = require('mongoose');

run().catch(err => console.log(err));

async function run() {
  await mongoose.connect('mongodb://localhost:27017/geotest', {
    useNewUrlParser: true,
    useUnifiedTopology: true
  });
  await mongoose.connection.dropDatabase();

  const State = mongoose.model('State', mongoose.Schema({
    name: String,
    location: mongoose.Schema({
      type: String,
      coordinates: [[[Number]]]
    })
  }));

  const colorado = await State.create({
    name: 'Colorado',
    location: {
      "type": "Polygon",
      "coordinates": [[
        [-109, 41],
        [-102, 41],
        [-102, 37],
        [-109, 37],
        [-109, 41]
      ]]
    }
  });

    const denver = {
    "type": "Point",
    "coordinates": [-104.9951943, 39.7645187]
  };

  const sanFrancisco = {
    "type": "Point",
    "coordinates": [-122.4726194, 37.7577627]
  };

  //     ?
  let res = await State.findOne({
    location: {
      $geoIntersects: { $geometry: denver }
    }
  });
  res.name; // 

  //     -?
  res = await State.findOne({
    location: {
      $geoIntersects: { $geometry: sanFrancisco }
    }
  });
  res; // null
}

ملخص


GeoJSON ليس فقط تخزين إحداثيات النقاط. يمكنك تخزين المسارات بهذا التنسيق. باستخدام بيانات GeoJSON ، يمكنك معرفة متى دخل مستخدم السياج الجغرافي. وإذا لزم الأمر ، فإن GeoJSON يسمح لك أيضًا بإنشاء إسوكرونات . حول تنسيق GeoJSON ، تم تشكيل مجموعة من الأدوات الرائعة. لذا ، يتيح لك المورد geojson.io أداء تصور بسيط للإحداثيات على الخريطة. يوفر مشروع Mapbox الوصول إلى واجهات برمجة التطبيقات الجغرافية المتقدمة. تتيح لك حزمة Turf إجراء الحوسبة الجغرافية المكانية في المتصفحات وفي Node.js.

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

القراء الأعزاء! هل تستخدم تنسيق GeoJSON؟


All Articles