ביצוע שאילתות והצגה חזותית של נתוני מיקום ב-BigQuery באמצעות הפלטפורמה של מפות Google (JavaScript)

1. סקירה כללית

מפות Google יכול להיות כלי יעיל מאוד להצגה חזותית של הדפוסים במערך נתונים הקשור למיקום בצורה מסוימת. הקשר יכול להיות שם של מקום, ערך ספציפי של קו רוחב וקו אורך, או שם של אזור עם גבולות ספציפיים כמו מפקד אוכלוסין או מיקוד.

כאשר מערכי הנתונים האלה גדולים מאוד, ייתכן שיהיה קשה לשלוח שאילתות ולהמחיש אותם באמצעות כלים קונבנציונליים. השימוש ב-Google BigQuery מאפשר להריץ שאילתות על הנתונים ועל ממשקי ה-API של מפות Google כדי ליצור את השאילתה ולהציג את הפלט בצורה חזותית. כך ניתן לגלות במהירות דפוסים גיאוגרפיים בנתונים באמצעות מעט מאוד הגדרות או קידוד, ובלי לנהל מערכת לאחסון מערכי נתונים גדולים מאוד.

מה תפתחו

במעבדה הזו, תוכלו לכתוב ולהריץ כמה שאילתות שמדגימות איך לספק תובנות מבוססות-מיקום למערכי נתונים ציבוריים גדולים מאוד באמצעות BigQuery. כמו כן, אתם בונים דף אינטרנט שטוען מפה באמצעות ממשק ה-API של JavaScript במפות Google, ולאחר מכן מריצים הצגה חזותית של שאילתות מרחביות באמצעות אותם מערכי נתונים ציבוריים גדולים מאוד באמצעות ספריית הלקוחות של Google APIs ל-JavaScript ו-BigQuery API.

מה תלמדו

  • איך לשלוח שאילתות לגבי מערכי נתונים של מיקומים בגודל פטה-בייט תוך שניות באמצעות BigQuery, באמצעות שאילתות SQL, פונקציות בהגדרת המשתמש ו-BigQuery API
  • איך להשתמש בפלטפורמה של מפות Google כדי להוסיף מפה של Google לדף אינטרנט ולאפשר למשתמשים לשרטט עליה צורות
  • הצגה חזותית של שאילתות נגד מערכי נתונים גדולים במפה של Google, כמו בתמונה לדוגמה הבאה, שמראה את הצפיפות של מיקומי המוניות ב-2016 מתהליכים שהתחילו בבלוק מסביב לבניין האמפייר סטייט.

צילום מסך 2017-05-09 בשעה 11.01.12 בבוקר

מה תצטרך להכין

  • ידע בסיסי ב-HTML, CSS, JavaScript, SQL וכלי פיתוח ל-Chrome
  • דפדפן אינטרנט מודרני, למשל גרסאות אחרונות של Chrome , Firefox, Safari או Edge.
  • עורך טקסט או IDE לבחירתך

הטכנולוגיה

BigQuery

BigQuery הוא שירות של ניתוח נתונים של Google עבור מערכי נתונים גדולים מאוד. לאפליקציה יש API ל-REST ותומך בשאילתות שנכתבו ב-SQL. אם יש לכם נתונים עם ערכי קו אורך וקו רוחב, אפשר להשתמש בהם כדי להריץ שאילתות לגבי הנתונים לפי מיקום. היתרון הוא שאתם יכולים לחקור באופן חזותי מערכי נתונים גדולים מאוד כדי לבדוק את הדפוסים בלי לנהל תשתית של שרת או מסד נתונים. אפשר לקבל תשובות לשאלות תוך שניות ספורות, גם אם הגודל של הטבלאות עולה, וגם ניתן להתאים את התשתית הגדולה ואת התשתית המנוהלת של BigQuery.

הפלטפורמה של מפות Google

הפלטפורמה של מפות Google מספקת גישה פרוגרמטית לנתוני מפות, מקומות ונתיבים ב-Google. כרגע, יותר מ-2 מיליון אתרים ואפליקציות משתמשים בו כדי לספק מפות מוטמעות ושאילתות מבוססות-מיקום למשתמשים שלהם.

שכבת שרטוט JavaScript API של מפות Google מאפשרת לשרטט צורות במפה. אפשר להמיר אותם לקלט כדי להריץ שאילתות מול טבלאות BigQuery שיש בהן ערכים של קווי אורך ורוחב שמאוחסנים בעמודות.

כדי להתחיל, אתם צריכים פרויקט Google Cloud Platform עם אפשרות ממשקי API של BigQuery ומפות Google.

2. תהליך ההגדרה

חשבון Google

אם עדיין אין לכם חשבון Google (Gmail או Google Apps), עליכם ליצור חשבון.

יצירת פרויקט

נכנסים למסוף Google Cloud Platform (console.cloud.google.com) ויוצרים פרויקט חדש. בחלק העליון של המסך יש תפריט נפתח של 'פרויקטים':

f2a353c3301dc649.png

לאחר לחיצה על התפריט הנפתח של הפרויקט, יוצג פריט תפריט שיאפשר לך ליצור פרויקט חדש:

56a42dfa7ac27a35.png

בתיבה "יש להזין שם חדש לפרויקט" יש להזין שם לפרויקט החדש, לדוגמה "BigQuery Codelab"

Codelab – יצירת פרויקט (1).png

המערכת תיצור עבורכם מזהה פרויקט. מזהה הפרויקט הוא שם ייחודי בכל הפרויקטים ב-Google Cloud. זכרו את מזהה הפרויקט, מכיוון שהוא ישמש אתכם מאוחר יותר. השם למעלה כבר תפוס והוא לא יעבוד. אפשר להזין את מזהה הפרויקט שלך בכל מקום שבו מופיע Your_PROJECT_ID במעבדת הקוד הזו.

הפעלת חיוב

כדי להירשם ל-BigQuery, אפשר להשתמש בפרויקט שנבחר או שנוצר בשלב הקודם. יש להפעיל את החיוב בפרויקט הזה. לאחר הפעלת החיוב, תוכלו להפעיל את BigQuery API.

כדי להפעיל את החיוב, עליכם ליצור פרויקט חדש או להפעיל מחדש את החיוב בפרויקט קיים.

Google מציעה תקופת ניסיון בחינם ל-12 חודשים עבור שימוש ב-Google Cloud Platform בשווי של עד 300 $. ייתכן שייעשה שימוש ב-Codelab הזה. מידע נוסף זמין בכתובת https://cloud.google.com/free/ .

פרויקטים חדשים

כשיוצרים פרויקט חדש, צריך לבחור אילו מחשבונות החיוב רוצים לקשר לפרויקט. אם יש לכם רק חשבון אחד לחיוב, החשבון הזה יקושר באופן אוטומטי לפרויקט.

אם אין לכם חשבון לחיוב, עליכם ליצור חשבון ולהפעיל את החיוב בפרויקט לפני שתוכלו להשתמש בתכונות רבות של Google Cloud Platform. כדי ליצור חשבון חדש לחיוב ולהפעיל את הפרויקט בחשבון, פועלים לפי ההוראות ליצירה של חשבון חדש לחיוב.

פרויקטים קיימים

אם יש לכם פרויקט שהשבתתם באופן זמני את החיוב בו, תוכלו להפעיל מחדש את החיוב:

  1. נכנסים ל-Cloud Platform Console.
  2. מרשימת הפרויקטים, בוחרים את הפרויקט שעבורו רוצים להפעיל מחדש את החיוב.
  3. פותחים את התפריט שבצד ימין במסוף ובוחרים באפשרות חיוב חיוב. תופיע בקשה לבחור חשבון לחיוב.
  4. לוחצים על הגדרת חשבון.

יצירת חשבון חדש לחיוב

כדי ליצור חשבון חדש לחיוב:

  1. נכנסים ל-Cloud Platform Console ונכנסים לחשבון, או אם אין לכם עדיין חשבון, נרשמים.
  2. פותחים את תפריט הצד שמימין במסוף ובוחרים באפשרות חיוב חיוב.
  3. לוחצים על הלחצן חשבון חדש לחיוב. (שימו לב: אם זה לא החשבון הראשון לחיוב, פותחים את רשימת החשבונות לחיוב. לשם כך, לוחצים על השם של החשבון לחיוב הקיים בחלק העליון של הדף ואז לוחצים על ניהול חשבונות לחיוב.)
  4. מזינים את השם של החשבון לחיוב ומזינים את נתוני החיוב. האפשרויות שיוצגו תלויות במדינה של הכתובת לחיוב. לידיעתך, בחשבונות בארה"ב לא ניתן לשנות את סטטוס המס לאחר יצירת החשבון.
  5. לוחצים על שליחה והפעלה של החיוב.

כברירת מחדל, האדם שיוצר את החשבון הוא מנהל המערכת של החיוב בחשבון.

מידע על אימות חשבונות בנק והוספת אמצעי תשלום לגיבוי זמין במאמר הוספה, הסרה או עדכון של אמצעי תשלום.

הפעלת ה-API של BigQuery

כדי להפעיל את BigQuery API בפרויקט, נכנסים ל-BigQuery API של דף ה-API במסוף ולוחצים על הלחצן הכחול 'הפעלה''.

3. שליחת שאילתות לגבי נתוני מיקום ב-BigQuery

יש שלוש דרכים לשלוח שאילתות על נתוני מיקום השמורים כערכי קו אורך וקו רוחב ב-BigQuery.

  • שאילתות מלבניות: מציינים את תחום העניין בתור שאילתה הבוחרת את כל השורות בטווח של קווי אורך ורוחב מינימליים ומקסימליים.
  • שאילתות רדיוס: יש לציין את אזור העניין על ידי חישוב מעגל מסביב לנקודה באמצעות הנוסחה 'הדרגתי' ופונקציות מתמטיות כדי לבנות מודלים של צורת כדור הארץ.
  • שאילתות מצולע: יש לציין צורה מותאמת אישית ולהשתמש בפונקציית משתמש כדי לבטא את הלוגיקה של הפוליגון הנדרשת כדי לבדוק אם קווי האורך והרוחב של כל שורה משתייכים לצורה.

כדי להתחיל, צריך להשתמש בעורך השאילתות בקטע Big Query במסוף של Google Cloud Platform כדי להריץ את השאילתות הבאות מול נתוני המוניות של NYC.

SQL סטנדרטי לעומת SQL מדור קודם

BigQuery תומך בשתי גרסאות של SQL: Legacy SQL ו-Standard SQL. האפשרות השנייה היא תקן ANSI לשנת 2011. לצורך המדריך הזה, נשתמש ב-SQL סטנדרטי כי הוא עומד בסטנדרטים גבוהים יותר.

אם רוצים להפעיל SQL מדור קודם בעורך BigQuery, אפשר לבצע את הפעולות הבאות:

  1. לוחצים על הלחצן 'עוד'
  2. בוחרים באפשרות 'הגדרות שאילתה&#39'; מהתפריט הנפתח
  3. בקטע 'ניב ניב&SQL', בוחרים בלחצן הבחירה 'דור קודם'
  4. לוחצים על הלחצן 'שמירה'

שאילתות לגבי מלבן

קל מאוד ליצור שאילתות במלבן ב-BigQuery. פשוט מוסיפים משפט WHERE שמגביל את התוצאות שמוחזרות למיקומים עם הערכים המינימליים והרוחבים של קו הרוחב וקו האורך.

אפשר לנסות לבצע את הדוגמה הבאה במסוף BigQuery. השאילתה הזו מציגה נתונים סטטיסטיים ממוצעים לגבי נסיעות שהתחילו באזור מלבני שמכיל את אזור המידטאון ותחת מנהטן. יש שני מיקומים שונים שאפשר לנסות. כדי להריץ את השאילתה בנסיעות שהתחילו בנמל התעופה JFK, יש לבטל את התגובה שלה בקטע 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_Tip

avg_fare

avg_distance

avg_Tip_pc

avg_fare_mile

2.52

12.03

9.97

22.39

5.97

JFK

avg_Tip

avg_fare

avg_distance

avg_Tip_pc

avg_fare_mile

9.22

48.49

41.19

22.48

4.36

שאילתות לגבי רדיוס

אפשר גם ליצור בקלות שאילתות רדיוס ב-SQL אם יש לכם קצת מתמטיקה. באמצעות פונקציות מתמטיות SQL מדור קודם של BigQuery אפשר ליצור שאילתת SQL על סמך נוסחה של Harsine, שמציינת שטח עגול או מכסה כדורית על פני השטח של כדור הארץ.

הנה דוגמה להצהרת 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_Tip

avg_fare

avg_distance

avg_Tip_pc

avg_fare_mile

1.17

11.8

45.28

10.53

6.42

הברונקס

avg_Tip

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.

אלגוריתם Point in Polygon

יש דרכים רבות לחשב אם נקודה נכללת בפוליגון. כאן היא יציאה מ-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 בלבד.

אם רק אנחנו יכולים לראות את הנתונים במפה ונבדוק את הנתונים על ידי הגדרת אזורי עניין שרירותיים! בדיוק כמו שאפשר לעשות זאת באמצעות ממשקי ה-API של מפות Google. תחילה עליכם להפעיל את ה-API של מפות Google, להגדיר דף אינטרנט פשוט שיפעל במחשב המקומי שלכם, ולהתחיל להשתמש ב-BigQuery API לשליחת שאילתות מדף האינטרנט שלכם.

4. עבודה עם ממשקי ה-API של מפות Google

לאחר שהרצתם כמה שאילתות מרחביות פשוטות, השלב הבא הוא להמחיש את הפלט כדי לראות את הדפוסים. כדי לעשות את זה, צריך להפעיל את ה-API של מפות Google, לבנות דף אינטרנט ששולח שאילתות מהמפה ל-BigQuery ואז משרטט את התוצאות במפה.

הפעלת ה-API של JavaScript במפות

עבור Codelab זה, עליך להפעיל את ממשק API של JavaScript במפות Google בפרויקט שלך. כדי לעשות זאת, בצע את הפעולות הבאות:

  1. במסוף Google Cloud Platform, עוברים אל Marketplace
  2. ב-Marketplace, מחפשים את 'Maps JavaScript API'
  3. לוחצים על האריח של Maps JavaScript API בתוצאות החיפוש
  4. לוחצים על הלחצן 'הפעלה'

יצירת מפתח API

כדי לשלוח בקשות לפלטפורמה של מפות Google, עליך ליצור מפתח API ולשלוח אותו עם כל הבקשות. כדי ליצור מפתח API, יש לבצע את הפעולות הבאות:

  1. במסוף Google Cloud Platform, לוחצים על תפריט ההמבורגר כדי לפתוח את סרגל הניווט הימני
  2. בוחרים באפשרות ‘APIs & Service' > ‘Credentials'
  3. לוחצים על הלחצן 'Create Credential'' ולאחר מכן בוחרים באפשרות 'API Key'
  4. העתקה של מפתח ה-API החדש

הורדת הקוד והגדרת שרת אינטרנט

כדי להוריד את כל הקוד של Lablab זה, לוחצים על הלחצן הבא:

פותחים את הקובץ הדחוס. הפעולה הזו תשחרר תיקיית שורש (bigquery), שמכילה תיקייה אחת לכל שלב ב-codelab זה, יחד עם כל המשאבים הדרושים לך.

stepN התיקיות מכילות את מצב הסיום הרצוי של כל שלב ב-codelab זה. הם נמצאים לעיונכם. אנחנו נעשה את כל עבודת הקידוד שלנו בספרייה שנקראת work.

הגדרת שרת אינטרנט מקומי

אתם יכולים להשתמש בשרת האינטרנט שלכם, אך קוד Lab זה תוכנן לפעול היטב עם שרת האינטרנט של Chrome. אם עדיין לא התקנתם את האפליקציה הזו, תוכלו להתקין אותה מחנות האינטרנט של Chrome.

לאחר ההתקנה פותחים את האפליקציה. ב-Chrome אפשר לבצע את הפעולות הבאות:

  1. פתיחת Chrome
  2. בסרגל הכתובות שבחלק העליון, מקלידים chrome://apps
  3. הקש Enter
  4. בחלון שנפתח, לוחצים על סמל שרת האינטרנט. אפשר גם ללחוץ לחיצה ימנית על אפליקציה כדי לפתוח אותה בכרטיסייה רגילה או מוצמדת, במסך מלא או בחלון חדש a3ed00e79b8bfee7.png. לאחר מכן תופיע תיבת הדו-שיח הזו, שמאפשרת לך להגדיר את שרת האינטרנט המקומי שלך: 81b6151c3f60c948.png
  5. לוחצים על 'בחירת תיקייה&39'; ובוחרים את המיקום שאליו הורדתם את קובצי הדגימה של Codelab.
  6. בקטע 'אפשרויות', מסמנים את התיבה לצד 'הצגה אוטומטית של index.html': 17f4913500faa86f.png'
  7. מסיטים את לחצן החלפת המצב 'שרת אינטרנט: התחלה&#39'; בצד שמאל ואז בחזרה שמאלה, כדי להפסיק ואז מפעילים מחדש את שרת האינטרנט

a5d554d0d4a91851.png

5. המפה נטענת וכלי השרטוט

יצירת דף מפה בסיסי

התחילו בדף HTML פשוט שטוען מפות Google באמצעות ממשק API של JavaScript בכמה שורות ובכמה שורות JavaScript. הקוד מדוגמה פשוטה למפה במפות Google הוא מקום מצוין להתחיל. היא משוחזרת כאן כדי להעתיק ולהדביק אותה בעורך הטקסט או בכלי הפיתוח המשולב (IDE) שבחרת, או לפתוח את index.html מהמאגר שהורדת.

  1. העתקת index.html לתיקייה work בעותק המקומי של הנכס
  2. העתקת ה-img/ התיקייה לעבודה/ תיקייה בעותק המקומי של ה-repo
  3. פתיחת העבודה/index.html בעורך הטקסט או בסביבת הפיתוח המשולבת (IDE)
  4. מחליפים את YOUR_API_KEY במפתח ה-API שיצרתם קודם לכן
<script src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&callback=initMap"
    async defer></script>
  1. בדפדפן, פותחים את localhost:<port>/work, כאשר port הוא מספר היציאה שצוין בהגדרות של שרת האינטרנט המקומי. יציאת ברירת המחדל היא 8887. אתה אמור לראות את המפות הראשונות שלך.

אם מופיעה הודעת שגיאה בדפדפן, מוודאים שמפתח ה-API נכון ושרת האינטרנט המקומי פעיל.

שינוי מיקום ברירת המחדל ורמת הזום

הקוד שמגדיר את המיקום ואת רמת המרחק מהתצוגה הוא בשורה 27 & 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>

לאחר מכן, יש לטעון מחדש את המפה בדפדפן כדי לראות את התוצאות.

טעינת ספריות האיור והתצוגה החזותית

כדי להוסיף יכולות שרטוט למפה, צריך לשנות את הסקריפט שטוען את ממשק ה-API של JavaScript במפות Google, על ידי הוספת פרמטר אופציונלי, שבו נאמר ל-Google Maps Platform להפעיל את ספריית השרטוטים.

ב-codelab זה נעשה שימוש גם ב-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

כדי להשתמש בצורות שנוצרו על ידי המשתמשים כקלט של שאילתה, יש להוסיף את DrawingManager למפה. יש להפעיל את הכלים Circle, Rectangle ו-Polygon.

כדאי להוסיף את כל קוד ההגדרה של DrawingManager לפונקציה חדשה. לכן, בעותק של index.html, יש לבצע את הפעולות הבאות:

  1. יש להוסיף פונקציה בשם setUpDrawingTools()עם הקוד הבא כדי ליצור את DrawingManager ולהגדיר את מאפיין map שלה כך שיפנה לאובייקט המפה בדף.

האפשרויות שהועברו אל google.maps.drawing.DrawingManager(options) קבעו את סוג שרטוט הצורה המוגדר כברירת מחדל ואת אפשרויות התצוגה לצורות משורטטות. כדי לבחור אזורים במפה שיישלחו כשאילתות, לצורות צריך להיות שקיפות של אפס. מידע נוסף על האפשרויות הזמינות זמין ב-DraingManager 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.

נוסיף את הקוד הזה בשלב מאוחר יותר, אבל לעת עתה נאתר שלושה רכיבי handler של אירועים ריקים כדי לטפל באירועים rectanglecomplete, circlecomplete ו-polygoncomplete. בשלב זה, ה-handlers לא צריכים להריץ קוד כלשהו.

צריך להוסיף את הערכים הבאים בתחתית הפונקציה 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 Client API

שימוש ב-Google BigQuery Client API יעזור לכם להימנע מכתיבת הרבה קוד קידוד הנדרש לבניית הבקשות, לניתוח תגובות ולטיפול באימות. Codelab זה משתמש ב-BigQuery API דרך ספריית הלקוחות של Google APIs ל-JavaScript, מכיוון שאנחנו מפתחים אפליקציה מבוססת דפדפן.

בשלב הבא, תצטרכו להוסיף קוד כדי לטעון את ה-API הזה בדף אינטרנט ולהשתמש בו לצורך אינטראקציה עם BigQuery.

הוספת Google Client API ל-JavaScript

כדי להריץ שאילתות מול BigQuery, תשתמשו ב-Google Client API ל-JavaScript. בעותק של index.html (בתיקייה work), יש לטעון את ה-API באמצעות תג <script> כזה. יש להציב את התג מתחת לתג <script> שטוען את ה-API של מפות Google:

<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, נכנסים לתפריט הניווט ובוחרים באפשרות APIs & Services > Credentials (פרטי כניסה).

לפני שתוכלו להגדיר את פרטי הכניסה שלכם, תצטרכו להוסיף תצורה כלשהי למסך ההרשאה שמשתמשי הקצה יראו את ההרשאה שלהם לאפליקציה שלכם לגשת לנתוני BigQuery בשמם.

כדי לעשות זאת, לוחצים על הכרטיסייה מסך הסכמה ל-OAuth. 2. יש להוסיף את Big Query API להיקפים של האסימון הזה. לוחצים על הלחצן הוספת היקף בקטע 'היקפים עבור Google APIs'. 3. מהרשימה, מסמנים את התיבה לצד הערך Big Query API עם ההיקף ../auth/bigquery. 4. לוחצים על "הוספה". 5. מזינים שם בשדה 'שם האפליקציה&#39'. 6. לוחצים על שמירה כדי לשמור את ההגדרות. 7. השלב הבא הוא ליצור מזהה לקוח ב-OAuth. כדי לעשות זאת, לוחצים על יצירת פרטי כניסה:

4d18a965fc760e39.png

  1. בתפריט הנפתח, לוחצים על מזהה לקוח OAuth. 1f8b36a1c27c75f0.png
  2. בקטע "סוג אפליקציה" בוחרים באפשרות אפליקציית אינטרנט.
  3. בשדה 'שם האפליקציה' מקלידים שם לפרויקט. לדוגמה "BigQuery ומפות Google .
  4. בקטע הגבלות, בשדה 'מקורות JavaScript מורשים' מזינים את כתובת ה-URL של localhost, כולל מספרי יציאות. למשל: http://localhost:8887
  1. לוחצים על הלחצן יצירה.

בחלון קופץ יופיע מזהה הלקוח וסוד הלקוח. יש צורך במזהה הלקוח כדי לבצע אימות מול BigQuery. מעתיקים אותו ומדביקים אותו ב-work/index.html כמשתנה JavaScript גלובלי חדש בשם clientId.

let clientId = 'YOUR_CLIENT_ID';

7. הרשאה ואתחול

דף האינטרנט צריך לתת למשתמש הרשאה לגשת ל-BigQuery לפני הפעלה של המפה. בדוגמה הזו אנחנו משתמשים ב-OAuth 2.0, כמתואר בקטע ההרשאות במסמכי התיעוד של JavaScript Client API. כדי לשלוח שאילתות, יש להשתמש במזהה הלקוח ב-OAuth ובמזהה הפרויקט.

כש-Google Client API נטען בדף האינטרנט, צריך לבצע את השלבים הבאים:

  • מאשרים את המשתמש.
  • אם הוא מורשה, יש לטעון את BigQuery API.
  • טוענים את המפה ומאתחלים אותה.

ב-step3/map.html ניתן לראות דוגמה לאופן שבו דף ה-HTML יסתיים.

אישור המשתמש

משתמש הקצה של האפליקציה צריך להעניק לאפליקציה הרשאת גישה לנתוני BigQuery בשמם. לשם כך, Google Client API ל-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 API

לאחר שהמשתמש קיבל הרשאה, טוענים את ה-API של BigQuery.

תחילה, יש להתקשר אל gapi.auth.authorize() עם המשתנה clientId שהוספת בשלב הקודם. יש לטפל בתגובה בפונקציית קריאה חוזרת בשם handleAuthResult.

הפרמטר immediate קובע אם חלון קופץ יוצג למשתמש. יש להגדיר את הערך ל-true כדי לבטל את החלון הקופץ של ההרשאה אם המשתמש כבר מורשה.

מוסיפים לדף פונקציה בשם handleAuthResult(). הפונקציה צריכה לקחת פרמטר authresult, שיאפשר לך לשלוט בזרימת הלוגיקה אם המשתמש קיבל הרשאה בהצלחה או לא.

יש להוסיף גם פונקציה בשם loadApi כדי לטעון את BigQuery API אם המשתמש מורשה.

מוסיפים לוגיקה לפונקציה handleAuthResult() כדי לקרוא ל-loadApi()אם יש אובייקט authResult בפונקציה, ואם לאובייקט{0}error יש ערך false.

צריך להוסיף קוד לפונקציה loadApi() כדי לטעון את BigQuery API באמצעות השיטה 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 של ממשק ה-API של מפות Google נטען.

אפשר לעשות זאת על ידי קריאה לפונקציה initMap() מהשיטה then() לאחר השיטה load() באובייקט gapi.client.

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

8. קונספטים של BigQuery API

קריאות ל-BigQuery API בדרך כלל מופעלות בשניות, אבל לא ניתן להחזיר אותן באופן מיידי. יש לכם צורך בלוגיקה כלשהי כדי לבדוק את BigQuery כדי לברר את הסטטוס של משרות ארוכות טווח, ולאחזר את התוצאות רק אחרי שהמשימה הושלמה.

הקוד המלא של שלב זה נמצא בכתובת step4/map.html.

שליחת בקשה

מוסיפים ל-work/index.html פונקציית JavaScript כדי לשלוח שאילתה באמצעות ה-API, ומשתנים מסוימים כדי לאחסן את הערכים של מערך הנתונים ב-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 מראה איך לבדוק את הסטטוס של משימה באופן תקופתי, באמצעות שיטת ה-API 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 API. מוסיפים לדף פונקציה בשם 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'.

בשאילתות מסוג bounding ו-radius, אפשר לקרוא ל-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

כדי ליצור שאילתה בטבלה באמצעות ה-API של BigQuery, עליכם לספק את שם הטבלה בצורה מלאה ומלאה בשאילתת ה-SQL. הפורמט ב-SQL סטנדרטי הוא project.dataset.tablename. ב-SQL מדור קודם, זה project.dataset.tablename.

במקום יש שולחנות רבים של נסיעות במונית בעיר ניו יורק. כדי לראות אותם, צריך לעבור אל מסוף האינטרנט של BigQuery ולהרחיב את פריט התפריט "Public מאתרים את מערך הנתונים שנקרא new_york ומרחיבים אותו כדי לראות את הטבלאות. בוחרים את טבלת הנסיעות 'צהוב מוניות': bigquery-public-data.new_york_taxi_trips.tlc_yellow_trips_2016).

ציון מזהה הפרויקט

בשיחת ה-API, עליכם לציין את שם הפרויקט שלכם ב-Google Cloud Platform למטרות חיוב. ב-codelab זה, הוא לא אותו פרויקט כמו זה שמכיל את הטבלה. אם עבדתם עם טבלה שיצרתם בפרויקט משלכם על ידי העלאת נתונים, מזהה הפרויקט הזה יהיה זהה לזה שבהצהרת ה-SQL.

הוסיפו משתני JavaScript לקוד כדי לכלול הפניות לפרויקט בפרויקט הנתונים הציבורי, שמכיל את הטבלה שעליה אתם שולחים שאילתה, וגם את שם הטבלה ושם מערך הנתונים. יש צורך גם במשתנה נפרד כדי להפנות למזהה הפרויקט שלכם לחיוב.

יש להוסיף משתני JavaScript גלובליים שנקראים billingProjectId, publicProjectId, datasetId ו-tableName לעותק של index.html.

מפעילים את המשתנים 'publicProjectId', 'datasetId' ו-'tableName' עם הפרטים מפרויקט מערך הנתונים הציבורי של BigQuery. מפעילים את billingProjectId בעזרת מזהה הפרויקט שלכם (המזהה שיצרתם ב-"הגדרה ו&ציטוט; מוקדם יותר ב-Codelab הזה).

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.

כדי שכמות הנתונים המועברת לדף האינטרנט שלך תהיה בגודל סביר עבור קוד Lab זה, צריך לשנות את הפונקציה rectangleSQL() כדי להוסיף הצהרה שמגבילה את התגובה ל-10,000 שורות. בדוגמה הבאה, משתנה זה מצוין במשתנה בינלאומי בשם 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;
}

כדי להמחיש את הצפיפות של המיקומים, אפשר להשתמש במפת חום. המטרה של ה-API של JavaScript במפות היא מחלקה HeatmapLayer המיועדת למטרה זו. ה-heatmapLayer מקבל מערך של קואורדינטות של קו אורך וקו רוחב, כך שקל מאוד להמיר את השורות שהוחזרו מהשאילתה למפת חום.

בפונקציה 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. שליחת שאילתות לפי רדיוס מסביב לנקודה מסוימת

שאילתות רדיוס דומות מאוד. באמצעות פונקציות מתמטיות SQL מדור קודם של BigQuery אפשר ליצור שאילתת SQL על סמך נוסחה של Harsine, שמעריכה שטח מעגלי על פני כדור הארץ.

אם משתמשים באותה שיטה למלבנים, אפשר לטפל באירוע OverlayComplete כדי לקבל את המרכז והרדיוס של עיגול שצייר המשתמש, וליצור SQL דומה לשאילתה.

דוגמה פעילה של הקוד של השלב הזה כלולה במאגר הקודים בתור step6/map.html.

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

בעותק של index.html, יש להוסיף שתי פונקציות ריקות חדשות: circleQuery() ו-haversineSQL().

לאחר מכן, מוסיפים handler של אירוע 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.

UDFs ב-BigQuery API

הגישה של BigQuery API לגבי UDF שונה מעט ממסוף האינטרנט: צריך לקרוא ל-jobs.insert method.

עבור שאילתות SQL סטנדרטיות דרך ה-API, יש צורך בהצהרת 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.

שנית, הקוד בונה את הצהרת ה-SQL הראשית SELECT. בדוגמה הזו, ה-UDF נקרא בביטוי WHERE.

שילוב עם ה-API של מפות Google

כדי להשתמש בזה עם ספריית ה-API של מפות Google, אנחנו צריכים לשמור את הפוליגון שנשלף על ידי המשתמש ולהעביר אותו לקטע 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.

אפשר לעיין בשלב 7/map.html לקבלת דוגמה לכך.

פלט לדוגמה

לפניכם תוצאה לדוגמה של יצירת שאילתות לגבי איסוף נתוני המוניות הצהובים משנת 2016 של ניו יורק ב-BigQuery באמצעות פוליגון חופשי, והנתונים שנבחרו משופים כמפת חום.

צילום מסך 2017-05-09 בשעה 10:00.48 AM

13. אנחנו מתקדמים

הנה כמה הצעות לדרכים שבהן ניתן להרחיב את קוד Lab זה להיבטים אחרים של הנתונים. ניתן למצוא דוגמה פעילה לרעיונות האלה בכתובת 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 באינדקס. ניתן לקרוא את האינדקסים האלה ממשתנה כדי שיהיה קל יותר לנהל את הקוד. NB של 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. צפיפות גבוהה יותר שלא מוצגת ברמת זום זו כוללת את נמל התעופה לה גוארדיה ונמל התעופה JFK, מרכז הסחר העולמי ופארק הסוללה.

צילום מסך ב-9 בספטמבר 2017 בשעה 10:40

עיצוב מפת הבסיס

כשיוצרים מפה של Google באמצעות ממשק API של JavaScript במפות Google, אפשר להגדיר את סגנון המפה באמצעות אובייקט 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);
  })
}

כדי להחליף את התצוגה של האנימציה, מוסיפים פונקציה לשליטה בחשיפה. פונקציה זו תשנה את השקיפות של כל רכיב DOM של HTML שהועבר אליו.

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.7.2017 בשעה 14:32.19

אתם יכולים לעיין בדוגמה המלאה בכתובת 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, עיינו בהצעות הבאות.

כדי לקבל מידע נוסף על שירות מחסן הנתונים (data warehouse) בקנה מידה של שרת ללא פטה (serverless), אפשר לעיין במאמר מה זה BigQuery.

כדאי לעיין במדריך ליצירת אפליקציה פשוטה באמצעות BigQuery API.

במדריך למפתחים של ספריית השרטוטים מפורט פרטים נוספים על הפעלת אינטראקציות של משתמשים כדי לשרטט צורות ב'מפות Google'.

יש דרכים נוספות להצגה חזותית של נתונים במפת Google.

יש לעיין במדריך לתחילת העבודה עם JavaScript Client-AP כדי להבין את העקרונות הבסיסיים של השימוש ב-Client API כדי לגשת אל ממשקי API אחרים של Google.