طلب البحث وعرض بيانات الموقع في BigQuery باستخدام Google Maps Platform (JavaScript)

1. نظرة عامة

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

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

العناصر التي سيتم إنشاؤها

في هذا الدرس التطبيقي حول الترميز، ستكتب بعض طلبات البحث وتعمل على توضيح كيفية تقديم إحصاءات تستند إلى الموقع الجغرافي لمجموعات البيانات العلنية الكبيرة جدًا باستخدام BigQuery. وستنشئ أيضًا صفحة ويب تحمِّل خريطة باستخدام واجهة برمجة تطبيقات JavaScript لمنصة "خرائط Google"، ثم تُشغِّل طلبات البحث المكانية وتعرضها بدلاً من مجموعات البيانات العامة الكبيرة جدًا باستخدام مكتبة برامج Google APIs للغة JavaScript وواجهة برمجة تطبيقات BigQuery.

ما ستتعرَّف عليه

  • كيفية طلب البحث في مجموعات بيانات الموقع على نطاق بيتابايت في ثوانٍ باستخدام BigQuery، باستخدام طلبات بحث SQL والوظائف من تحديد المستخدم وواجهة برمجة تطبيقات BigQuery
  • كيفية استخدام منصة خرائط Google لإضافة خريطة من Google إلى صفحة ويب وتمكين المستخدمين من رسم أشكال عليها
  • كيفية تمثيل طلبات البحث تمثيلاً بصريًا لمجموعات البيانات الكبيرة على "خرائط Google"، كما هو موضّح في نموذج الصورة أدناه، والذي يوضّح كثافة انسحاب سيارات الأجرة في عام 2016 من الرحلات التي بدأت من الكتلة المحيطة بمبنى "إمباير ستيت".

لقطة شاشة يوم 2017-05-09 الساعة 11.01.12 صباحًا.png

الأشياء التي تحتاج إليها

  • معرفة أساسية بلغة HTML وCSS وJavaScript وSQL وChrome DevTools
  • متصفح ويب حديث، مثل الإصدارات الحديثة من Chrome أو Firefox أو Safari أو Edge.
  • محرِّر نصوص أو IDE من اختيارك

التكنولوجيا

BigQuery

أداة BigQuery هي خدمة إحصاءات البيانات في Google لمجموعات البيانات الكبيرة جدًا. وتحتوي على واجهة برمجة تطبيقات RESTful وتتوافق مع طلبات البحث المكتوبة بلغة SQL. إذا كانت لديك بيانات تتضمّن قيمًا لخط الطول وخط العرض، يمكن استخدام تلك البيانات للاستعلام عن بياناتك حسب الموقع الجغرافي. وتتمثّل الميزة في أنه يمكنك استكشاف مجموعات البيانات الكبيرة جدًا بغرض الاطّلاع على الأنماط بدون الحاجة إلى إدارة أي بنية أساسية لخوادم أو قواعد بيانات. يمكنك الحصول على إجابات عن أسئلتك خلال ثوانٍ معدودة بغض النظر عن حجم جداولك، وذلك باستخدام ميزة قابلية التوسّع الهائلة والبنية المُدارة لـ BigQuery.

منصة خرائط Google

يوفر "منصة خرائط Google" الوصول الآلي إلى بيانات الخريطة والمكان والمسار على Google. ويستخدمها حاليًا أكثر من مليوني موقع إلكتروني وتطبيق لتقديم خرائط مضمّنة وطلبات بحث مستندة إلى الموقع الجغرافي للمستخدمين.

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

للبدء، تحتاج إلى مشروع على Google Cloud Platform تم تفعيل BigQuery وواجهات برمجة تطبيقات "خرائط Google" له.

2. البدء في الإعداد

حساب Google

إذا لم يكن لديك حساب على Google (Gmail أو Google Apps)، يجب إنشاء حساب.

إنشاء مشروع

سجِّل الدخول إلى وحدة تحكُّم Google Cloud Platform (console.cloud.google.com) وأنشئ مشروعًا جديدًا. في أعلى الشاشة، ستظهر قائمة منسدلة بالمشروع:

f2a353c3301dc649.png

بعد النقر على القائمة المنسدلة لهذا المشروع، ستحصل على عنصر قائمة يسمح لك بإنشاء مشروع جديد:

56a42dfa7ac27a35.png

في المربّع "يُرجى إدخال اسم جديد لمشروعك"، أدخِل اسمًا لمشروعك الجديد، على سبيل المثال:

Codelab - إنشاء مشروع (1).png

سيتم إنشاء رقم تعريف للمشروع لك. رقم تعريف المشروع هو اسم فريد في جميع مشاريع Google Cloud. تذكّر معرِّف المشروع، لأنك ستستخدمه لاحقًا. سبق أن تم استخدام الاسم أعلاه ولن يكون متاحًا لك. أدرِج رقم تعريف مشروعك في أي مكان يظهر فيه your_PROJECT_ID في هذا الدرس التطبيقي حول الترميز.

تفعيل الفوترة

للاشتراك في BigQuery، استخدِم المشروع المحدّد أو الذي تم إنشاؤه في الخطوة السابقة. يجب تفعيل الفوترة في هذا المشروع. بعد تفعيل الفوترة، يمكنك تفعيل BigQuery API.

تعتمد كيفية تفعيل الفوترة على ما إذا كنت تنشئ مشروعًا جديدًا أو تعيد تفعيل الفوترة لمشروع حالي.

تقدِّم Google فترة تجريبية مجانية لمدة 12 شهرًا بقيمة تصل إلى 300 دولار أمريكي من استخدام Google Cloud Platform، والتي يمكنك استخدامها في هذا الدرس التطبيقي للتعرُّف على مزيد من التفاصيل على https://cloud.google.com/free/.

مشاريع جديدة

عند إنشاء مشروع جديد، سيُطلب منك اختيار حسابات الفوترة التي تريد ربطها بالمشروع. إذا كان لديك حساب فوترة واحد فقط، يتم ربط هذا الحساب تلقائيًا بمشروعك.

إذا لم يكن لديك حساب فوترة، يجب إنشاء حساب وتفعيل الفوترة لمشروعك قبل أن تتمكن من استخدام العديد من ميزات Google Cloud Platform. لإنشاء حساب فوترة جديد وتفعيل الفوترة لمشروعك، اتّبع التعليمات الواردة في إنشاء حساب فوترة جديد.

المشاريع الحالية

إذا كان لديك مشروع أوقفت الفوترة مؤقتًا لأجله، يمكنك إعادة تفعيل الفوترة:

  1. انتقِل إلى وحدة تحكُّم Cloud Platform.
  2. من قائمة المشاريع، اختَر المشروع لإعادة تفعيل الفوترة له.
  3. افتح القائمة على يمين وحدة التحكّم واختَر الفوترة الفوترة. يُطلب منك اختيار حساب فوترة.
  4. انقر على ضبط الحساب.

إنشاء حساب فوترة جديد

لإنشاء حساب فوترة جديد:

  1. انتقِل إلى وحدة تحكُّم Cloud Platform وسجِّل الدخول، أو إذا لم يكن لديك حساب، اشترك.
  2. افتَح القائمة الجانبية اليمنى من وحدة التحكّم واختَر الفوترة الفوترة.
  3. انقر على زر حساب فوترة جديد. (ملاحظة: إذا لم يكن هذا حساب الفوترة الأول لك، عليك أولاً فتح قائمة حسابات الفوترة بالنقر على اسم حساب الفوترة الحالي بالقرب من أعلى الصفحة، ثم النقر على إدارة حسابات الفوترة.)
  4. أدخِل اسم حساب الفوترة وأدخِل معلومات الفوترة. وتعتمد الخيارات التي تراها على بلد عنوان إرسال الفواتير. لاحظ أنه بالنسبة إلى حسابات الولايات المتحدة، لا يمكنك تغيير الحالة الضريبية بعد إنشاء الحساب.
  5. انقر على إرسال وتفعيل الفوترة.

بشكل تلقائي، الشخص الذي ينشئ حساب الفوترة هو مشرف الفوترة للحساب.

للحصول على معلومات حول إثبات ملكية الحسابات المصرفية وإضافة طرق دفع احتياطية، يُرجى الاطّلاع على إضافة طريقة دفع أو إزالتها أو تعديلها.

تفعيل واجهة برمجة تطبيقات BigQuery

لتفعيل واجهة برمجة تطبيقات BigQuery في مشروعك، انتقِل إلى سوق صفحة واجهة برمجة تطبيقات BigQuery في وحدة التحكُّم وانقر على الزر الأزرق "تفعيل&#39؛

3- الاستعلام عن بيانات الموقع في BigQuery

هناك ثلاث طرق لطلب بيانات الموقع الجغرافي المخزَّنة كقيم خطوط طول وعرض في BigQuery.

  • طلبات البحث المستطيلة: يمكنك تحديد المنطقة محط الاهتمام كطلب بحث يحدد جميع الصفوف ضمن حد أدنى وحد أقصى لنطاق العرض.
  • طلبات البحث عن النطاق الجغرافي: يمكنك تحديد المنطقة محط الاهتمام عن طريق حساب دائرة حول نقطة باستخدام صيغة "هافيرسين" ودوال الرياضيات لوضع نموذج على شكل الأرض.
  • طلبات البحث المضلِّعة: حدِّد شكلًا مخصّصًا واستخدِم دالة مُحدَّدة من جانب المستخدم للتعبير عن منطق المضلّع المطلوب لاختبار ما إذا كان كل من خطوط العرض والطول يقعان داخل الشكل.

للبدء، استخدِم محرّر طلب البحث في قسم Big Query في وحدة تحكّم Google Cloud Platform لتشغيل طلبات البحث التالية مقابل بيانات سيارة الأجرة في نيويورك.

لغة الاستعلامات البنيوية (SQL) العادية مقابل لغة الاستعلامات البنيوية (SQL) القديمة

يتوافق BigQuery مع إصدارَين من SQL: لغة الاستعلامات البنيوية (SQL) القديمة ولغة الاستعلامات البنيوية (SQL) العادية. والشهادة الأخيرة هي معيار ANSI لعام 2011. لأغراض هذا البرنامج التعليمي، سنستخدم إصدار SQL العادي لأنه يتضمن معايير أفضل للامتثال.

إذا كنت تريد تنفيذ لغة الاستعلامات البنيوية (SQL) القديمة في محرر BigQuery، يمكنك إجراء ذلك:

  1. النقر على الزر "المزيد&#39؛
  2. تحديد "إعدادات طلب البحث&#39؛ من القائمة المنسدلة
  3. ضمن "SQL SQL &&39"، اختَر زر الاختيار "قديم":
  4. النقر على الزر "حفظ&#39؛

طلبات البحث المستطيلة

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

جرِّب المثال أدناه في وحدة تحكُّم BigQuery. تعرض طلبات البحث هذه بعض إحصاءات متوسط الرحلات التي بدأت في منطقة مستطيلة تحتوي على وسط مدينة مانهاتن وأقلها. هناك نوعان من المواقع المختلفة التي يمكنك تجربتها، يمكنك إلغاء التعليق على عبارة WHERE الثانية لتنفيذ الطلب على الرحلات التي بدأت في مطار أبو ظبي الدولي.

SELECT 
ROUND(AVG(tip_amount),2) as avg_tip, 
ROUND(AVG(fare_amount),2) as avg_fare, 
ROUND(AVG(trip_distance),2) as avg_distance, 
ROUND(AVG(tip_proportion),2) as avg_tip_pc, 
ROUND(AVG(fare_per_mile),2) as avg_fare_mile FROM

(SELECT 

pickup_latitude, pickup_longitude, tip_amount, fare_amount, trip_distance, (tip_amount / fare_amount)*100.0 as tip_proportion, fare_amount / trip_distance as fare_per_mile

FROM `bigquery-public-data.new_york_taxi_trips.tlc_yellow_trips_2015`

WHERE trip_distance > 0.01 AND fare_amount <100 AND payment_type = "1" AND fare_amount > 0
)

--Manhattan
WHERE pickup_latitude < 40.7679 AND pickup_latitude > 40.7000 AND pickup_longitude < -73.97 and pickup_longitude > -74.01

--JFK
--WHERE pickup_latitude < 40.654626 AND pickup_latitude > 40.639547 AND pickup_longitude < -73.771497 and pickup_longitude > -73.793755

توضّح نتائج طلبَي البحث أنّ هناك اختلافات كبيرة في متوسّط مسافة الرحلة ومعدّل الأجرة ونصيحة حول استلام الطلب في الموقعَين الجغرافيَين.

مانهاتن

avg_tips

avg_fare

avg_distance

avg_Tip_pc

avg_fare_mile

2.52

12.03

9.97

22.39

5.97

AUH

avg_tips

avg_fare

avg_distance

avg_Tip_pc

avg_fare_mile

9.22

48.49

41.19

22.48

4.36

طلبات بحث النطاق الجغرافي

من السهل أيضًا إنشاء طلبات بحث نصف قطرية في SQL إذا كنت تعرف بعض الرياضيات. باستخدام دالات الرياضيات القديمة في BigQuery&#39، يمكنك إنشاء طلب بحث SQL باستخدام صيغة هافرسين التي تحدد مساحة دائرية أو غطاء كروي على سطح الأرض.

في ما يلي مثال على BigQuery SQL لطلب بحث دائرة يتمركز في 40.73943, -73.99585 مع نصف قطر 0.1 كلم.

ويستخدم قيمة ثابتة تبلغ 111.045 كيلومترًا لتقريب المسافة التي تمثلها درجة واحدة.

ويعتمد هذا على مثال تم العثور عليه في http://www.plumislandmedia.net/mysql/haversine-mysql-nearest-loc/:

SELECT pickup_latitude, pickup_longitude, 
    (111.045 * DEGREES( 
      ACOS( 
        COS( RADIANS(40.73943) ) * 
        COS( RADIANS( pickup_latitude ) ) * 
        COS( 
          RADIANS( -73.99585 ) - 
          RADIANS( pickup_longitude ) 
        ) + 
        SIN( RADIANS(40.73943) ) * 
        SIN( RADIANS( pickup_latitude ) ) 
      ) 
     ) 
    ) AS distance FROM `project.dataset.tableName` 
    HAVING distance < 0.1 

تبدو لغة الاستعلامات البنيوية (SQL) لصيغة هافرسين معقدة، ولكن كل ما عليك فعله هو توصيل إحداثيات مركز الدائرة والنطاق الجغرافي والمشروع وأسماء مجموعة البيانات وجداول الجدول لأداة BigQuery.

في ما يلي مثال على طلب بحث يحسب بعض إحصاءات متوسط الرحلات للاستلام داخل 100 متر من مبنى إمباير ستيت. ويمكنك نسخ هذا النص ولصقه في وحدة تحكّم BigQuery على الويب للاطّلاع على النتائج. غيِّر خط العرض وخط الطول للمقارنة بين المناطق الأخرى، مثل الموقع الجغرافي في "برونكس".

#standardSQL
CREATE TEMPORARY FUNCTION Degrees(radians FLOAT64) RETURNS FLOAT64 AS
(
  (radians*180)/(22/7)
);

CREATE TEMPORARY FUNCTION Radians(degrees FLOAT64) AS (
  (degrees*(22/7))/180
);

CREATE TEMPORARY FUNCTION DistanceKm(lat FLOAT64, lon FLOAT64, lat1 FLOAT64, lon1 FLOAT64) AS (
     Degrees( 
      ACOS( 
        COS( Radians(lat1) ) * 
        COS( Radians(lat) ) *  
        COS( Radians(lon1 ) -  
        Radians( lon ) ) +  
        SIN( Radians(lat1) ) *  
        SIN( Radians( lat ) ) 
        ) 
    ) * 111.045
);

SELECT 

ROUND(AVG(tip_amount),2) as avg_tip,
ROUND(AVG(fare_amount),2) as avg_fare,
ROUND(AVG(trip_distance),2) as avg_distance,
ROUND(AVG(tip_proportion), 2) as avg_tip_pc,
ROUND(AVG(fare_per_mile),2) as avg_fare_mile

FROM

-- EMPIRE STATE BLDG 40.748459, -73.985731
-- BRONX 40.895597, -73.856085

(SELECT pickup_latitude, pickup_longitude, tip_amount, fare_amount, trip_distance, tip_amount/fare_amount*100 as tip_proportion, fare_amount / trip_distance as fare_per_mile, DistanceKm(pickup_latitude, pickup_longitude, 40.748459, -73.985731)


FROM `bigquery-public-data.new_york_taxi_trips.tlc_yellow_trips_2015`

WHERE 
  DistanceKm(pickup_latitude, pickup_longitude, 40.748459, -73.985731) < 0.1
  AND fare_amount > 0 and trip_distance > 0
  )
WHERE fare_amount < 100

وفي ما يلي نتائج طلب البحث. يمكنك ملاحظة أن هناك فروقًا كبيرة في متوسط الإكرامية والمسافة والمسافة المقطوعة والحجم النسبي للرأس مع الأجرة ومتوسط السعر لكل ميل تم قطعه.

مبنى إمباير ستيت:

avg_tips

avg_fare

avg_distance

avg_Tip_pc

avg_fare_mile

1.17

11 أغسطس

45.28

10.53

6.42

برونكس

avg_tips

avg_fare

avg_distance

avg_Tip_pc

avg_fare_mile

0.52

17.63

4.75

4.74

10.9

طلبات البحث المضلّعة

لا تتيح لغة الاستعلامات البنيوية (SQL) طلب البحث باستخدام الأشكال العشوائية بخلاف المستطيلات والدوائر. لا يحتوي BigQuery على أي نوع بيانات هندسية أصلية أو فهرس مكاني، لذا يجب تنفيذ نهج مختلف لإجراء طلبات بحث SQL بسيطة، وذلك عند تنفيذ طلبات بحث باستخدام الأشكال المضلّعة. أحد الأساليب هو تحديد دالة هندسية في JavaScript وتنفيذها كدالة من تحديد المستخدم (UDF) في BigQuery.

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

خوارزمية النقطة في المضلع

هناك طرق متعددة لاحتساب ما إذا كانت نقطة تقع داخل شكل مضلع في JavaScript. هنا هو أحد المنفذ من C من تنفيذ معروف يستخدم خوارزمية تتبُّع الشعاع لتحديد ما إذا كانت النقطة داخل مضلع أو خارجه عن طريق حساب عدد المرات التي يعبر فيها خط طويل لا نهاية له حد الشكل. ولا تستغرق هذه الميزة سوى بضعة أسطر من الرموز.

function pointInPoly(nvert, vertx, verty, testx, testy){
  var i, j, c = 0;
  for (i = 0, j = nvert-1; i < nvert; j = i++) {
    if ( ((verty[i]>testy) != (verty[j]>testy)) &&
                (testx < (vertx[j]-vertx[i]) * (testy-verty[i]) / (verty[j]-verty[i]) + vertx[i]) )
      c = !c;
  }
  return c;
}

نقل التطبيقات إلى JavaScript

تظهر نسخة JavaScript لهذه الخوارزمية على النحو التالي:

/* This function includes a port of C code to calculate point in polygon
* see http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html for license
*/

function pointInPoly(polygon, point){
    // Convert a JSON poly into two arrays and a vertex count.
    let vertx = [],
        verty = [],
        nvert = 0,
        testx = point[0],
        testy = point[1];
    for (let coord of polygon){
      vertx[nvert] = coord[0];
      verty[nvert] = coord[1];
      nvert ++;
    }

        
    // The rest of this function is the ported implementation.
    for (let i = 0, let j = nvert - 1; i < nvert; j = i++) {
      if ( ((verty[i] > testy) != (verty[j] > testy)) &&
         (testx < (vertx[j] - vertx[i]) * (testy - verty[i]) / (verty[j] - verty[i]) + vertx[i]) )
        c = !c;
    }
    return c;
}

عند استخدام لغة الاستعلامات البنيوية (SQL) العادية في BigQuery، يتطلب نهج UDF عبارة واحدة فقط، ولكن يجب تحديد UDF كدالة مؤقتة في البيان. إليك مثال على ذلك. الصِق عبارة SQL أدناه في نافذة "محرِّر طلب البحث".

CREATE TEMPORARY FUNCTION pointInPolygon(latitude FLOAT64, longitude FLOAT64)
RETURNS BOOL LANGUAGE js AS """
  let polygon=[[-73.98925602436066,40.743249676056955],[-73.98836016654968,40.74280666503313],[-73.98915946483612,40.741676770346295],[-73.98967981338501,40.74191656974406]];

  let vertx = [],
    verty = [],
    nvert = 0,
    testx = longitude,
    testy = latitude,
    c = false,
    j = nvert - 1;

  for (let coord of polygon){
    vertx[nvert] = coord[0];
    verty[nvert] = coord[1];
    nvert ++;
  }

  // The rest of this function is the ported implementation.
  for (let i = 0; i < nvert; j = i++) {
    if ( ((verty[i] > testy) != (verty[j] > testy)) &&
 (testx < (vertx[j] - vertx[i]) * (testy - verty[i]) / (verty[j] - verty[i]) + vertx[i]) ) {
      c = !c;
    }
  }

  return c;
""";

SELECT pickup_latitude, pickup_longitude, dropoff_latitude, dropoff_longitude, pickup_datetime
FROM `bigquery-public-data.new_york_taxi_trips.tlc_yellow_trips_2016`
WHERE pointInPolygon(pickup_latitude, pickup_longitude) = TRUE
AND (pickup_datetime BETWEEN CAST("2016-01-01 00:00:01" AS DATETIME) AND CAST("2016-02-28 23:59:59" AS DATETIME))
LIMIT 1000

تهانينا.

نفّذت ثلاثة أنواع من طلبات البحث المكاني باستخدام BigQuery. كما لاحظت، فإن الموقع يُحدث اختلافًا كبيرًا في بيانات النتائج لطلبات البحث مقابل مجموعة البيانات هذه، لكن ما لم تخمّن مكان تنفيذ طلبات بحثك، فإنه من الصعب اكتشاف الأنماط المكانية المخصصة باستخدام طلبات بحث SQL فقط.

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

4. العمل باستخدام Google Maps API

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

تفعيل واجهة برمجة تطبيقات JavaScript للخرائط

في هذا الدرس التطبيقي، عليك تفعيل واجهة برمجة تطبيقات JavaScript لمنصة "خرائط Google" في مشروعك. لإجراء ذلك، الرجاء القيام بما يلي:

  1. في وحدة تحكّم Google Cloud Platform، انتقِل إلى Marketplace.
  2. في السوق، ابحث عن "Maps JavaScript API'"
  3. النقر على العنوان لـ "واجهة برمجة تطبيقات JavaScript" للخرائط في نتائج البحث
  4. النقر على الزر "تفعيل&#39؛

إنشاء مفتاح واجهة برمجة تطبيقات

لتقديم طلبات إلى "منصة خرائط Google"، يجب إنشاء مفتاح واجهة برمجة التطبيقات وإرساله مع جميع الطلبات. يُرجى اتّباع الخطوات التالية لإنشاء مفتاح واجهة برمجة التطبيقات:

  1. في وحدة تحكّم Google Cloud Platform، انقر على قائمة الهمبورغر لفتح شريط التنقّل الأيمن.
  2. اختَر "واجهات برمجة التطبيقات &amp؛ الخدمة&#39؛ > "بيانات الاعتماد'.
  3. انقر على الزر "إنشاء بيانات اعتماد&#39؛ ثم اختر "مفتاح واجهة برمجة التطبيقات"&#39؛
  4. انسَخ مفتاح واجهة برمجة التطبيقات الجديد

تنزيل الرمز وإعداد خادم ويب

انقر على الزر التالي لتنزيل كل رموز هذا الدرس التطبيقي حول الترميز:

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

تحتوي مجلدات stepN على حالة النهاية المطلوبة لكل خطوة من هذا الدرس التطبيقي حول الترميز. وهي متاحة كمرجع. سنُنفِّذ كل أعمال الترميز في الدليل الذي يحمل اسم work.

إعداد خادم ويب محلي

على الرغم من أن بإمكانك استخدام خادم الويب الخاص بك، فإن هذا الدرس التطبيقي المُصمَّم للعمل بشكلٍ جيد مع خادم الويب Chrome. إذا لم يكن هذا التطبيق مثبتًا لديك بعد، يمكنك تثبيته من "سوق Chrome الإلكتروني".

بعد تثبيت التطبيق، افتحه في Chrome على النحو التالي:

  1. فتح Chrome
  2. في شريط العناوين بأعلى الشاشة، اكتب chrome://apps
  3. اضغط على Enter‏
  4. في النافذة التي تفتح، انقر على رمز خادم الويب. يمكنك أيضًا النقر بزر الماوس الأيمن على التطبيق لفتحه في علامة تبويب عادية أو مثبّتة، أو وضع ملء الشاشة، أو نافذة جديدة a3ed00e79b8bfee7.png سيظهر لك مربع الحوار التالي، الذي يسمح لك بإعداد خادم الويب المحلي: 81b6151c3f60c948.png
  5. النقر على "اختيار مجلد&#39"، ثم اختيار الموقع الذي تم فيه تنزيل نماذج الملفات الاختبارية للرمز
  6. في قسم "Options'"، ضع علامة في المربّع بجانب "عرض index.html&#39 تلقائيًا: 17f4913500faa86f.png".
  7. يمكنك تحريك مفتاح التبديل المسمى "خادم الويب: ابدأ و&#39؛ على اليمين ثم العودة إلى اليسار للإيقاف ثم إعادة تشغيل خادم الويب.

a5d554d0d4a91851.png

5. جارٍ تحميل أدوات الخريطة والرسم

إنشاء صفحة خريطة أساسية

ابدأ بصفحة HTML بسيطة تحمِّل خريطة Google باستخدام واجهة برمجة تطبيقات JavaScript للخرائط وبعض الأسطر عن JavaScript. يُعدّ الرمز من عيّنة الخريطة البسيطة على "منصة خرائط Google" مكانًا رائعًا للبدء. وتتم إعادة إنتاجه هنا لتتمكن من نسخه ولصقه في محرر النص أو IDE الذي تختاره، أو يمكنك العثور عليه من خلال فتح index.html من ملف repo الذي نزّلته.

  1. نسخ index.html إلى مجلد work في نسختك المحلية من الإعادة
  2. نسخ رمز الصورة/ المجلد إلى العمل/ المجلد في نسختك المحلية من الريبو
  3. فتح العمل/index.html في محرر النصوص أو IDE
  4. استبدال YOUR_API_KEY بمفتاح واجهة برمجة التطبيقات الذي أنشأته سابقًا
<script src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&callback=initMap"
    async defer></script>
  1. في المتصفّح الذي تستخدمه، افتح localhost:<port>/work حيث port هو رقم المنفذ المحدّد في إعداد خادم الويب المحلي. المنفذ التلقائي هو 8887. ومن المفترض أن ترى الخرائط الأولى المعروضة.

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

تغيير الموقع التلقائي ومستوى التكبير أو التصغير

الرمز الذي يحدد الموقع ومستوى التكبير/التصغير يقع في الخطوط 27 &amp؛ 28 من index.html، ويتمركز حاليًا في سيدني، أستراليا:

<script>
      let map;
      function initMap() {
        map = new google.maps.Map(document.getElementById('map'), {
          center: {lat: -34.397, lng: 150.644},
          zoom: 8
        });
      }
</script>

يعمل هذا البرنامج التعليمي مع بيانات رحلة سيارة أجرة BigQuery في نيويورك، لذا بعد ذلك ستُغيّر رمز إعداد الخريطة لتوسيطه في موقع جغرافي في مدينة نيويورك بمستوى تكبير مناسب - 13 أو 14 يجب أن يعمل بشكل جيد.

للقيام بذلك، عليك تحديث كتلة الرمز أعلاه إلى ما يلي لتوسيط الخريطة في مبنى "إمباير ستيت" وضبط مستوى التكبير/التصغير على 14:

<script>
      let map;
      function initMap() {
        map = new google.maps.Map(document.getElementById('map'), {
          center: {lat: 40.7484405, lng: -73.9878531},
          zoom: 14
        });
      }
</script>

وبعد ذلك، أعِد تحميل الخريطة في المتصفّح للاطّلاع على النتائج.

تحميل مكتبات الرسم والعرض

لإضافة إمكانات الرسم إلى خريطتك، ستغيّر النص البرمجي الذي يحمّل واجهة برمجة تطبيقات JavaScript لـ "خرائط Google" من خلال إضافة معلّمة اختيارية تطلب من "منصة خرائط Google" تفعيل مكتبة الرسم.

يستخدم هذا الدرس التطبيقي أيضًا الترميز HeatmapLayer، لذا يمكنك أيضًا تعديل النص البرمجي لطلب مكتبة التمثيل البصري. ولإجراء ذلك، أضِف المعلّمة libraries وحدِّد المكتبتَين visualization وdrawing كقيم مفصولة بفواصل، مثل libraries=visualization,drawing.

ومن المفترض أن يظهر الرقم بالشكل التالي:

<script src='http://maps.googleapis.com/maps/api/js?libraries=visualization,drawing&callback=initMap&key=YOUR_API_KEY' async defer></script>

إضافة مدير الرسم

لاستخدام الأشكال التي رسمها المستخدم كإدخال لطلب البحث، أضف DrawingManager إلى خريطتك مع تفعيل أدوات Circle وRectangle وPolygon.

من المفيد وضع كل رموز إعداد DrawingManager في وظيفة جديدة، لذا عليك تنفيذ ما يلي في نسختك من index.html:

  1. يمكنك إضافة دالة باسم setUpDrawingTools() باستخدام الرمز التالي لإنشاء DrawingManager وإعداد خاصية map للإشارة إلى كائن الخريطة في الصفحة.

تضبط الخيارات التي تم تمريرها إلى google.maps.drawing.DrawingManager(options) نوع الرسم التلقائي وأشكال العرض للأشكال المرسومة. لتحديد مناطق من الخريطة لإرسالها كطلبات بحث، يجب أن تكون الأشكال تعتيمة بقيمة صفر. لمعرفة المزيد من المعلومات حول الخيارات المتاحة، يمكنك الاطّلاع على خيارات "مدير الرسومات".

function setUpDrawingTools() {
  // Initialize drawing manager
  drawingManager = new google.maps.drawing.DrawingManager({
    drawingMode: google.maps.drawing.OverlayType.CIRCLE,
    drawingControl: true,
    drawingControlOptions: {
      position: google.maps.ControlPosition.TOP_LEFT,
      drawingModes: [
        google.maps.drawing.OverlayType.CIRCLE,
        google.maps.drawing.OverlayType.POLYGON,
        google.maps.drawing.OverlayType.RECTANGLE
      ]
    },
    circleOptions: {
      fillOpacity: 0
    },
    polygonOptions: {
      fillOpacity: 0
    },
    rectangleOptions: {
      fillOpacity: 0
    }
  });
  drawingManager.setMap(map);
}
  1. استدعاء setUpDrawingTools() في دالة initMap() بعد إنشاء كائن الخريطة
function initMap() {
  map = new google.maps.Map(document.getElementById('map'), {
    center: {lat: 40.744593, lng: -73.990370}, // Manhattan, New York.
    zoom: 12
  });

  setUpDrawingTools();
}
  1. أعد تحميل index.html وتأكد من توفر أدوات الرسم لديك. وتحقّق أيضًا من أنّه يمكنك استخدامها لرسم الدوائر والمستطيلات والأشكال المضلّعة.

يمكنك النقر والسحب لرسم الدوائر والمستطيلات ولكن يجب رسم المضلعات عن طريق النقر لكل رأس، والنقر مرّتين لإنهاء الشكل.

التعامل مع أحداث الرسم

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

سنضيف رمزًا لذلك في خطوة لاحقة، ولكن في الوقت الحالي، سيتم إيقاف ثلاثة معالجات أحداث فارغة للتعامل مع الأحداث rectanglecomplete وcirclecomplete وpolygoncomplete. ليس على المعالجات تشغيل أي رمز في هذه المرحلة.

أضف ما يلي إلى أسفل دالة setUpDrawingTools():

drawingManager.addListener('rectanglecomplete', rectangle => {
    // We will add code here in a later step.
});
drawingManager.addListener('circlecomplete', circle => {
  // We will add code here in a later step.
});

drawingManager.addListener('polygoncomplete', polygon => {
  // We will add code here in a later step.
});

يمكنك العثور على مثال صالح لهذا الرمز في نسختك المحلية من الإعادة في مجلد step2: step2/map.html.

6- استخدام واجهة برمجة تطبيقات BigQuery

ستساعدك واجهة برمجة تطبيقات عميل Google BigQuery في تجنب كتابة الكثير من الرموز النموذجية اللازمة لإنشاء الطلبات وتحليل الردود ومعالجة المصادقة. يستخدم هذا الدرس التطبيقي واجهة برمجة التطبيقات BigQuery في مكتبة برامج Google APIs للغة JavaScript لأنّنا سنعمل على تطوير تطبيق يستند إلى المتصفّح.

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

إضافة واجهة برمجة تطبيقات Google Client JavaScript

سوف تستخدم Google Client API لـ JavaScript لتنفيذ طلبات البحث مقابل BigQuery. في نسختك من index.html (في مجلد work)، حمِّل واجهة برمجة التطبيقات باستخدام علامة <script> على النحو التالي. ضع العلامة أسفل العلامة <script> التي تُحمِّل API للخرائط مباشرةً:

<script src='https://apis.google.com/js/client.js'></script>

بعد تحميل Google Client API، يمكنك تفويض المستخدم بالوصول إلى البيانات في BigQuery. ولإجراء ذلك، يمكنك استخدام OAuth 2.0. أولاً، يجب إعداد بعض بيانات الاعتماد في مشروع Google Cloud Console.

إنشاء بيانات اعتماد OAuth 2.0

  1. في Google Cloud Console، من قائمة التنقل، اختَر واجهات برمجة التطبيقات &amp؛ الخدمات &gt؛ بيانات الاعتماد.

قبل أن تتمكن من إعداد بيانات الاعتماد، عليك إضافة بعض الضبط لشاشة "التفويض" التي سيشاهدها المستخدم النهائي لتطبيقك عند السماح له بالوصول إلى بيانات BigQuery نيابةً عنه.

ولإجراء ذلك، انقر على علامة التبويب شاشة موافقة OAuth. 2. عليك إضافة واجهة برمجة تطبيقات Big Query إلى نطاقات هذا الرمز المميز. انقر على الزر إضافة نطاق في قسم "النطاقات في Google APIs". 3- من القائمة، ضع علامة في المربّع بجانب إدخال Big Query API بالنطاق ../auth/bigquery. 4. انقر على إضافة. 5. أدخِل اسمًا في الحقل "اسم التطبيق&#39؛ 6- انقر على حفظ لحفظ الإعدادات. 7- الخطوة التالية هي إنشاء معرّف عميل OAuth. ولإجراء ذلك، انقر على إنشاء بيانات اعتماد:

4d18a965fc760e39.png

  1. في القائمة المنسدلة، انقر على OAuth Client ID. 1f8b36a1c27c75f0.png
  2. ضمن نوع التطبيق، اختَر تطبيق ويب.
  3. في الحقل "اسم التطبيق"، اكتب اسمًا لمشروعك. على سبيل المثال، "BigQuery و"خرائط Google";
  4. ضمن القيود، في حقل "مصادر JavaScript المعتمَدة"، أدخِل عنوان URL للمضيف المحلي، بما في ذلك أرقام المنافذ. على سبيل المثال: http://localhost:8887
  1. انقر على الزرّ إنشاء.

ستظهر لك نافذة منبثقة معرِّف العميل وسر العميل. تحتاج إلى معرِّف العميل لإجراء المصادقة مقابل BigQuery. انسخه والصقه في work/index.html كمتغيّر JavaScript جديد يُسمى clientId.

let clientId = 'YOUR_CLIENT_ID';

7- التفويض والإعداد

ستحتاج صفحة الويب إلى تفويض المستخدم للدخول إلى BigQuery قبل إعداد الخريطة. في هذا المثال، نستخدم OAuth 2.0 كما هو موضح في قسم التفويض لمستندات واجهة برمجة تطبيقات JavaScript. عليك استخدام معرِّف عميل OAuth ورقم تعريف المشروع لإرسال طلبات البحث.

عند تحميل Google Client API في صفحة الويب، يجب تنفيذ الخطوات التالية:

  • تفويض المستخدم.
  • في حال التفويض بذلك، حمِّل واجهة برمجة تطبيقات BigQuery.
  • تحميل الخريطة وإعدادها.

ارجع إلى step3/map.html للحصول على مثال للشكل الذي ستظهر به صفحة HTML المنتهية.

تفويض المستخدم

يحتاج المستخدم النهائي للتطبيق إلى تفويض التطبيق للوصول إلى البيانات في BigQuery نيابةً عنه. وتتعامل Google Client API for JavaScript مع منطق OAuth لإجراء ذلك.

في التطبيق الحقيقي، لديك العديد من الخيارات حول كيفية دمج خطوة التفويض.

على سبيل المثال، يمكنك استدعاء authorize() من أحد عناصر واجهة المستخدم، مثل زر، أو تنفيذ ذلك عند تحميل الصفحة. لقد اخترنا هنا تفويض المستخدم بعد تحميل Google Client API في JavaScript، باستخدام دالة رد اتصال في الطريقة gapi.load().

اكتب بعض الرموز على الفور بعد العلامة <script> التي تحمِّل Google Client API لـ Javascript لتحميل كل من مكتبة العميل ووحدة المصادقة حتى نتمكن من مصادقة المستخدم على الفور.

<script src='https://apis.google.com/js/client.js'></script>
<script type='text/javascript'>
  gapi.load('client:auth', authorize);
</script>

عند التفويض، حمِّل واجهة برمجة تطبيقات BigQuery

بعد تفويض المستخدم، حمِّل BigQuery API.

أولاً، عليك استدعاء gapi.auth.authorize() مع المتغير clientId الذي أضفته في الخطوة السابقة. يمكنك معالجة الاستجابة في دالة رد اتصال تُسمى handleAuthResult.

تتحكم المعلمة immediate في ظهور نافذة منبثقة للمستخدم. يمكنك ضبط true على إيقاف النافذة المنبثقة للتفويض في حال سبق أن تم منح الإذن للمستخدم.

إضافة دالة إلى صفحتك باسم handleAuthResult(). يجب أن تستخدم الدالة معلَمة authresult التي ستسمح لك بالتحكّم في تدفق المنطق، استنادًا إلى ما إذا كان المستخدم مفوّضًا بنجاح أم لا.

أضِف أيضًا دالة تُسمى loadApi لتحميل BigQuery API في حال نجاح المستخدم في التفويض.

أضف المنطق في دالة handleAuthResult() لاستدعاء loadApi() إذا كان هناك كائن authResult تم تمريره إلى الدالة، وإذا كانت الخاصية error تحتوي على قيمة false.

أضف الرمز إلى الدالة loadApi() لتحميل واجهة برمجة التطبيقات BigQuery باستخدام الطريقة gapi.client.load().

let clientId = 'your-client-id-here';
let scopes = 'https://www.googleapis.com/auth/bigquery';

// Check if the user is authorized.
function authorize(event) {
  gapi.auth.authorize({client_id: clientId, scope: scopes, immediate: false}, handleAuthResult);
  return false;
}

// If authorized, load BigQuery API
function handleAuthResult(authResult) {
  if (authResult && !authResult.error) {
    loadApi();
    return;
  }
  console.error('Not authorized.')  
}

// Load BigQuery client API
function loadApi(){
  gapi.client.load('bigquery', 'v2');
}

تحميل الخريطة

وتتمثل الخطوة الأخيرة في إعداد الخريطة. عليك تغيير ترتيب المنطق قليلاً لتنفيذ هذا الإجراء. ويتم حاليًا إعداده عندما يتم تحميل JavaScript للخرائط.

ويمكنك إجراء ذلك باستدعاء الدالة initMap() من الطريقة then() بعد الطريقة load() على الكائن gapi.client.

// Load BigQuery client API
function loadApi(){
  gapi.client.load('bigquery', 'v2').then(
   () => initMap()
  );
}

8- مفاهيم واجهة برمجة تطبيقات BigQuery

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

الرمز الكامل لهذه الخطوة هو في step4/map.html.

جارٍ إرسال الطلب

يمكنك إضافة دالة JavaScript إلى work/index.html لإرسال طلب بحث باستخدام واجهة برمجة التطبيقات، وبعض المتغيرات لتخزين قيم مجموعة بيانات BigQuery والمشروع الذي يحتوي على الجدول لطلب البحث، ورقم تعريف المشروع الذي سيتم تحرير فاتورة له بأي رسوم.

let datasetId = 'your_dataset_id';
let billingProjectId = 'your_project_id';
let publicProjectId = 'bigquery-public-data';

function sendQuery(queryString){
  let request = gapi.client.bigquery.jobs.query({
      'query': queryString,
      'timeoutMs': 30000,
      'datasetId': datasetId,
      'projectId': billingProjectId,
      'useLegacySql':false
  });
  request.execute(response => {
      //code to handle the query response goes here.
  });
}

التحقق من حالة وظيفة

توضّح الدالة checkJobStatus أدناه كيفية التحقّق من حالة وظيفة بشكلٍ دوري، باستخدام طريقة واجهة برمجة التطبيقات get وjobId التي يعرضها طلب البحث الأصلي. في ما يلي مثال على التشغيل كل 500 مللي ثانية إلى أن تكتمل المهمة.

let jobCheckTimer;

function checkJobStatus(jobId){
  let request = gapi.client.bigquery.jobs.get({
    'projectId': billingProjectId,
    'jobId': jobId
  });
  request.execute(response =>{
    if (response.status.errorResult){
      // Handle any errors.
      console.log(response.status.error);
      return;
    }

    if (response.status.state == 'DONE'){
      // Get the results.
      clearTimeout(jobCheckTimer);
      getQueryResults(jobId);
      return;
    }
    // Not finished, check again in a moment.
    jobCheckTimer = setTimeout(checkJobStatus, 500, [jobId]);    
  });
}

عدِّل طريقة sendQuery لاستدعاء الطريقة checkJobStatus() كرد اتصال في مكالمة request.execute(). أدخِل مُعرّف الوظيفة إلى checkJobStatus. ويتعرض كائن الاستجابة كـ jobReference.jobId.

function sendQuery(queryString){
  let request = gapi.client.bigquery.jobs.query({
      'query': queryString,
      'timeoutMs': 30000,
      'datasetId': datasetId,
      'projectId': billingProjectId,
      'useLegacySql':false
  });
  request.execute(response => checkJobStatus(response.jobReference.jobId));
}

الحصول على نتائج طلب بحث

للحصول على نتائج طلب البحث عند انتهاء تشغيله، يمكنك استخدام طلب بيانات من واجهة برمجة التطبيقات jobs.getQueryResults. إضافة دالة إلى صفحتك تُسمى getQueryResults()، تقبل المعلمة jobId:

function getQueryResults(jobId){
  let request = gapi.client.bigquery.jobs.getQueryResults({
    'projectId': billingProjectId,
    'jobId': jobId
  });
  request.execute(response => {
    // Do something with the results.
  })
}

9- إجراء طلبات بحث عن بيانات الموقع الجغرافي باستخدام BigQuery API

هناك ثلاث طرق لاستخدام SQL لإجراء طلبات البحث المكانية مقابل البيانات في BigQuery:

هناك أمثلة على طلبات البحث المربّع والنطاق الجغرافي في قسم الدوال الرياضية في مرجع SQL القديم في BigQuery، ضمن "أمثلة متقدّمة&#39؛

بالنسبة إلى طلبات البحث عن المربّع والنطاق الجغرافي المحيط، يمكنك استدعاء طريقة BigQuery API query. يمكنك إنشاء لغة SQL لكل طلب وتمريرها إلى الدالة sendQuery التي أنشأتها في الخطوة السابقة.

ومن أمثلة العمل على الرمز لهذه الخطوة step4/map.html.

طلبات البحث المستطيلة

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

لاستخدام شكل رسمه المستخدم، غيِّر الرمز في index.html لمعالجة حدث الرسم الذي يتم تنشيطه عند اكتمال المستطيل. في هذا المثال، تستخدم الرمز getBounds() على عنصر المستطيل للحصول على كائن يمثل حجم المستطيل في إحداثيات الخريطة، ويمرره إلى دالة تُسمى rectangleQuery:

drawingManager.addListener('rectanglecomplete', rectangle => rectangleQuery(rectangle.getBounds()));

تحتاج الدالة rectangleQuery فقط إلى استخدام إحداثيات أعلى اليسار (الشمال الشرقي) والإحداثيات السفلية اليمنى (الجنوب الغربي) لإنشاء أقل من/أكبر من المقارنة مع كل صف في جدول BigQuery. في ما يلي مثال لطلب بحث في جدول يحتوي على عمودَين يُسمّى 'pickup_latitude' و'pickup_longitude' يُخزِّن قيم الموقع الجغرافي.

تحديد جدول BigQuery

لإجراء طلب بحث في جدول باستخدام BigQuery API، عليك إدخال اسم الجدول في نموذج مؤهَّل بالكامل في طلب بحث SQL. التنسيق في لغة الاستعلامات البنيوية (SQL) العادي هو project.dataset.tablename. في الإصدار القديم من SQL، إنه project.dataset.tablename.

هناك العديد من جداول رحلات سيارات الأجرة في نيويورك. وللاطّلاع عليها، انتقِل إلى وحدة تحكُّم BigQuery على الويب ووسِّع عنصر القائمة "مجموعات البيانات العلنية". ابحث عن مجموعة البيانات باسم new_york ووسِّعها للاطّلاع على الجداول. اختر جدول رحلات سيارة الأجرة الصفراء: bigquery-public-data.new_york_taxi_trips.tlc_yellow_trips_2016.

تحديد رقم تعريف المشروع

في طلب البيانات من واجهة برمجة التطبيقات، عليك تحديد اسم مشروع Google Cloud Platform لأغراض الفوترة. في هذا الدرس التطبيقي حول الترميز، ليس المشروع نفسه الذي يحتوي على الجدول. في حال العمل على جدول أنشأته في مشروعك من خلال تحميل البيانات، سيكون رقم تعريف المشروع هو نفسه رقم التعريف في عبارة SQL.

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

يمكنك إضافة متغيّرات JavaScript عمومية باسم billingProjectId, publicProjectId, datasetId وtableName إلى نسختك من index.html.

أولاً، أدخِل المتغيّرات 'publicProjectId' و'datasetId' و'tableName' باستخدام التفاصيل من مشروع مجموعات البيانات المتاحة في BigQuery. أولاً، اضبط billingProjectId باستخدام رقم تعريف المشروع (الرقم الذي أنشأته في &&بدء الإعداد) في وقت سابق من هذا الدرس التطبيقي حول الترميز.

let billingProjectId = 'YOUR_PROJECT_ID';
let publicProjectId = 'bigquery-public-data';
let datasetId = 'new_york_taxi_trips';
let tableName = 'tlc_yellow_trips_2016';

أضِف الآن دالتَين إلى الرمز لإنشاء لغة الاستعلامات البنيوية (SQL) ولإرسال الطلب إلى BigQuery باستخدام الدالة sendQuery التي أنشأتها في الخطوة السابقة.

يجب أن تُسمى الدالة الأولى rectangleSQL() ويجب أن تقبل وسيطتين، زوجًا من العناصر google.Maps.LatLng يمثل زوايا المستطيل في إحداثيات الخريطة.

والدالة الثانية يجب أن تُسمى rectangleQuery(). يؤدي ذلك إلى تمرير نص طلب البحث إلى دالة sendQuery.

let billingProjectId = 'YOUR_PROJECT_ID';
let publicProjectId = 'bigquery-public-data';
let datasetId = 'new_york';
let tableName = 'tlc_yellow_trips_2016';

function rectangleQuery(latLngBounds){
  let queryString = rectangleSQL(latLngBounds.getNorthEast(), latLngBounds.getSouthWest());
  sendQuery(queryString);
}

function rectangleSQL(ne, sw){
  let queryString = 'SELECT pickup_latitude, pickup_longitude '
  queryString +=  'FROM `' + publicProjectId +'.' + datasetId + '.' + tableName + '`'
  queryString += ' WHERE pickup_latitude > ' + sw.lat();
  queryString += ' AND pickup_latitude < ' + ne.lat();
  queryString += ' AND pickup_longitude > ' + sw.lng();
  queryString += ' AND pickup_longitude < ' + ne.lng();
  return queryString;
}

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

10- جارٍ عرض الاستجابة

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

يتوفّر الرمز الكامل لهذه الخطوة في step5/map.html.

للاحتفاظ بمقدار البيانات التي يتم نقلها إلى صفحة ويب بحجم معقول لهذا الدرس التطبيقي، عدِّل الدالة rectangleSQL() لإضافة عبارة تحدّ من الاستجابة لـ 10000 صف. في المثال أدناه، تم تحديد هذا المتغيّر في متغيّر عمومي يُسمى recordLimit، بحيث يمكن لجميع وظائف طلب البحث استخدام القيمة نفسها.

let recordLimit = 10000;
function rectangleSQL(ne, sw){
  var queryString = 'SELECT pickup_latitude, pickup_longitude '
  queryString +=  'FROM `' + publicProjectId +'.' + datasetId + '.' + tableName + '`'
  queryString += ' WHERE pickup_latitude > ' + sw.lat();
  queryString += ' AND pickup_latitude < ' + ne.lat();
  queryString += ' AND pickup_longitude > ' + sw.lng();
  queryString += ' AND pickup_longitude < ' + ne.lng();
  queryString += ' LIMIT ' + recordLimit;
  return queryString;
}

لوضع تمثيل بصري لكثافة المواقع الجغرافية، يمكنك استخدام خريطة التمثيل اللوني. تتضمن واجهة برمجة تطبيقات JavaScript لـ "خرائط Google" فئة HatmapLayer لهذا الغرض. تستخدم حاوية مصدر الخريطة مصفوفة من إحداثيات خطوط الطول والعرض لذلك من السهل جدًا تحويل الصفوف التي يتم عرضها من طلب البحث إلى خريطة تمثيل لوني.

في الدالة getQueryResults، مرِّر مصفوفة response.result.rows إلى دالة JavaScript جديدة باسم doHeatMap()، والتي ستنشئ خريطة التمثيل اللوني.

سيحتوي كل صف على خاصية اسم f، وهي عبارة عن مصفوفة من الأعمدة. وسيكون لكل عمود خاصية v تحتوي على القيمة.

تحتاج الرمز إلى تكرار الأعمدة في كل صف واستخراج القيم.

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

لا تنسَ استدعاء setMap() في طبقة خريطة التمثيل اللوني عندما تحدّد مصفوفة المواضع لها. سيؤدي ذلك إلى ظهوره على الخريطة.

وفي ما يلي مثال لذلك:

function getQueryResults(jobId){
  let request = gapi.client.bigquery.jobs.getQueryResults({
    'projectId': billingProjectId,
    'jobId': jobId
  });
  request.execute(response => doHeatMap(response.result.rows))
}

let heatmap;

function doHeatMap(rows){
  let heatmapData = [];
  if (heatmap != null){
    heatmap.setMap(null);
  }
  for (let i = 0; i < rows.length; i++) {
      let f = rows[i].f;
      let coords = { lat: parseFloat(f[0].v), lng: parseFloat(f[1].v) };
      let latLng = new google.maps.LatLng(coords);
      heatmapData.push(latLng);
  }
  heatmap = new google.maps.visualization.HeatmapLayer({
      data: heatmapData
  });
  heatmap.setMap(map);
}

في هذه المرحلة، من المفترض أن يكون بإمكانك:

  • فتح الصفحة والتفويض مقابل BigQuery
  • ارسم مستطيلاً في مكان ما في نيويورك
  • يمكنك الاطلاع على نتائج طلب البحث الناتج على شكل تمثيل بصري لخريطة التمثيل اللوني.

في ما يلي مثال على نتيجة طلب بحث مستطيل مقارنةً ببيانات سيارة الأجرة الصفراء في نيويورك لعام 2016، مرسومةً على هيئة خريطة تمثيل لوني. يوضّح ذلك توزيع عمليات استلام الطلبات من "مبنى إمباير ستيت" يوم السبت في تموز (يوليو):

7b1face0e7c71c78.png

11- طلب البحث حسب النطاق الجغرافي حول نقطة معيّنة

طلبات البحث الشعاعية متشابهة جدًا. باستخدام دالات الرياضيات القديمة في BigQuery&#39، يمكنك إنشاء طلب SQL باستخدام صيغة هافيرس التي تحدد مساحة دائرية على سطح الأرض.

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

تم تضمين مثال صالح للرمز لهذه الخطوة في مستودع الرموز باسم step6/map.html.

drawingManager.addListener('circlecomplete', circle => circleQuery(circle));

وفي نسختك من index.html، أضِف دالتَين فارغتَين جديدتَين: circleQuery() وhaversineSQL().

بعد ذلك، أضِف معالج أحداث circlecomplete يجتاز المركز والنطاق الجغرافي مع دالة جديدة باسم circleQuery()..

ستعمل دالة circleQuery() على استدعاء haversineSQL() لإنشاء لغة الاستعلامات البنيوية (SQL) المستخدمة في طلب البحث، ثم إرسال طلب البحث عن طريق استدعاء الدالة sendQuery() وفقًا لرمز المثال التالي.

function circleQuery(circle){
  let queryString = haversineSQL(circle.getCenter(), circle.radius);
  sendQuery(queryString);
}

// Calculate a circular area on the surface of a sphere based on a center and radius.
function haversineSQL(center, radius){
  let queryString;
  let centerLat = center.lat();
  let centerLng = center.lng();
  let kmPerDegree = 111.045;

  queryString = 'CREATE TEMPORARY FUNCTION Degrees(radians FLOAT64) RETURNS FLOAT64 LANGUAGE js AS ';
  queryString += '""" ';
  queryString += 'return (radians*180)/(22/7);';
  queryString += '"""; ';

  queryString += 'CREATE TEMPORARY FUNCTION Radians(degrees FLOAT64) RETURNS FLOAT64 LANGUAGE js AS';
  queryString += '""" ';
  queryString += 'return (degrees*(22/7))/180;';
  queryString += '"""; ';

  queryString += 'SELECT pickup_latitude, pickup_longitude '
  queryString += 'FROM `' + publicProjectId +'.' + datasetId + '.' + tableName + '` ';
  queryString += 'WHERE '
  queryString += '(' + kmPerDegree + ' * DEGREES( ACOS( COS( RADIANS('
  queryString += centerLat;
  queryString += ') ) * COS( RADIANS( pickup_latitude ) ) * COS( RADIANS( ' + centerLng + ' ) - RADIANS('
  queryString += ' pickup_longitude ';
  queryString += ') ) + SIN( RADIANS('
  queryString += centerLat;
  queryString += ') ) * SIN( RADIANS( pickup_latitude ) ) ) ) ) ';

  queryString += ' < ' + radius/1000;
  queryString += ' LIMIT ' + recordLimit;
  return queryString;
}

لم لا تجرِّب!

أضف الرمز أعلاه وجرّب أداة "الدائرة&#39؛ لتحديد منطقة على الخريطة. ومن المفترض أن تظهر النتيجة على النحو التالي:

845418166b7cc7a3.png

12- طلب البحث عن أشكال عشوائية

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

ومن ميزات BigQuery الفعّالة التي يمكن استخدامها لهذا الغرض دوال من تحديد المستخدم (UDF). تنفّذ ملفات UDF رمز JavaScript داخل طلب بحث SQL.

يكون رمز العمل لهذه الخطوة في step7/map.html.

UDF في واجهة برمجة تطبيقات BigQuery

تختلف طريقة BigQuery API لـ UDF عن وحدة تحكم الويب قليلاً: ستحتاج إلى استدعاء jobs.insert method.

بالنسبة إلى طلبات البحث العادية التي تستخدم لغة الاستعلامات البنيوية (SQL) من خلال واجهة برمجة التطبيقات، يجب إدخال عبارة SQL واحدة لاستخدام وظيفة من تحديد المستخدم. ويجب ضبط قيمة useLegacySql على false. يوضح مثال JavaScript أدناه دالة تُنشئ كائن طلب وتُرسله لإدراج وظيفة جديدة، وفي هذه الحالة طلب بحث مع دالة يحددها المستخدم.

يمكنك الاطّلاع على نموذج صالح لهذا النهج في step7/map.html.

function polygonQuery(polygon) {
  let request = gapi.client.bigquery.jobs.insert({
    'projectId' : billingProjectId,
      'resource' : {
        'configuration':
          {
            'query':
            {
              'query': polygonSql(polygon),
              'useLegacySql': false
            }
          }
      }
  });
  request.execute(response => checkJobStatus(response.jobReference.jobId));
}

يتم إنشاء طلب بحث SQL على النحو التالي:

function polygonSql(poly){
  let queryString = 'CREATE TEMPORARY FUNCTION pointInPolygon(latitude FLOAT64, longitude FLOAT64) ';
  queryString += 'RETURNS BOOL LANGUAGE js AS """ ';
  queryString += 'var polygon=' + JSON.stringify(poly) + ';';
  queryString += 'var vertx = [];';
  queryString += 'var verty = [];';
  queryString += 'var nvert = 0;';
  queryString += 'var testx = longitude;';
  queryString += 'var testy = latitude;';
  queryString += 'for(coord in polygon){';
  queryString += '  vertx[nvert] = polygon[coord][0];';
  queryString += '  verty[nvert] = polygon[coord][1];';
  queryString += '  nvert ++;';
  queryString += '}';
  queryString += 'var i, j, c = 0;';
  queryString += 'for (i = 0, j = nvert-1; i < nvert; j = i++) {';
  queryString += '  if ( ((verty[i]>testy) != (verty[j]>testy)) &&(testx < (vertx[j]-vertx[i]) * (testy-verty[i]) / (verty[j]-verty[i]) + vertx[i]) ){';
  queryString += '    c = !c;';
  queryString += '  }';
  queryString += '}';
  queryString += 'return c;';
  queryString += '"""; ';
  queryString += 'SELECT pickup_latitude, pickup_longitude, dropoff_latitude, dropoff_longitude, pickup_datetime ';
  queryString += 'FROM `' + publicProjectId + '.' + datasetId + '.' + tableName + '` ';
  queryString += 'WHERE pointInPolygon(pickup_latitude, pickup_longitude) = TRUE ';
  queryString += 'LIMIT ' + recordLimit;
  return queryString;
}

هُنَاكَ عُنْصُرَانِ هُنَاكَ. أولاً، ينشئ الرمز عبارة CREATE TEMPORARY FUNCTION التي تلتف على رمز JavaScript للعمل إذا كانت نقطة محددة داخل المضلع. يتم إدراج إحداثيات المضلع باستخدام استدعاء الطريقة JSON.stringify(poly) لتحويل مصفوفة JavaScript من أزواج إحداثيات x وy إلى سلسلة. يتم نقل كائن المضلع كوسيطة للدالة التي تنشئ SQL.

ثانيًا، ينشئ الرمز عبارة SELECT الرئيسية. يتم استدعاء UDF في تعبير WHERE في هذا المثال.

التكامل مع API للخرائط

لاستخدام هذا مع مكتبة رسم API للخرائط، يجب حفظ المضلع الذي رسمه المستخدم وتمريره إلى جزء UDF في طلب بحث SQL.

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

drawingManager.addListener('polygoncomplete', polygon => {
  let path = polygon.getPaths().getAt(0);
  let queryPolygon = path.map(element => {
    return [element.lng(), element.lat()];
  });
  polygonQuery(queryPolygon);
});

ويمكن لدالة polygonQuery بعد ذلك إنشاء وظائف JavaScript UDF كسلسلة، بالإضافة إلى عبارة SQL التي ستطلب وظيفة UDF.

اطّلِع على step7/map.html للحصول على مثال صالح لذلك.

مثال على الناتج

في ما يلي مثال على نتيجة طلب البحث عن عمليات استلام من بيانات سيارة الأجرة الصفراء من TLC لعام 2016 في نيويورك في BigQuery باستخدام مضلّع مستقل، مع رسم البيانات المحددة على شكل خريطة تمثيل لوني.

لقطة شاشة يوم 2017-05-09 الساعة 10.00.48 صباحًا

13- الاستفادة من ميزات إضافية

في ما يلي بعض الاقتراحات حول طُرق لتوسيع هذا الدرس التطبيقي حول الترميز للاطّلاع على جوانب أخرى من البيانات. يمكنك العثور على مثال صالح لهذه الأفكار على step8/map.html في مستودع الرموز.

عمليات الانسحاب على الخرائط

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

على سبيل المثال، لنتعرّف على الأماكن التي غالبًا ما تنخفض فيها سيارات الأجرة عندما يطلبون خدمة النقل من مبنى "إمباير ستيت".

غيِّر رمز عبارة SQL في polygonSql() لطلب هذه الأعمدة بالإضافة إلى موقع الاستلام.

function polygonSql(poly){
  let queryString = 'CREATE TEMPORARY FUNCTION pointInPolygon(latitude FLOAT64, longitude FLOAT64) ';
  queryString += 'RETURNS BOOL LANGUAGE js AS """ ';
  queryString += 'var polygon=' + JSON.stringify(poly) + ';';
  queryString += 'var vertx = [];';
  queryString += 'var verty = [];';
  queryString += 'var nvert = 0;';
  queryString += 'var testx = longitude;';
  queryString += 'var testy = latitude;';
  queryString += 'for(coord in polygon){';
  queryString += '  vertx[nvert] = polygon[coord][0];';
  queryString += '  verty[nvert] = polygon[coord][1];';
  queryString += '  nvert ++;';
  queryString += '}';
  queryString += 'var i, j, c = 0;';
  queryString += 'for (i = 0, j = nvert-1; i < nvert; j = i++) {';
  queryString += '  if ( ((verty[i]>testy) != (verty[j]>testy)) &&(testx < (vertx[j]-vertx[i]) * (testy-verty[i]) / (verty[j]-verty[i]) + vertx[i]) ){';
  queryString += '    c = !c;';
  queryString += '  }';
  queryString += '}';
  queryString += 'return c;';
  queryString += '"""; ';

  queryString += 'SELECT pickup_latitude, pickup_longitude, dropoff_latitude, dropoff_longitude, pickup_datetime ';
  queryString += 'FROM `' + publicProjectId + '.' + datasetId + '.' + tableName + '` ';
  queryString += 'WHERE pointInPolygon(pickup_latitude, pickup_longitude) = TRUE ';
  queryString += 'LIMIT ' + recordLimit;
  return queryString;
}

يمكن للدالة doHeatMap بعد ذلك استخدام قيم "الانسحاب". يحتوي كائن النتيجة على مخطط يمكن فحصه للعثور على موقع هذه الأعمدة في المصفوفة. وفي هذه الحالة، ستكون في موضعي الفهرس 2 و3. ويمكن قراءة هذه المؤشرات من أحد المتغيرات لتسهيل إدارة الرمز. ملاحظة: تم ضبط maxIntensity لخريطة التمثيل اللوني لعرض الحدّ الأقصى لكثافة عمليات الطرح وهو 20 نقطة انطلاق لكل بكسل.

أضف بعض المتغيرات للسماح لك بتغيير الأعمدة التي تستخدمها لبيانات خريطة التمثيل اللوني.

// Show query results as a Heatmap.
function doHeatMap(rows){
  let latCol = 2;
  let lngCol = 3;
  let heatmapData = [];
  if (heatmap!=null){
    heatmap.setMap(null);
  }
  for (let i = 0; i < rows.length; i++) {
      let f = rows[i].f;
      let coords = { lat: parseFloat(f[latCol].v), lng: parseFloat(f[lngCol].v) };
      let latLng = new google.maps.LatLng(coords);
      heatmapData.push(latLng);
  }
  heatmap = new google.maps.visualization.HeatmapLayer({
      data: heatmapData,
      maxIntensity: 20
  });
  heatmap.setMap(map);
}

في ما يلي خريطة التمثيل اللوني التي توضح توزيع الانسحاب من جميع عمليات الركوب على الفور في جميع أنحاء مبنى "إمباير ستيت" في 2016. وترتكز التركيزات الكبيرة (النقاط الثنائية) على وجهات وسط المدينة خاصةً حول ميدان "تايمز سكوير"، بالإضافة إلى الجادة الخامسة بين شارعَي "شارع 23" و"14"، وتشمل المواقع ذات الكثافة العالية التي لا تظهر في مستوى التكبير أو التصغير أيضًا مطارَي "لاغوارديا" و"جي إف كاي" ومركز التجارة العالمي و"حديقة البطارية".

لقطة شاشة يوم 2017-05-09 الساعة 10.40.01 صباحًا

تصميم خريطة القاعدة

عند إنشاء خريطة في Google باستخدام واجهة برمجة تطبيقات JavaScript للخرائط، يمكنك ضبط نمط الخريطة باستخدام كائن JSON. بالنسبة إلى التمثيلات البصرية للبيانات، من المفيد كتم صوت الألوان على الخريطة. يمكنك إنشاء أنماط الخريطة وتجربتها باستخدام معالج النمط في API لـ "خرائط Google" على mapstyle.withgoogle.com.

يمكنك ضبط نمط الخريطة عند إعداد أحد عناصر الخريطة، أو في أي وقت لاحق في وقت لاحق. إليك كيفية إضافة نمط مخصّص في دالة initMap():

function initMap() {
  map = new google.maps.Map(document.getElementById('map'), {
        center: {lat: 40.744593, lng: -73.990370}, // Manhattan, New York.
  zoom: 12,
  styles: [
    {
        "elementType": "geometry",
          "stylers": [
            {
              "color": "#f5f5f5"
            }
          ]
        },
        {
          "elementType": "labels.icon",
            "stylers": [
              {
                "visibility": "on"
              }
            ]
        },
        {
          "featureType": "water",
            "elementType": "labels.text.fill",
              "stylers": [
                {
                  "color": "#9e9e9e"
                }
              ]
        }
      ]
    });
  setUpDrawingTools();
}

يعرض نموذج النمط أدناه خريطة تدرّج الرمادية ذات نقاط الاهتمام.

[
  {
    "elementType": "geometry",
    "stylers": [
      {
        "color": "#f5f5f5"
      }
    ]
  },
  {
    "elementType": "labels.icon",
    "stylers": [
      {
        "visibility": "on"
      }
    ]
  },
  {
    "elementType": "labels.text.fill",
    "stylers": [
      {
        "color": "#616161"
      }
    ]
  },
  {
    "elementType": "labels.text.stroke",
    "stylers": [
      {
        "color": "#f5f5f5"
      }
    ]
  },
  {
    "featureType": "administrative.land_parcel",
    "elementType": "labels.text.fill",
    "stylers": [
      {
        "color": "#bdbdbd"
      }
    ]
  },
  {
    "featureType": "poi",
    "elementType": "geometry",
    "stylers": [
      {
        "color": "#eeeeee"
      }
    ]
  },
  {
    "featureType": "poi",
    "elementType": "labels.text.fill",
    "stylers": [
      {
        "color": "#757575"
      }
    ]
  },
  {
    "featureType": "poi.park",
    "elementType": "geometry",
    "stylers": [
      {
        "color": "#e5e5e5"
      }
    ]
  },
  {
    "featureType": "poi.park",
    "elementType": "labels.text.fill",
    "stylers": [
      {
        "color": "#9e9e9e"
      }
    ]
  },
  {
    "featureType": "road",
    "elementType": "geometry",
    "stylers": [
      {
        "color": "#ffffff"
      }
    ]
  },
  {
    "featureType": "road.arterial",
    "elementType": "labels.text.fill",
    "stylers": [
      {
        "color": "#757575"
      }
    ]
  },
  {
    "featureType": "road.highway",
    "elementType": "geometry",
    "stylers": [
      {
        "color": "#dadada"
      }
    ]
  },
  {
    "featureType": "road.highway",
    "elementType": "labels.text.fill",
    "stylers": [
      {
        "color": "#616161"
      }
    ]
  },
  {
    "featureType": "road.local",
    "elementType": "labels.text.fill",
    "stylers": [
      {
        "color": "#9e9e9e"
      }
    ]
  },
  {
    "featureType": "transit.line",
    "elementType": "geometry",
    "stylers": [
      {
        "color": "#e5e5e5"
      }
    ]
  },
  {
    "featureType": "transit.station",
    "elementType": "geometry",
    "stylers": [
      {
        "color": "#eeeeee"
      }
    ]
  },
  {
    "featureType": "water",
    "elementType": "geometry",
    "stylers": [
      {
        "color": "#c9c9c9"
      }
    ]
  },
  {
    "featureType": "water",
    "elementType": "labels.text.fill",
    "stylers": [
      {
        "color": "#9e9e9e"
      }
    ]
  }
]

منح المستخدم ملاحظات وآراء

على الرغم من أن BigQuery يرسل عادةً ردًا خلال ثوانٍ، إلا أنه من المفيد أحيانًا أن توضّح للمستخدم أن هناك أمرًا يجري أثناء تشغيل طلب البحث.

أضِف بعض واجهة المستخدم إلى صفحة الويب التي تعرض استجابة الدالة checkJobStatus() ورسمًا متحركًا يشير إلى أن طلب البحث قيد التقدم.

تتضمّن المعلومات التي يمكنك عرضها مدة طلب البحث وكمية البيانات المعروضة وحجم البيانات التي تمت معالجتها.

يمكنك إضافة بعض رموز HTML بعد الخريطة <div> لإنشاء لوحة إلى الصفحة تعرض عدد الصفوف التي يعرضها طلب البحث والوقت الذي استغرقه الطلب وكمية البيانات التي تمت معالجتها.

<div id="menu">
    <div id="stats">
        <h3>Statistics:</h3>
        <table>
            <tr>
                <td>Total Locations:</td><td id="rowCount"> - </td>
            </tr>
            <tr>
                <td>Query Execution:</td><td id="duration"> - </td>
            </tr>
            <tr>
                <td>Data Processed:</td><td id="bytes"> - </td>
            </tr>
        </table>
    </div>
</div>

تتحكّم خدمة CSS في مظهر هذه اللوحة وموضعها. أضف CSS لوضع اللوحة في أعلى يمين الصفحة أسفل أزرار نوع الخريطة وشريط أدوات الرسم كما في المقتطف أدناه.

#menu {
  position: absolute; 
  background: rgba(255, 255, 255, 0.8); 
  z-index: 1000; 
  top: 50px; 
  left: 10px; 
  padding: 15px;
}
#menu h1 {
  margin: 0 0 10px 0;
  font-size: 1.75em;
}
#menu div {
  margin: 5px 0px;
}

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

أضِف بعض رموز HTML لعرض رسم متحرك. هناك ملف صورة باسم loader.gif في مجلد img في مستودع الرموز.

<img id="spinner" src="img/loader.gif">

يمكنك إضافة بعض صفحات الأنماط المتتالية (CSS) لضبط موضع الصورة وإخفاؤها تلقائيًا حتى تكون بحاجة إلى ذلك.

#spinner {
  position: absolute; 
  top: 50%; 
  left: 50%; 
  margin-left: -32px; 
  margin-top: -32px; 
  opacity: 0; 
  z-index: -1000;
}

وأخيرًا أضِف بعض رموز JavaScript لتعديل لوحة الحالة وعرض الرسم أو إخفاؤه عندما يكون طلب البحث قيد التشغيل. ويمكنك استخدام العنصر response لتعديل اللوحة استنادًا إلى المعلومات المتاحة.

عند التحقّق من وظيفة حالية، يمكنك استخدام السمة response.statistics. عند اكتمال المهمة، يمكنك الوصول إلى الخاصيتين response.totalRows وresponse.totalBytesProcessed. من المفيد للمستخدم تحويل المللي ثانية إلى ثوانٍ والبايت إلى أن يتم عرضها بالغيغابايت كما هو موضح في نموذج الرمز أدناه.

function updateStatus(response){
  if(response.statistics){
    let durationMs = response.statistics.endTime - response.statistics.startTime;
    let durationS = durationMs/1000;
    let suffix = (durationS ==1) ? '':'s';
    let durationTd = document.getElementById("duration");
    durationTd.innerHTML = durationS + ' second' + suffix;
  }
  if(response.totalRows){
    let rowsTd = document.getElementById("rowCount");
    rowsTd.innerHTML = response.totalRows;
  }
  if(response.totalBytesProcessed){
    let bytesTd = document.getElementById("bytes");
    bytesTd.innerHTML = (response.totalBytesProcessed/1073741824) + ' GB';
  }
}

وعليك باستدعاء هذه الطريقة عند وجود رد على استدعاء checkJobStatus() وعند استرجاع نتائج طلب البحث. مثلاً:

// Poll a job to see if it has finished executing.
function checkJobStatus(jobId){
  let request = gapi.client.bigquery.jobs.get({
    'projectId': billingProjectId,
    'jobId': jobId
  });
  request.execute(response => {
    //Show progress to the user
    updateStatus(response);

    if (response.status.errorResult){
      // Handle any errors.
      console.log(response.status.error);
      return;
    }
    if (response.status.state == 'DONE'){
      // Get the results.
      clearTimeout(jobCheckTimer);
      getQueryResults(jobId);
      return;
    }
    // Not finished, check again in a moment.
    jobCheckTimer = setTimeout(checkJobStatus, 500, [jobId]); 
  });
}

// When a BigQuery job has completed, fetch the results.
function getQueryResults(jobId){
  let request = gapi.client.bigquery.jobs.getQueryResults({
    'projectId': billingProjectId,
    'jobId': jobId
  });
  request.execute(response => {
    doHeatMap(response.result.rows);
    updateStatus(response);
  })
}

لتبديل الرسم المتحرك، أضف وظيفة للتحكم في مستوى الرؤية. ستغير هذه الدالة تعتيم أي عنصر HTML DOM تم تمريره إليه.

function fadeToggle(obj){
    if(obj.style.opacity==1){
        obj.style.opacity = 0;
        setTimeout(() => {obj.style.zIndex = -1000;}, 1000);
    } else {
        obj.style.zIndex = 1000;
        obj.style.opacity = 1;
    }
}

وأخيرًا، عليك باستدعاء هذه الطريقة قبل معالجة طلب البحث، وبعد ظهور نتيجة طلب البحث من BigQuery.

يطلب هذا الرمز دالة fadeToggle عند الانتهاء من رسم مستطيل.

drawingManager.addListener('rectanglecomplete', rectangle => {
  //show an animation to indicate that something is happening.
  fadeToggle(document.getElementById('spinner'));
  rectangleQuery(rectangle.getBounds());
});

عندما يتم تلقي استجابة طلب البحث، اتّصل بـ fadeToggle() مرة أخرى لإخفاء الرسم المتحرك.

// When a BigQuery job has completed, fetch the results.
function getQueryResults(jobId){
  let request = gapi.client.bigquery.jobs.getQueryResults({
    'projectId': billingProjectId,
    'jobId': jobId
  });
  request.execute(response => {
    doHeatMap(response.result.rows);
    //hide the animation.
    fadeToggle(document.getElementById('spinner'));
    updateStatus(response);
  })
}

من المفترض أن تبدو الصفحة على النحو التالي:

لقطة شاشة يوم 10-05-2017 في الساعة 2.32.19 مساءً.png

ألقِ نظرة على المثال الكامل في step8/map.html.

14- ملاحظات مهمّة

عدد العلامات أكبر من الحدّ المسموح به

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

يمكن أن يؤدي رسم العديد من العلامات إلى جعل الخريطة غير قابلة للقراءة. يمكنك استخدام HeatmapLayer لعرض الكثافة أو علامات المجموعات للإشارة إلى العديد من نقاط البيانات المتاحة باستخدام رمز واحد لكل مجموعة. يمكنك الاطّلاع على المزيد من التفاصيل في البرنامج التعليمي الخاص بتجميع العلامات.

تحسين طلبات البحث

سيفحص BigQuery الجدول بالكامل مع كل طلب بحث. لتحسين استخدام حصة BigQuery، اختَر الأعمدة التي تحتاج إليها في طلب البحث فقط.

ستكون طلبات البحث أسرع إذا خزّنت خطوط العرض وخطوط الطول كعوائق بدلاً من سلاسل.

تصدير النتائج المثيرة للاهتمام

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

ضَع في اعتبارك بنود خدمة "منصة خرائط Google". ولمزيد من التفاصيل عن تسعير "منصة خرائط Google"، يُرجى الاطّلاع على المستندات على الإنترنت.

العب بمزيد من البيانات.

هناك عدد من مجموعات البيانات العلنية في BigQuery التي تحتوي على أعمدة خطوط الطول والعرض، مثل مجموعات بيانات سيارات الأجرة في دبي من 2009 إلى 2016، وبيانات رحلات Uber وLyft ومجموعة بيانات GDELT.

15- تهانينا.

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

ما الخطوات التالية؟

إذا أردت معرفة المزيد من المعلومات عن منصّة "خرائط Google" أو BigQuery، يمكنك الاطّلاع على الاقتراحات التالية.

يمكنك الاطّلاع على ما هي أداة BigQuery لمعرفة المزيد من المعلومات حول خدمة مستودع البيانات غير المُعدّة على مستوى بتالبايت في Google.

اطّلِع على دليل كيفية إنشاء تطبيق بسيط باستخدام BigQuery API.

اطّلع على دليل مطوّري البرامج لمكتبة الرسم للحصول على مزيد من التفاصيل عن تفعيل تفاعل المستخدم لرسم الأشكال على "خرائط Google".

يمكنك الاطلاع على طرق أخرى للعرض المرئي للبيانات على خريطة Google.

يُرجى الاطِّلاع على دليل البدء لـ JAPscript Client AP للتعرُّف على المفاهيم الأساسية لاستخدام واجهة برمجة تطبيقات العميل للوصول إلى واجهات Google API الأخرى.