ค้นหาและแสดงภาพข้อมูลตําแหน่งใน BigQuery ด้วย Google Maps Platform (JavaScript)
เกี่ยวกับ Codelab นี้
1 ภาพรวม
Maps เป็นเครื่องมือที่มีประสิทธิภาพมากเมื่อแสดงภาพรูปแบบในชุดข้อมูลที่เกี่ยวข้องกับตําแหน่งในบางกรณี ความสัมพันธ์นี้อาจเป็นชื่อสถานที่ ค่าละติจูดและลองจิจูดที่เฉพาะเจาะจง หรือชื่อพื้นที่ที่มีขอบเขตที่เฉพาะเจาะจง เช่น ข้อมูลสํามะโนประชากรหรือรหัสไปรษณีย์
เมื่อชุดข้อมูลเหล่านี้มีขนาดใหญ่มาก อาจทําให้ค้นหาและแสดงภาพข้อมูลได้ยากโดยใช้เครื่องมือทั่วไป เมื่อใช้ Google BigQuery เพื่อค้นหาข้อมูลและ Google Maps API เพื่อสร้างการค้นหาและแสดงผลลัพธ์ออกมาเป็นภาพ คุณจะสํารวจรูปแบบทางภูมิศาสตร์ในข้อมูลได้อย่างรวดเร็วด้วยการตั้งค่าหรือการเขียนโค้ดน้อยมาก และไม่จําเป็นต้องจัดการระบบเพื่อจัดเก็บชุดข้อมูลขนาดใหญ่มาก
สิ่งที่คุณจะสร้าง
ใน Codelab นี้ คุณจะต้องเขียนและเรียกใช้คําค้นหาบางรายการที่ให้ข้อมูลเชิงลึกเกี่ยวกับชุดข้อมูลสาธารณะขนาดใหญ่โดยใช้ BigQuery นอกจากนี้ คุณยังจะสร้างหน้าเว็บที่โหลดแผนที่โดยใช้ JavaScript API ของ Google Maps Platform จากนั้นเรียกใช้และแสดงภาพการค้นหาเชิงพื้นที่ด้วยชุดข้อมูลสาธารณะขนาดใหญ่มากๆ โดยใช้ไลบรารีของไคลเอ็นต์ Google API สําหรับ JavaScript และ BigQuery API
สิ่งที่คุณจะได้เรียนรู้
- วิธีค้นหาชุดข้อมูลตําแหน่งระดับเพตะไบต์เป็นวินาทีด้วย BigQuery โดยใช้การค้นหา SQL ฟังก์ชันที่กําหนดโดยผู้ใช้ และ BigQuery API
- วิธีใช้ Google Maps Platform เพื่อเพิ่ม Google Maps ลงในหน้าเว็บและให้ผู้ใช้วาดรูปร่างในนั้น
- วิธีแสดงภาพการค้นหาที่มีต่อชุดข้อมูลขนาดใหญ่บน Google Maps ดังในภาพตัวอย่างด้านล่าง ซึ่งแสดงความหนาแน่นของการปล่อยรถแท็กซี่ในปี 2016 จากการเดินทางที่เริ่มจากบล็อกรอบอาคารเอ็มไพร์สเตท
สิ่งที่ต้องมี
- ความรู้เบื้องต้นเกี่ยวกับ HTML, CSS, JavaScript, SQL และเครื่องมือสําหรับนักพัฒนาเว็บใน Chrome
- เว็บเบราว์เซอร์รุ่นใหม่ เช่น Chrome, Firefox, Safari หรือ Edge เวอร์ชันล่าสุด
- เครื่องมือแก้ไขข้อความหรือ IDE ที่ต้องการ
เทคโนโลยี
BigQuery
BigQuery คือบริการวิเคราะห์ข้อมูลของ Google สําหรับชุดข้อมูลขนาดใหญ่มาก มี RESTful API และรองรับการค้นหาที่เขียนใน SQL หากมีข้อมูลที่มีค่าละติจูดและลองจิจูด ระบบจะใช้ค่าเหล่านี้ในการค้นหาข้อมูลของคุณตามสถานที่ ข้อได้เปรียบคือคุณสามารถสํารวจชุดข้อมูลขนาดใหญ่มากเพื่อดูรูปแบบโดยไม่ต้องจัดการโครงสร้างพื้นฐานของเซิร์ฟเวอร์หรือฐานข้อมูล คุณจะหาคําตอบให้ได้ในไม่กี่วินาที ไม่ว่าตารางจะใหญ่แค่ไหนก็ตาม ด้วยความสามารถในการปรับขนาดและโครงสร้างพื้นฐานที่มีการจัดการขนาดใหญ่ของ BigQuery
Google Maps Platform
Google Maps Platform ให้สิทธิ์เข้าถึงข้อมูลแผนที่ สถานที่ และเส้นทางของ Google แบบเป็นโปรแกรม ปัจจุบันเว็บไซต์และแอปกว่า 2 ล้านรายการใช้เว็บไซต์และแอปดังกล่าวเพื่อให้บริการแผนที่และข้อมูลตําแหน่งที่อิงตามตําแหน่งแบบฝังให้กับผู้ใช้
Google Platform API Drawing Layer ช่วยให้คุณวาดรูปร่างในแผนที่ได้ ตัวแปรเหล่านี้สามารถแปลงเป็นอินพุตเพื่อเรียกใช้คําค้นหากับตาราง BigQuery ที่เก็บค่าละติจูดและลองจิจูดในคอลัมน์ได้
หากต้องการเริ่มต้นใช้งาน คุณต้องมีโปรเจ็กต์ Google Cloud Platform ที่เปิดใช้ BigQuery และ Maps API
2 การเริ่มตั้งค่า
บัญชี Google
หากยังไม่มีบัญชี Google (Gmail หรือ Google Apps) คุณต้องสร้างบัญชี
สร้างโปรเจ็กต์
ลงชื่อเข้าใช้คอนโซล Google Cloud Platform ( console.cloud.google.com) และสร้างโปรเจ็กต์ใหม่ มีเมนูแบบเลื่อนลงของโปรเจ็กต์ที่ด้านบนของหน้าจอ
เมื่อคุณคลิกเมนูแบบเลื่อนลงของโปรเจ็กต์นี้ คุณจะได้รับรายการในเมนูที่ช่วยให้คุณสร้างโปรเจ็กต์ใหม่ได้
ในช่องที่มีข้อความ "" ป้อนชื่อใหม่สําหรับโปรเจ็กต์" ป้อนชื่อโปรเจ็กต์ใหม่ เช่น "BigQuery Codelab"
ระบบจะสร้างรหัสโปรเจ็กต์ให้คุณ รหัสโปรเจ็กต์เป็นชื่อที่ไม่ซ้ํากันสําหรับโปรเจ็กต์ Google Cloud ทั้งหมด จดจํารหัสโปรเจ็กต์ เนื่องจากคุณจะใช้ในภายหลัง ชื่อข้างต้นมีผู้อื่นใช้ไปแล้วและคุณจะใช้ไม่ได้ ใส่รหัสโปรเจ็กต์ของคุณเองได้ทุกที่ที่เห็น YOUR_PROJECT_ID ใน Codelab นี้
เปิดใช้การเรียกเก็บเงิน
หากต้องการลงชื่อสมัครใช้ BigQuery ให้ใช้โปรเจ็กต์ที่เลือกหรือสร้างไว้ในขั้นตอนก่อนหน้า ต้องเปิดใช้การเรียกเก็บเงินในโปรเจ็กต์นี้ เมื่อเปิดใช้การเรียกเก็บเงินแล้ว คุณจะเปิดใช้ BigQuery API ได้
วิธีเปิดใช้การเรียกเก็บเงินจะขึ้นอยู่กับว่าคุณกําลังสร้างโปรเจ็กต์ใหม่หรือเปิดใช้การเรียกเก็บเงินอีกครั้งสําหรับโปรเจ็กต์ที่มีอยู่
Google มอบสิทธิ์ทดลองใช้ฟรี 12 เดือนสําหรับการใช้งาน Google Cloud Platform มูลค่า $300 ซึ่งคุณจะสามารถใช้ใน Codelab นี้ได้ ดูรายละเอียดเพิ่มเติมได้ที่ https://cloud.google.com/free/
โปรเจ็กต์ใหม่
เมื่อสร้างโปรเจ็กต์ใหม่ ระบบจะแจ้งให้คุณเลือกบัญชีสําหรับการเรียกเก็บเงินที่ต้องการลิงก์กับโปรเจ็กต์ หากมีบัญชีสําหรับการเรียกเก็บเงินเพียงบัญชีเดียว ระบบจะลิงก์บัญชีดังกล่าวกับโปรเจ็กต์ของคุณโดยอัตโนมัติ
หากคุณไม่มีบัญชีการเรียกเก็บเงิน คุณจะต้องสร้างบัญชีสําหรับการเรียกเก็บเงินและเปิดใช้โปรเจ็กต์ก่อนจึงจะสามารถใช้ฟีเจอร์หลายรายการของ Google Cloud Platform ได้ หากต้องการสร้างบัญชีสําหรับการเรียกเก็บเงินใหม่และเปิดใช้การเรียกเก็บเงินสําหรับโปรเจ็กต์ ให้ทําตามวิธีการในสร้างบัญชีสําหรับการเรียกเก็บเงินใหม่
โปรเจ็กต์ที่มีอยู่
หากมีโปรเจ็กต์ที่ปิดใช้การเรียกเก็บเงินชั่วคราว คุณเปิดใช้การเรียกเก็บเงินอีกครั้งได้โดยทําดังนี้
- ไปที่คอนโซล Cloud Platform
- เลือกโปรเจ็กต์เพื่อเปิดใช้การเรียกเก็บเงินอีกครั้งจากรายการโปรเจ็กต์
- เปิดเมนูคอนโซลด้านซ้าย แล้วเลือกการเรียกเก็บเงิน
ระบบจะแจ้งให้คุณเลือกบัญชีสําหรับการเรียกเก็บเงิน
- คลิกตั้งค่าบัญชี
สร้างบัญชีสําหรับการเรียกเก็บเงินใหม่
วิธีสร้างบัญชีสําหรับการเรียกเก็บเงินใหม่
- ไปที่คอนโซล Cloud Platform แล้วลงชื่อเข้าใช้หรือหากยังไม่มีบัญชี ให้ลงชื่อสมัครใช้
- เปิดเมนูคอนโซลด้านซ้าย แล้วเลือกการเรียกเก็บเงิน
- คลิกปุ่มบัญชีสําหรับการเรียกเก็บเงินใหม่ (โปรดทราบว่าหากไม่ใช่บัญชีสําหรับการเรียกเก็บเงินบัญชีแรกของคุณ คุณจะต้องเปิดรายการบัญชีสําหรับการเรียกเก็บเงินโดยคลิกชื่อของบัญชีสําหรับการเรียกเก็บเงินที่มีอยู่ ใกล้กับด้านบนของหน้า แล้วคลิกจัดการบัญชีสําหรับการเรียกเก็บเงิน)
- ป้อนชื่อของบัญชีสําหรับการเรียกเก็บเงินและป้อนข้อมูลสําหรับการเรียกเก็บเงิน ตัวเลือกที่คุณเห็นจะขึ้นอยู่กับประเทศตามที่อยู่สําหรับการเรียกเก็บเงินของคุณ โปรดทราบว่าสําหรับบัญชีในสหรัฐอเมริกา คุณจะเปลี่ยนสถานะภาษีไม่ได้หลังจากสร้างบัญชีแล้ว
- คลิกส่งและเปิดใช้การเรียกเก็บเงิน
โดยค่าเริ่มต้น ผู้ที่สร้างบัญชีสําหรับการเรียกเก็บเงินคือผู้ดูแลระบบการเรียกเก็บเงินของบัญชีนั้น
สําหรับข้อมูลเกี่ยวกับการยืนยันบัญชีธนาคารและการเพิ่มวิธีการชําระเงินสํารอง โปรดดูหัวข้อเพิ่ม นําออก หรืออัปเดตวิธีการชําระเงิน
เปิดใช้ BigQuery API
หากต้องการเปิดใช้ BigQuery API ในโปรเจ็กต์ ให้ไปที่ตลาดกลางหน้า BigQuery API ในคอนโซล แล้วคลิกปุ่ม "เปิดใช้' สีน้ําเงิน
3 การค้นหาข้อมูลตําแหน่งใน BigQuery
มี 3 วิธีในการค้นหาข้อมูลตําแหน่งที่จัดเก็บไว้เป็นค่าละติจูดและลองจิจูดใน BigQuery
- คําค้นหารูปสี่เหลี่ยมผืนผ้า: ระบุพื้นที่ที่สนใจว่าเป็นคําค้นหาที่เลือกทุกแถวภายในช่วงละติจูดและลองจิจูดต่ําสุดและสูงสุด
- การค้นหารัศมี: ระบุพื้นที่ที่สนใจโดยคํานวณวงกลมรอบจุดโดยใช้สูตรฮาร์ซีนและฟังก์ชันคณิตศาสตร์เพื่อจําลองรูปร่างของโลก
- การค้นหารูปหลายเหลี่ยม: ระบุรูปร่างที่กําหนดเองและใช้ฟังก์ชันที่ผู้ใช้กําหนดเพื่อแสดงตรรกะของจุดเข้ารูปหลายเหลี่ยมเพื่อทดสอบ โดยให้ละติจูดและลองจิจูดของแต่ละแถวอยู่ในรูปร่าง
หากต้องการเริ่มต้นใช้งาน ให้ใช้ตัวแก้ไขคําค้นหาในส่วน Big Query ของคอนโซล Google Cloud Platform เพื่อเรียกใช้การค้นหาต่อไปนี้กับข้อมูลแท็กซี่ NYC
SQL มาตรฐานกับ SQL แบบเดิม
BigQuery รองรับ SQL 2 เวอร์ชัน ได้แก่ SQL เดิมและ SQL มาตรฐาน นโยบายหลังคือมาตรฐาน ANSI 2011 สําหรับวัตถุประสงค์ของบทแนะนํานี้ เราจะใช้ SQL มาตรฐาน เนื่องจากมีการปฏิบัติตามข้อกําหนดมาตรฐานดีกว่า
หากต้องการเรียกใช้ SQL เดิมในเครื่องมือแก้ไข BigQuery คุณดําเนินการได้ดังต่อไปนี้
- คลิกปุ่ม "เพิ่มเติม'
- เลือก "การตั้งค่าการค้นหา' จากเมนูแบบเลื่อนลง
- ในส่วน "ภาษา SQL' ให้เลือกปุ่มตัวเลือก "Legacy'
- คลิกปุ่ม "บันทึก"
การค้นหารูปสี่เหลี่ยมผืนผ้า
คําค้นหาสี่เหลี่ยมผืนผ้าจะค่อนข้างตรงไปตรงมาใน BigQuery คุณแค่ต้องเพิ่มอนุประโยค WHERE
ที่จํากัดผลลัพธ์ซึ่งได้ตําแหน่งซึ่งมีค่าต่ําสุดและสูงสุดสําหรับพิกัดละติจูดและลองจิจูด
ลองดูตัวอย่างด้านล่างในคอนโซล BigQuery นี่คือการค้นหาสถิติการเดินทางเฉลี่ยบางส่วนสําหรับเครื่องเล่นที่เริ่มในพื้นที่รูปสี่เหลี่ยมผืนผ้า มีเมืองและเขตแมนฮัตตันตอนล่าง คุณลองใช้สถานที่ที่แตกต่างกัน 2 แห่งได้ ยกเลิกการแสดงความคิดเห็นในข้อที่ WHERE
ที่สองเพื่อเรียกใช้คําค้นหาที่เริ่มจากสนามบิน JFK
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
ผลการค้นหาจากทั้ง 2 คําค้นหาพบว่าระยะทางในการเดินทาง ค่าโดยสาร และเคล็ดลับสําหรับการรับสินค้าทั้ง 2 แห่งมีความแตกต่างกันอย่างมาก
แมนฮัตตัน
เคล็ดลับเฉลี่ย | ราคาเฉลี่ย [avg_fare] | ระยะทางเฉลี่ย | เคล็ดลับเฉลี่ย [avg_Tip_pc] | ระยะทางเฉลี่ย [avg_fare_mile] |
2.52 | 12.03 | 9.97 | 22.39 | 5.97 |
JFK
เคล็ดลับเฉลี่ย | ราคาเฉลี่ย [avg_fare] | ระยะทางเฉลี่ย | เคล็ดลับเฉลี่ย [avg_Tip_pc] | ระยะทางเฉลี่ย [avg_fare_mile] |
9.22 | 48.49 | 41.19 | 22.48 | 4.36 |
การค้นหารัศมี
นอกจากนี้ยังสร้างคําค้นหาแบบรัศมีใน SQL ได้ง่ายๆ หากรู้จักคณิตศาสตร์เล็กน้อย เมื่อใช้ฟังก์ชันคณิตศาสตร์ของ SQL เดิมของ BigQuery คุณจะสร้างการค้นหา SQL ได้โดยใช้สูตรฮาร์ซีนซึ่งจะประมาณพื้นที่วงกลมหรือทรงกลมบนพื้นผิวโลก
ต่อไปนี้คือตัวอย่างคําสั่ง BigQuery SQL สําหรับคําค้นหาวงกลมที่ศูนย์กลางอยู่ที่ 40.73943, -73.99585
ซึ่งมีรัศมี 0.1 กม.
จะใช้ค่าคงที่ 111.045 กิโลเมตรโดยประมาณ ระยะห่างระหว่าง 1 องศา
ดังตัวอย่างต่อไปนี้ที่ 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 เพื่อดูผลลัพธ์ เปลี่ยนละติจูดและลองจิจูดเพื่อเปรียบเทียบกับพื้นที่อื่นๆ เช่น สถานที่ใน Bronx
#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_fare] | ระยะทางเฉลี่ย | เคล็ดลับเฉลี่ย [avg_Tip_pc] | ระยะทางเฉลี่ย [avg_fare_mile] |
1.17 | 8 พ.ย. | 45.28 ปี | 10.53 | 6.42 ปี |
บรองซ์
เคล็ดลับเฉลี่ย | ราคาเฉลี่ย [avg_fare] | ระยะทางเฉลี่ย | เคล็ดลับเฉลี่ย [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
วิธีคํานวณว่าจุดหนึ่งๆ จะอยู่ในรูปหลายเหลี่ยมใน 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
ยินดีด้วย
คุณเรียกใช้ข้อความค้นหาเชิงพื้นที่ 3 ประเภทโดยใช้ BigQuery แล้ว ตามที่คุณเห็น ตําแหน่งที่สร้างความแตกต่างอย่างมากให้กับข้อมูลผลลัพธ์สําหรับคําค้นหาจากชุดข้อมูลนี้ แต่หากคุณเดาไม่ออกว่าจะเรียกใช้คําค้นหาที่ใด ก็จะเป็นเรื่องยากที่จะพบรูปแบบตามพื้นที่เฉพาะด้วยคําค้นหา SQL เท่านั้น
เฉพาะเราแสดงภาพข้อมูลบนแผนที่และสํารวจข้อมูลได้โดยการกําหนดพื้นที่ความสนใจที่กําหนดเอง การใช้ Google Maps API ทําให้คุณทําได้แน่นอน ก่อนอื่น คุณต้องเปิดใช้ Maps API แล้วตั้งค่าหน้าเว็บที่เรียบง่ายซึ่งทํางานในเครื่องของคุณเอง และเริ่มใช้ BigQuery API เพื่อส่งคําค้นหาจากหน้าเว็บ
4 การทํางานกับ Google Maps API
หลังจากเรียกใช้การค้นหาเชิงพื้นที่แล้ว ขั้นตอนต่อไปคือแสดงภาพเอาต์พุตเพื่อดูรูปแบบ หากต้องการทําเช่นนั้น คุณจะต้องเปิดใช้ Maps API สร้างหน้าเว็บที่ส่งคําค้นหาจากแผนที่ไปยัง BigQuery จากนั้นแสดงผลลัพธ์บนแผนที่
เปิดใช้ Maps JavaScript API
สําหรับ Codelab นี้ คุณจะต้องเปิดใช้ JavaScript Maps API ของ Google Maps Platform' ซึ่งทำได้ดังนี้:
- ไปที่ตลาดกลางในคอนโซล Google Cloud Platform
- ใน Marketplace ให้ค้นหา "Maps JavaScript API'
- คลิกการ์ดสําหรับ Maps JavaScript API ในผลการค้นหา
- คลิกปุ่ม "เปิดใช้'
สร้างคีย์ API
หากต้องการส่งคําขอไปยัง Google Maps Platform คุณจะต้องสร้างคีย์ API และส่งไปพร้อมกับคําขอทั้งหมด วิธีสร้างคีย์ API
- ในคอนโซล Google Cloud Platform ให้คลิกเมนูแฮมเบอร์เกอร์เพื่อเปิดการนําทางด้านซ้าย
- เลือก ‘APIs & Service' > ‘ข้อมูลเข้าสู่ระบบ'
- คลิกปุ่ม "สร้างข้อมูลรับรอง' จากนั้นเลือก "คีย์ API'
- คัดลอกคีย์ API ใหม่
ดาวน์โหลดโค้ดและตั้งค่าเว็บเซิร์ฟเวอร์
คลิกปุ่มต่อไปนี้เพื่อดาวน์โหลดโค้ดทั้งหมดสําหรับ Codelab นี้
คลายการคลายไฟล์ ZIP ที่ดาวน์โหลด การดําเนินการนี้จะคลายโฟลเดอร์รูท (bigquery
) ซึ่งประกอบด้วย 1 โฟลเดอร์สําหรับแต่ละขั้นตอนของ Codelab นี้ พร้อมด้วยทรัพยากรทั้งหมดที่จําเป็น
โฟลเดอร์ stepN
มีสถานะสิ้นสุดที่ต้องการในแต่ละขั้นตอนของ Codelab นี้ เพื่อการอ้างอิง เราจะทําการเขียนโค้ดทั้งหมดในไดเรกทอรีที่ชื่อ work
ตั้งค่าเว็บเซิร์ฟเวอร์ในเครื่อง
แม้ว่าคุณจะใช้เว็บเซิร์ฟเวอร์ของคุณเองได้ แต่ Codelab นี้ได้รับการออกแบบมาให้ทํางานกับ Chrome เว็บสโตร์ได้ดี หากยังไม่มีการติดตั้งแอปดังกล่าว ให้ติดตั้งจาก Chrome เว็บสโตร์
หลังจากติดตั้งแล้ว ให้เปิดแอป ใน Chrome คุณสามารถทําสิ่งต่อไปนี้ได้
- เปิด Chrome
- พิมพ์ chrome://apps ในแถบที่อยู่ด้านบน
- กด Enter
- ในหน้าต่างที่เปิดขึ้นมา ให้คลิกไอคอนเว็บเซิร์ฟเวอร์หรือคลิกขวาที่แอปเพื่อเปิดในแท็บปกติหรือแท็บปักหมุด แบบเต็มหน้าจอ หรือหน้าต่างใหม่
จากนั้นคุณจะเห็นกล่องโต้ตอบถัดไปซึ่งช่วยให้คุณกําหนดค่าเว็บเซิร์ฟเวอร์ในเครื่องได้:
- คลิก "เลือกโฟลเดอร์'" และเลือกตําแหน่งที่ดาวน์โหลดไฟล์ตัวอย่าง Codelab ไปยัง
- ในส่วน ‘Options' ให้เลือกช่องถัดจาก ‘แสดง index.html' โดยอัตโนมัติ:
- เลื่อนปุ่มสลับที่มีป้ายกํากับ "เว็บเซิร์ฟเวอร์: StartED' ไปทางซ้ายแล้วย้อนกลับไปทางขวาเพื่อหยุดแล้วรีสตาร์ทเว็บเซิร์ฟเวอร์
5 กําลังโหลดแผนที่และเครื่องมือวาดภาพ
สร้างหน้าแผนที่พื้นฐาน
เริ่มต้นด้วยหน้า HTML แบบง่ายที่โหลด Google Maps โดยใช้ Maps JavaScript API และ JavaScript ไม่กี่บรรทัด โค้ดจาก ตัวอย่างแผนที่แบบง่ายของ Google Maps Platform! เป็นจุดเริ่มต้นที่ดี ซึ่งคัดลอกมาไว้ที่นี่เพื่อให้คุณคัดลอกและวางลงในโปรแกรมแก้ไขข้อความหรือ IDE ที่ต้องการ หรือค้นหาโดยเปิด index.html
จากที่เก็บที่คุณดาวน์โหลด
- คัดลอก
index.html
ไปยังโฟลเดอร์work
ในสําเนาในเครื่องของคุณ - คัดลอก img/ โฟลเดอร์ไปที่งาน/ โฟลเดอร์ในสําเนาที่เก็บในเครื่อง
- เปิดโปรไฟล์งาน/
index.html
ในเครื่องมือแก้ไขข้อความหรือ IDE - แทนที่
YOUR_API_KEY
ด้วยคีย์ API ที่สร้างไว้ก่อนหน้านี้
<script src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&callback=initMap"
async defer></script>
- เปิด
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>
จากนั้นโหลดแผนที่ซ้ําในเบราว์เซอร์เพื่อดูผลลัพธ์
โหลดไลบรารีภาพวาดและการแสดงภาพ
ในการเพิ่มความสามารถในการวาดลงในแผนที่ คุณสามารถเปลี่ยนสคริปต์ที่โหลด Maps JavaScript API ได้โดยการเพิ่มพารามิเตอร์ที่ไม่บังคับ ซึ่งจะบอก 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 ให้ดําเนินการดังนี้
- เพิ่มฟังก์ชันชื่อ
setUpDrawingTools()
ที่มีรหัสต่อไปนี้เพื่อสร้างDrawingManager
และตั้งค่าพร็อพเพอร์ตี้map
ให้อ้างอิงออบเจ็กต์แผนที่ในหน้า
ตัวเลือกที่ส่งไปยัง google.maps.drawing.DrawingManager(options)
จะกําหนดประเภทภาพวาดของรูปร่างเริ่มต้นและตัวเลือกการแสดงผลสําหรับรูปร่างที่วาด หากต้องการเลือกพื้นที่ในแผนที่เพื่อส่งเป็นคําค้นหา รูปร่างควรทึบแสงเป็น 0 ดูข้อมูลเพิ่มเติมเกี่ยวกับตัวเลือกที่พร้อมใช้งานได้ที่ตัวเลือก DrawingManager
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);
}
- เรียก
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();
}
- โหลด index.html ซ้ําและตรวจสอบว่าเครื่องมือวาดภาพปรากฏขึ้น และตรวจสอบว่าคุณสามารถใช้ภาพวาดเพื่อวาดวงกลม สี่เหลี่ยมผืนผ้า และรูปร่างรูปหลายเหลี่ยมได้
คุณสามารถคลิกและลากเพื่อวาดวงกลมและสี่เหลี่ยมผืนผ้าได้ แต่จะต้องวาดรูปหลายเหลี่ยมด้วยการคลิกที่จุดยอดแต่ละจุด แล้วดับเบิลคลิกเพื่อวาดรูปร่างให้เสร็จ
เหตุการณ์การวาด
คุณต้องมีโค้ดบางอย่างเพื่อจัดการเหตุการณ์ที่จะเริ่มทํางานเมื่อผู้ใช้วาดรูปร่างเสร็จ แบบเดียวกับที่คุณต้องใช้พิกัดของรูปร่างที่วาดไว้เพื่อสร้างการค้นหา SQL
เราจะใส่รหัสลงในขั้นตอนนี้ในภายหลัง แต่ตอนนี้เราจะจัดการเครื่องจัดการเหตุการณ์ว่าง 3 กลุ่มเพื่อจัดการเหตุการณ์ 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 Client API
ส่วน Google BigQuery Client API จะช่วยให้คุณไม่ต้องเขียนโค้ด Boilerplate จํานวนมากที่จําเป็นต่อการสร้างคําขอ แยกวิเคราะห์การตอบกลับ และจัดการการตรวจสอบสิทธิ์ Codelab นี้ใช้ BigQuery API ผ่านไลบรารีของไคลเอ็นต์ Google APIs สําหรับ JavaScript เนื่องจากเราจะพัฒนาแอปพลิเคชันบนเบราว์เซอร์
จากนั้นให้เพิ่มโค้ดเพื่อโหลด API นี้ในหน้าเว็บและใช้โค้ดเพื่อโต้ตอบกับ BigQuery
เพิ่ม Google Client API สําหรับ JavaScript
คุณจะใช้ Google Client API สําหรับ JavaScript เพื่อเรียกใช้การค้นหากับ BigQuery ในสําเนา index.html
(ในโฟลเดอร์ work
) ให้โหลด API โดยใช้แท็ก <script>
แบบนี้ วางแท็กที่ใต้แท็ก <script>
ที่โหลด Maps API ทันที
<script src='https://apis.google.com/js/client.js'></script>
หลังจากโหลด Google Client API แล้ว ให้อนุญาตให้ผู้ใช้เข้าถึงข้อมูลใน BigQuery โดยใช้ OAuth 2.0 ก่อนอื่นคุณต้องตั้งค่าข้อมูลรับรองบางอย่างในโปรเจ็กต์ Google Cloud Console
สร้างข้อมูลเข้าสู่ระบบ OAuth 2.0
- ใน Google Cloud Console จากเมนูการนําทาง ให้เลือก API และเครื่องหมายบริการ > ข้อมูลเข้าสู่ระบบ
ก่อนที่จะตั้งค่าข้อมูลเข้าสู่ระบบได้ คุณต้องเพิ่มการกําหนดค่าสําหรับหน้าจอการให้สิทธิ์ที่ผู้ใช้ปลายทางของแอปพลิเคชันจะเห็นเมื่อให้สิทธิ์แอปเข้าถึงข้อมูล BigQuery ในนามของผู้ใช้
หากต้องการดําเนินการนี้ ให้คลิกแท็บหน้าจอคํายินยอม OAuth 2. คุณต้องเพิ่ม Big Query API ในขอบเขตของโทเค็นนี้ คลิกปุ่มเพิ่มขอบเขตในส่วนขอบเขตสําหรับ Google APIs 3. ในรายการ ให้เลือกช่องถัดจากรายการ Big Query API ที่มีขอบเขต ../auth/bigquery
4. คลิก Add 5. ป้อนชื่อในช่อง "ชื่อแอปพลิเคชัน' 6. คลิกบันทึกเพื่อบันทึกการตั้งค่า 7. ต่อไป คุณจะต้องสร้างรหัสไคลเอ็นต์ OAuth โดยคลิกสร้างข้อมูลเข้าสู่ระบบ ดังนี้
- ในเมนูแบบเลื่อนลง ให้คลิกรหัสไคลเอ็นต์ OAuth
- ในส่วนเว็บแอปพลิเคชัน ให้เลือกเว็บแอปพลิเคชัน
- พิมพ์ชื่อแอปพลิเคชันลงในช่องแอปพลิเคชัน เช่น "BigQuery และ Maps"
- ในส่วน Restrictions ในช่อง URL ของ JavaScript ที่ได้รับอนุญาต ให้ป้อน URL ของ localhost ซึ่งรวมถึงหมายเลขพอร์ต ตัวอย่าง:
http://localhost:8887
- คลิกที่ปุ่มสร้าง
ป๊อปอัปจะแสดงรหัสไคลเอ็นต์และรหัสลับไคลเอ็นต์ คุณต้องมีรหัสไคลเอ็นต์เพื่อดําเนินการตรวจสอบสิทธิ์กับ BigQuery คัดลอกและวางลงใน work/index.html
เป็นตัวแปร JavaScript ร่วมใหม่ที่ชื่อว่า clientId
let clientId = 'YOUR_CLIENT_ID';
7 การให้สิทธิ์และการเริ่มต้น
หน้าเว็บของคุณจะต้องให้สิทธิ์ผู้ใช้ในการเข้าถึง BigQuery ก่อนที่จะเริ่มต้นแผนที่ ในตัวอย่างนี้ เราใช้ OAuth 2.0 ตามที่อธิบายไว้ในส่วนการให้สิทธิ์ของเอกสาร JavaScript Client API คุณต้องใช้รหัสไคลเอ็นต์ OAuth และรหัสโปรเจ็กต์เพื่อส่งคําถาม
เมื่อมีการโหลด API ไคลเอ็นต์ของ Google ในหน้าเว็บ คุณจะต้องดําเนินการตามขั้นตอนต่อไปนี้
- ให้สิทธิ์ผู้ใช้
- หากได้รับอนุญาต ให้โหลด BigQuery API
- โหลดแล้วเริ่มต้นแผนที่
ดูตัวอย่างว่าหน้า HTML ที่เสร็จสมบูรณ์จะเป็นอย่างไรจาก step3/map.html
ให้สิทธิ์ผู้ใช้
ผู้ใช้ปลายทางของแอปพลิเคชันต้องให้สิทธิ์แอปพลิเคชันเพื่อเข้าถึงข้อมูลใน BigQuery ในนามของตนเอง Google Client API สําหรับ JavaScript จะจัดการตรรกะ OAuth ในการดําเนินการดังกล่าว
ในแอปพลิเคชันในชีวิตจริง คุณมีตัวเลือกมากมายเกี่ยวกับวิธีผสานรวมขั้นตอนการให้สิทธิ์
ตัวอย่างเช่น คุณสามารถเรียก authorize()
จากองค์ประกอบ UI อย่างปุ่ม หรือจะดําเนินการเมื่อโหลดหน้าเว็บแล้ว ในที่นี้เราได้เลือกให้สิทธิ์ผู้ใช้หลังจากโหลด 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 เมื่อให้สิทธิ์
หลังจากที่ผู้ใช้ได้รับสิทธิ์แล้ว ให้โหลด BigQuery API
ขั้นแรก ให้เรียกใช้ gapi.auth.authorize()
ด้วยตัวแปร clientId
ที่คุณเพิ่มในขั้นตอนก่อนหน้า จัดการการตอบสนองในฟังก์ชันเรียกกลับที่เรียกว่า handleAuthResult
พารามิเตอร์ immediate
จะควบคุมว่าจะให้ป๊อปอัปแสดงให้ผู้ใช้เห็นหรือไม่ ตั้งค่าเป็น true
เพื่อระงับป๊อปอัปการให้สิทธิ์ หากผู้ใช้ให้สิทธิ์แล้ว
เพิ่มฟังก์ชันลงในหน้าเว็บชื่อ handleAuthResult()
ฟังก์ชันการทํางานจะต้องใช้พารามิเตอร์ authresult
ซึ่งจะช่วยให้คุณควบคุมโฟลว์ของตรรกะได้ ขึ้นอยู่กับว่าผู้ใช้ได้รับอนุญาตเรียบร้อยแล้วหรือไม่
และเพิ่มฟังก์ชันชื่อ loadApi
เพื่อโหลด BigQuery API หากผู้ใช้ได้รับสิทธิ์เรียบร้อยแล้ว
เพิ่มตรรกะในฟังก์ชัน handleAuthResult()
เพื่อเรียก loadApi()
หากมีการส่งออบเจ็กต์ authResult
ไปยังฟังก์ชัน และหากพร็อพเพอร์ตี้ 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 ของ Maps API
ซึ่งทําได้โดยเรียกฟังก์ชัน 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
การส่งคําขอ
เพิ่มฟังก์ชัน JavaScript ใน work/index.html
เพื่อส่งคําขอโดยใช้ 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));
}
ได้รับผลการค้นหา
หากต้องการดูผลลัพธ์การค้นหาเมื่อทํางานเสร็จสิ้น ให้ใช้การเรียก API ของ 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 ได้ 3 วิธี ดังนี้
- เลือกตามสี่เหลี่ยมผืนผ้า (หรือที่เรียกว่าช่องล้อมรอบ)
- เลือกตามรัศมี และ
- ฟีเจอร์ฟังก์ชันที่กําหนดโดยผู้ใช้ที่มีประสิทธิภาพ
ตัวอย่างของการค้นหาขอบเขตและรัศมีในส่วนฟังก์ชันทางคณิตศาสตร์ของการอ้างอิง SQL เดิมของ BigQuery ในส่วน "ตัวอย่างขั้นสูง'
สําหรับการค้นหาขอบเขตและขอบเขตขอบเขต คุณสามารถเรียกใช้เมธอด 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
มีตารางการเดินทางของ NYC Taxi ให้บริการมากมาย ดูได้ที่คอนโซล BigQuery ของ 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
ด้วยรหัสโปรเจ็กต์ของคุณเอง (รหัสที่คุณสร้างใน "การสร้างบัญชี\ut; ก่อนหน้านี้ใน Codelab)
let billingProjectId = 'YOUR_PROJECT_ID';
let publicProjectId = 'bigquery-public-data';
let datasetId = 'new_york_taxi_trips';
let tableName = 'tlc_yellow_trips_2016';
ต่อไปให้เพิ่มฟังก์ชัน 2 รายการลงในโค้ดเพื่อสร้าง SQL และส่งคําถามไปยัง BigQuery โดยใช้ฟังก์ชัน sendQuery
ที่สร้างไว้ในขั้นตอนก่อนหน้า
ฟังก์ชันแรกควรมีชื่อว่า rectangleSQL()
และจะต้องยอมรับอาร์กิวเมนต์ 2 คู่ ซึ่งเป็นออบเจ็กต์ google.Maps.LatLng
คู่ที่แสดงถึงมุมของสี่เหลี่ยมผืนผ้าในพิกัดแผนที่
ฟังก์ชันที่ 2 ควรมีชื่อว่า 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
หากต้องการจํากัดจํานวนข้อมูลที่โอนไปยังหน้าเว็บของคุณให้มีขนาดคงที่สําหรับ Codelab นี้ ให้แก้ไขฟังก์ชัน 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;
}
คุณใช้ภาพแผนที่ความหนาแน่นเพื่อแสดงความหนาแน่นของตําแหน่งได้ Maps JavaScript API มีคลาส HeatmapLayer เพื่อวัตถุประสงค์นี้ HeatmapLayer ต้องใช้พิกัดละติจูดและลองจิจูดเพื่อให้แปลงแถวที่แสดงจากการค้นหาเป็นแผนที่ความร้อนได้ค่อนข้างง่าย
ในฟังก์ชัน getQueryResults
ให้ส่งอาร์เรย์ response.result.rows
ไปยังฟังก์ชัน JavaScript ใหม่ที่เรียกว่า doHeatMap()
ซึ่งจะสร้างฮีตแมป
แต่ละแถวจะมีพร็อพเพอร์ตี้ชื่อ f
ซึ่งเป็นอาร์เรย์ของคอลัมน์ แต่ละคอลัมน์จะมีพร็อพเพอร์ตี้ v
ที่มีค่า
โค้ดต้องวนซ้ําคอลัมน์ในแต่ละแถวและแยกค่าออกมา
ในการค้นหา SQL คุณได้ถามเฉพาะค่าละติจูดและลองจิจูดของการรับสินค้ารถแท็กซี่ เพื่อให้มีเพียง 2 คอลัมน์ในการตอบกลับเท่านั้น
อย่าลืมเรียกใช้ 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
- วาดสี่เหลี่ยมผืนผ้าที่อื่นในนิวยอร์ก
- ดูผลลัพธ์การค้นหาที่แสดงเป็นแผนที่ความร้อน
ต่อไปนี้คือตัวอย่างผลการค้นหาจากคําค้นหาสี่เหลี่ยมผืนผ้าเทียบกับข้อมูล NYC Yellow Taxi 2016 ที่วาดเป็นแผนที่ความหนาแน่น ส่วนนี้แสดงการมารับสินค้ารอบอาคารเอ็มไพร์สเตทในวันเสาร์ในเดือนกรกฎาคม
11 การค้นหาตามรัศมีรอบๆ จุด
การค้นหารัศมีใกล้เคียงกันมาก เมื่อใช้ฟังก์ชันคณิตศาสตร์ของ SQL เดิมของ BigQuery คุณจะสร้างการค้นหา SQL ได้โดยใช้สูตรฮาร์ซีนซึ่งจะประมาณพื้นที่วงกลมบนพื้นผิวโลก
คุณสามารถใช้เทคนิค OverlayComplete
เพื่อให้ได้กึ่งกลางและรัศมีของวงกลมที่ผู้ใช้วาด และสร้าง SQL สําหรับการค้นหาด้วยวิธีเดียวกันโดยใช้เทคนิคเดียวกันสําหรับสี่เหลี่ยมผืนผ้า
ตัวอย่างโค้ดที่ใช้งานได้สําหรับขั้นตอนนี้จะรวมอยู่ในที่เก็บโค้ดเป็น step6/map.html
drawingManager.addListener('circlecomplete', circle => circleQuery(circle));
ในสําเนาของ index.html ให้เพิ่มฟังก์ชันว่าง 2 ฟังก์ชันใหม่ ได้แก่ 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;
}
ลองใช้งาน
เพิ่มโค้ดด้านบนและลองใช้เครื่องมือ "วงกลม' เพื่อเลือกพื้นที่ในแผนที่ ผลลัพธ์ที่ได้ควรมีลักษณะดังนี้
12 การค้นหารูปร่างที่กําหนดเอง
สรุป: SQL ไม่รองรับการค้นหาโดยใช้รูปทรงที่กําหนดเองที่ไม่ใช่รูปสี่เหลี่ยมผืนผ้าและวงกลม BigQuery ไม่มีประเภทข้อมูลรูปทรงเรขาคณิตเนทีฟ ดังนั้นหากต้องการเรียกใช้การค้นหาโดยใช้รูปร่างรูปหลายเหลี่ยม คุณจะต้องมีวิธีที่แตกต่างจากการค้นหา SQL อย่างตรงไปตรงมา
ฟีเจอร์ BigQuery ที่ทรงพลังซึ่งสามารถใช้เป็นฟังก์ชันที่กําหนดโดยผู้ใช้ (UDF) ได้ UDF จะเรียกใช้โค้ด JavaScript ภายในคําค้นหา SQL
โค้ดการทํางานสําหรับขั้นตอนนี้อยู่ใน step7/map.html
UDF ใน 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;
}
การดําเนินการ 2 อย่างที่นี่ ในตอนแรกโค้ดจะสร้างคําสั่ง CREATE TEMPORARY FUNCTION
ที่สรุปโค้ด JavaScript ให้ทํางานได้หากจุดนั้นๆ อยู่ในรูปหลายเหลี่ยม ระบบจะแทรกพิกัดรูปหลายเหลี่ยมโดยใช้การเรียกใช้เมธอด JSON.stringify(poly)
เพื่อแปลงอาร์เรย์ JavaScript ของพิกัด x,y เป็นสตริง ระบบจะส่งออบเจ็กต์รูปหลายเหลี่ยมเป็นอาร์กิวเมนต์ไปยังฟังก์ชันที่สร้าง SQL
อันดับที่ 2 โค้ดจะสร้างคําสั่ง SELECT
ของ SQL หลัก มีการเรียก UDF ในนิพจน์ WHERE
ในตัวอย่างนี้
ผสานรวมกับ Maps API
ในการใช้ส่วนนี้กับไลบรารีภาพวาดของ Maps 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
จะสร้างฟังก์ชัน UDF JavaScript เป็นสตริง รวมถึงคําสั่ง SQL ซึ่งจะเรียกใช้ฟังก์ชัน UDF
โปรดดู example7 ที่ทํางานด้วยตัวอย่างนี้ในstep7/map.html
ตัวอย่างเอาต์พุต
นี่คือตัวอย่างผลของการค้นหาการรับสินค้าจากข้อมูลแท็กซี่สีเหลืองในนิวยอร์กปี 2016 ใน BigQuery โดยใช้รูปหลายเหลี่ยมแบบแฮนด์ฟรี ซึ่งข้อมูลที่เลือกได้วาดเป็นแผนที่ความหนาแน่น
13 ยกระดับไปอีกขั้น
ต่อไปนี้คือคําแนะนําเกี่ยวกับวิธีขยาย Codelab นี้เพื่อดูข้อมูลแง่มุมอื่นๆ ดูตัวอย่างแนวคิดเหล่านี้ที่ใช้งานได้ที่ 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 คุณจะเห็นกลุ่มข้อมูลขนาดใหญ่ (กลุ่มสีแดง) ของจุดหมายใจกลางเมืองโดยเฉพาะรอบๆ ไทม์สแควร์ รวมถึงถนน 5th Avenue ระหว่างถนน 23rd St และ 14th St สถานที่ที่ความหนาแน่นสูงอื่นๆ ไม่ได้แสดงในระดับการซูมนี้ ได้แก่ สนามบิน La Guardia และ JFK, ตึก World Trade Center และสวนแบตเตอรี่
การจัดรูปแบบแผนที่ฐาน
เมื่อสร้าง Google Maps โดยใช้ Maps JavaScript API คุณจะตั้งค่ารูปแบบแผนที่ได้โดยใช้ออบเจ็กต์ JSON การแสดงภาพข้อมูลจะเป็นประโยชน์ในการปิดเสียงสีบนแผนที่ คุณสร้างและลองใช้รูปแบบแผนที่ได้โดยใช้วิซาร์ด Google Maps API Styling ที่ 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 มักจะให้คําตอบเป็นวินาที แต่ในบางครั้งการแสดงให้ผู้ใช้เห็นข้อมูลว่าคําค้นหากําลังทํางานอยู่
เพิ่ม UI ลงในหน้าเว็บที่แสดงการตอบกลับของฟังก์ชัน 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);
})
}
หน้าเว็บควรมีลักษณะดังนี้
ดูตัวอย่างที่สมบูรณ์ใน step8/map.html
14 สิ่งที่ควรพิจารณา
มีเครื่องหมายมากเกินไป
หากทํางานกับตารางขนาดใหญ่มาก คําค้นหาอาจแสดงแถวจํานวนมากเกินไปที่จะแสดงบนแผนที่ได้อย่างมีประสิทธิภาพ จํากัดผลลัพธ์โดยเพิ่มวลี WHERE
หรือคําสั่ง LIMIT
การวาดเครื่องหมายจํานวนมากอาจทําให้แผนที่อ่านไม่ได้ ลองใช้ HeatmapLayer
เพื่อแสดงความหนาแน่น หรือตัวทําเครื่องหมายคลัสเตอร์เพื่อระบุตําแหน่งของจุดข้อมูลต่างๆ ที่ใช้สัญลักษณ์เดียวต่อคลัสเตอร์ ดูรายละเอียดเพิ่มเติมได้ในบทแนะนําคลัสเตอร์มาร์กเกอร์
การเพิ่มประสิทธิภาพคําค้นหา
BigQuery จะสแกนทั้งตารางกับคําค้นหาทั้งหมด หากต้องการเพิ่มประสิทธิภาพการใช้งานโควต้า BigQuery ให้เลือกคอลัมน์ที่ต้องการในการค้นหาเท่านั้น
การค้นหาจะเร็วขึ้นหากคุณจัดเก็บละติจูดและลองจิจูดเป็นแบบลอยแทนที่จะเป็นสตริง
ส่งออกผลลัพธ์ที่น่าสนใจ
ตัวอย่างต่อไปนี้กําหนดให้ผู้ใช้ปลายทางต้องผ่านการตรวจสอบสิทธิ์กับตาราง BigQuery ซึ่งจะไม่เหมาะกับกรณีการใช้งานทั้งหมด เมื่อคุณพบรูปแบบที่น่าสนใจบางอย่าง การแชร์รูปแบบดังกล่าวกับกลุ่มเป้าหมายที่กว้างขึ้นจะง่ายขึ้นโดยการส่งออกผลลัพธ์จาก BigQuery และสร้างชุดข้อมูลแบบคงที่โดยใช้ชั้นข้อมูล Google Maps
การขัดกันทางกฎหมาย
โปรดทราบว่าข้อกําหนดในการให้บริการของ Google Maps Platform ดูรายละเอียดเพิ่มเติมเกี่ยวกับราคาของ Google Maps Platform ได้ที่เอกสารประกอบออนไลน์
สนุกไปกับข้อมูลที่มากขึ้น
มีชุดข้อมูลสาธารณะจํานวนหนึ่งใน BigQuery ที่มีคอลัมน์ละติจูดและลองจิจูด เช่น ชุดข้อมูลแท็กซี่นิวยอร์กจากปี 2009-2016, ข้อมูลการเดินทางใน Uber และ Lyft นิวยอร์ก และชุดข้อมูล GDELT
15 ยินดีด้วย
เราหวังว่าข้อมูลนี้จะช่วยให้คุณเริ่มต้นทํางานได้อย่างรวดเร็วด้วยการถามคําถามตามภูมิศาสตร์กับตาราง BigQuery เพื่อให้คุณค้นพบรูปแบบและแสดงข้อมูลเป็นภาพใน Google Maps ขอให้มีความสุขกับการแมป
มีอะไรอีกบ้าง
หากต้องการดูข้อมูลเพิ่มเติมเกี่ยวกับ Google Maps Platform หรือ BigQuery โปรดดูคําแนะนําต่อไปนี้
ไปที่ BigQuery คืออะไร เพื่อดูข้อมูลเพิ่มเติมเกี่ยวกับบริการคลังข้อมูลแบบ Peaไบต์ ที่ไม่ต้องใช้เซิร์ฟเวอร์ที่มีปริมาณเพตะไบต์
โปรดดูคําแนะนําวิธีสร้างแอปพลิเคชันแบบง่ายโดยใช้ BigQuery API
ดูรายละเอียดเพิ่มเติมเกี่ยวกับการเปิดใช้การโต้ตอบของผู้ใช้เพื่อวาดรูปร่างบน Google Maps ได้ในคู่มือนักพัฒนาซอฟต์แวร์สําหรับไลบรารีภาพวาด
ดูวิธีอื่นๆ ในการแสดงภาพข้อมูลใน Google Maps
ดูคู่มือเริ่มต้นใช้งานไคลเอ็นต์ API ของ JavaScript เพื่อทําความเข้าใจแนวคิดพื้นฐานในการใช้ API ไคลเอ็นต์เพื่อเข้าถึง Google API อื่นๆ