Google Maps Platform (자바스크립트)을 사용하여 BigQuery에서 위치 데이터 쿼리 및 시각화

1. 개요

지도는 어떤 방식으로 위치와 관련된 데이터 세트의 패턴을 시각화할 때 매우 유용한 도구가 될 수 있습니다. 이 관계는 장소의 이름, 특정 위도/경도 값 또는 인구 조사 표준 구역과 우편번호처럼 특정 경계가 있는 지역의 이름일 수 있습니다.

이러한 데이터 세트가 너무 커지면 기존 도구를 사용하여 쿼리하고 시각화하기가 어려울 수 있습니다. Google BigQuery를 사용하여 데이터를 쿼리하고 Google 지도 API를 사용하여 쿼리를 구성하고 출력을 시각화하면 매우 큰 데이터 세트를 저장하기 위해 시스템을 관리할 필요 없이 설정이나 코딩을 거치지 않고도 데이터의 지리적 패턴을 빠르게 탐색할 수 있습니다.

빌드할 항목

이 Codelab에서는 BigQuery를 사용하여 매우 큰 공개 데이터 세트에 위치 기반 통계를 제공하는 방법을 보여주는 쿼리를 작성하고 실행합니다. 또한 Google Maps Platform JavaScript API를 사용하여 지도를 로드하는 웹페이지를 빌드한 다음 자바스크립트용 Google API 클라이언트 라이브러리BigQuery API를 사용하여 동일한 대형 공개 데이터 세트에 대한 공간 쿼리를 실행하고 시각화합니다.

학습할 내용

  • SQL 쿼리, 사용자 정의 함수, BigQuery API를 사용하여 BigQuery로 페타바이트급 위치 데이터 세트를 몇 초 만에 쿼리하는 방법
  • Google Maps Platform을 사용하여 웹페이지에 Google 지도를 추가하고 사용자가 그 위에 도형을 그릴 수 있도록 하는 방법
  • 아래 예시 이미지와 같이 Google 지도에서 대규모 데이터 세트에 대한 쿼리를 시각화하는 방법: 엠파이어 스테이트 빌딩 주변 블록에서 시작된 2016년 택시 하차 위치의 밀도를 보여줍니다.

Screen Shot 2017-05-09 11:01.12 AM.png

필요한 항목

  • HTML, CSS, 자바스크립트, SQL, Chrome DevTools 관련 기본 지식
  • 최신 버전의 Chrome, Firefox, Safari, Edge와 같은 최신 웹브라우저
  • 원하는 텍스트 편집기 또는 IDE

기술

BigQuery

BigQuery는 대규모 데이터 세트를 위한 데이터 분석 서비스입니다. RESTful API를 제공하며 SQL로 작성된 쿼리를 지원합니다. 위도와 경도 값이 있는 데이터가 있는 경우 데이터를 사용하여 위치별로 데이터를 쿼리할 수 있습니다. 서버 또는 데이터베이스 인프라 관리 없이도 매우 큰 데이터 세트를 시각적으로 탐색하여 패턴을 살펴볼 수 있다는 장점이 있습니다. BigQuery의 대규모 확장성과 관리형 인프라를 통해 테이블 규모가 커지더라도 몇 초 만에 답을 얻을 수 있습니다.

Google Maps Platform

Google Maps Platform을 사용하면 Google의 지도, 장소, 경로 데이터에 프로그래매틱 방식으로 액세스할 수 있습니다. 현재 200만 개가 넘는 웹사이트와 앱에서 이를 사용하여 사용자에게 삽입된 지도 및 위치 기반 쿼리를 제공합니다.

Google Maps Platform JavaScript API 그리기 레이어를 사용하여 지도에 도형을 그릴 수 있습니다. 위도 및 경도 값이 열에 저장된 BigQuery 테이블에 대해 쿼리를 실행하기 위해 입력을 변환할 수 있습니다.

시작하려면 BigQuery 및 Maps API가 사용 설정된 Google Cloud Platform 프로젝트가 필요합니다.

2. 설정하기

Google 계정

아직 Google 계정(Gmail 또는 Google Apps)이 없으면 계정을 만들어야 합니다.

프로젝트 만들기

Google Cloud Platform Console(console.cloud.google.com)에 로그인하여 새 프로젝트를 만듭니다. 화면 상단에는 프로젝트 드롭다운 메뉴가 있습니다.

f2a353c3301dc649.png

이 프로젝트 드롭다운 메뉴를 클릭하면 새 프로젝트를 만들 수 있는 메뉴 항목이 표시됩니다.

56a42dfa7ac27a35.png

'프로젝트 이름 새 이름 입력'에서 새 프로젝트의 이름을 입력합니다. 예: 'BigQuery Codelab"

Codelab - 프로젝트 (1).png 만들기

프로젝트 ID가 생성됩니다. 프로젝트 ID는 모든 Google Cloud 프로젝트에서 고유한 이름입니다. 나중에 사용할 때 프로젝트 ID를 기억합니다. 위의 이름은 이미 사용 중이며 사용할 수 없습니다. 이 Codelab에서 YOUR_PROJECT_ID가 표시될 때마다 프로젝트 ID를 삽입하세요.

결제 사용 설정

BigQuery에 가입하려면 이전 단계에서 선택했거나 만든 프로젝트를 사용하세요. 이 프로젝트에서 결제를 사용 설정해야 합니다. 결제가 사용 설정되면 BigQuery API를 사용 설정할 수 있습니다.

결제를 사용 설정하는 방법은 새 프로젝트를 만드는지 아니면 기존 프로젝트에 결제를 다시 사용 설정했는지에 따라 다릅니다.

Google에서는 이 Codelab에서 사용할 수 있는 최대 $300의 Google Cloud Platform 사용량에 대해 12개월 무료 체험판을 제공합니다. 자세한 내용은 https://cloud.google.com/free/에서 확인하세요.

새 프로젝트

새 프로젝트를 만들면 프로젝트에 연결할 결제 계정을 선택하라는 메시지가 표시됩니다. 결제 계정이 하나만 있는 경우 해당 계정이 프로젝트에 자동으로 연결됩니다.

결제 계정이 없는 경우 여러 개의 Google Cloud Platform 기능을 사용하려면 먼저 결제 계정을 만들고 프로젝트에 결제를 사용 설정해야 합니다. 새 결제 계정을 만들고 프로젝트에 결제를 사용 설정하려면 새 결제 계정 만들기의 안내를 따르세요.

기존 프로젝트

프로젝트가 일시적으로 결제를 사용 중지한 경우 결제를 다시 사용 설정할 수 있습니다.

  1. Cloud Platform Console로 이동합니다.
  2. 프로젝트 목록에서 결제를 다시 사용 설정하려는 프로젝트를 선택합니다.
  3. Console 왼쪽 메뉴를 열고 결제 청구를 선택합니다. 결제 계정을 선택하라는 메시지가 표시됩니다.
  4. 계정 설정을 클릭합니다.

새 결제 계정 만들기

새 결제 계정을 만드는 방법은 다음과 같습니다.

  1. Cloud Platform Console로 이동하여 로그인하거나 아직 계정이 없는 경우 가입합니다.
  2. Console 왼쪽 메뉴를 열고 결제 청구를 선택합니다.
  3. 새 결제 계정 버튼을 클릭합니다. (첫 번째 결제 계정이 아닌 경우 먼저 페이지 상단에 있는 기존 결제 계정의 이름을 클릭한 다음 결제 계정 관리를 클릭하여 결제 계정 목록을 엽니다.)
  4. 결제 계정의 이름과 결제 정보를 입력합니다. 표시되는 옵션은 청구서 수신 주소의 국가에 따라 다릅니다. 미국 계정의 경우 계정을 만든 후에 납세자 유형을 변경할 수 없습니다.
  5. 제출 및 결제 사용 설정을 클릭합니다.

기본적으로 결제 계정을 만든 사람이 계정의 결제 관리자입니다.

은행 계좌 확인 및 백업 결제 수단 추가에 대한 자세한 내용은 결제 수단 추가, 삭제, 업데이트하기를 참고하세요.

BigQuery API 사용 설정

프로젝트에서 BigQuery API를 사용 설정하려면 Console에서 BigQuery API 페이지 Marketplace로 이동하여 파란색 '사용 설정' 버튼을 클릭합니다.

3. BigQuery에서 위치 데이터 쿼리

BigQuery에서 위도, 경도 값으로 저장된 위치 데이터를 쿼리하는 방법은 세 가지가 있습니다.

  • 직사각형 쿼리: 최소 및 최대 위도와 경도 범위 내의 모든 행을 선택하는 쿼리로 관심 영역을 지정합니다.
  • 반경 쿼리: 지구의 모양을 모델링하기 위해 해저 수식과 수학 함수를 사용하여 특정 점 주변의 원을 계산하여 관심 지역을 지정합니다.
  • 다각형 쿼리: 맞춤 도형을 지정하고 사용자 정의 함수를 사용하여 각 행의 위도와 경도가 셰이프 내에 속하는지 테스트하는 데 필요한 다각형 내 로직을 표시합니다.

시작하려면 Google Cloud Platform Console의 BigQuery 섹션에서 쿼리 편집기를 사용하여 NYC 택시 데이터에 대해 다음 쿼리를 실행하세요.

표준 SQL과 Legacy SQL 비교

BigQuery는 두 가지 버전의 SQL, 즉 Legacy SQL표준 SQL을 지원합니다. 후자는 2011 ANSI 표준입니다. 이 가이드에서는 더 나은 표준 준수를 위해 표준 SQL을 사용합니다.

BigQuery 편집기에서 Legacy SQL을 실행하려면 다음 단계를 따르세요.

  1. '더보기' 버튼을 클릭합니다.
  2. 드롭다운 메뉴에서 '쿼리 설정'을 선택합니다.
  3. 'SQL 언어'에서 '기존' 라디오 버튼을 선택합니다.
  4. '저장' 버튼을 클릭합니다.

직사각형 쿼리

직사각형 쿼리는 BigQuery에서 매우 간단합니다. 위도 및 경도의 최솟값과 최댓값 사이의 위치가 반환되는 결과를 반환하는 WHERE 절을 추가하기만 하면 됩니다.

BigQuery 콘솔에서 아래 예를 참고하세요. 이 쿼리는 미드타운과 낮은 맨해튼이 포함된 직사각형 지역에서 시작된 놀이기구의 평균 이동 통계 몇 가지를 쿼리합니다. 이렇게 할 수 있는 위치는 두 군데, 두 번째 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

두 쿼리의 결과는 두 위치에서 수령하는 평균 이동 거리, 요금, 팁에 큰 차이가 있음을 나타냅니다.

맨해튼

avg_tip

평균_요금

avg_distance

avg_tip_pc

avg_fee_mile

2.52

12월 3일

9.97

22.39달러

5.97

JFK

avg_tip

평균_요금

avg_distance

avg_tip_pc

avg_fee_mile

9.22

48.49달러

41.19

22.48

4.36

반경 쿼리

또한 반경을 잘 알고 있다면 SQL에서 쉽게 쿼리할 수 있습니다. BigQuery의 Legacy SQL 수학 함수를 사용하면 지표면의 원형 영역 또는 구면 근사에 가까운 haversine 수식을 사용하여 SQL 쿼리를 구성할 수 있습니다.

다음은 반경이 0.1km인 40.73943, -73.99585를 중심으로 하는 원 쿼리의 BigQuery SQL 문 예시입니다.

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 

Hiversine Formula의 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_distance

avg_tip_pc

avg_fee_mile

1.17

11월 8일

45,280

1,030원

6,420

브롱스

avg_tip

평균_요금

avg_distance

avg_tip_pc

avg_fee_mile

0.52

17,630

7.75

7.74

10.9

다각형 쿼리

SQL은 직사각형과 원 이외의 임의의 도형을 사용하여 쿼리를 지원하지 않습니다. BigQuery에는 네이티브 도형 데이터 유형 또는 공간 색인이 없으므로 다각형을 사용하여 쿼리를 실행하려면 간단한 SQL 쿼리에 다른 접근 방식이 필요합니다. 한 가지 접근 방식은 자바스크립트로 도형 함수를 정의하고 이를 BigQuery에서 사용자 정의 함수 (UDF)로 실행하는 것입니다.

많은 도형 작업을 자바스크립트로 작성할 수 있으므로 위도 및 경도 값이 포함된 BigQuery 테이블에 대해 손쉽게 가져와서 실행할 수 있습니다. UDF를 통해 맞춤 다각형을 전달하고 각 행에 대해 테스트를 실행하여 위도와 경도가 다각형 내에 있는 행만 반환해야 합니다. BigQuery 참조에서 UDF에 대해 자세히 알아보세요.

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;
}

자바스크립트로 포팅

이 알고리즘의 자바스크립트 버전은 다음과 같습니다.

/* 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;
}

BigQuery에서 표준 SQL을 사용할 때 UDF 접근 방식은 단일 구문만 필요하지만 UDF는 문에서 임시 함수로 정의해야 합니다. 예를 들면 다음과 같습니다. 아래의 SQL 문을 쿼리 편집기 창에 붙여넣습니다.

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

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

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

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

  return c;
""";

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

수고하셨습니다.

이제 BigQuery를 사용하여 세 가지 유형의 공간 쿼리를 실행했습니다. 보시다시피 위치는 이 데이터 세트에 대한 쿼리의 결과 데이터에 큰 차이를 만들지만 쿼리를 실행할 위치를 추측하지 않으면 SQL 쿼리만 사용하여 공간 패턴을 임시적으로 발견하기는 어렵습니다.

지도의 데이터를 시각화하고 임의의 관심분야 영역을 정의하여 데이터를 탐색할 수 있다면 어떨까요? Google 지도 API를 사용하면 됩니다. 먼저 Maps API를 사용 설정하고 로컬 머신에서 실행되는 간단한 웹페이지를 설정한 다음 BigQuery API를 사용하여 웹페이지에서 쿼리를 전송해야 합니다.

4. Google 지도 API 작업

간단한 공간 쿼리를 실행하면 다음 단계로 출력을 시각화하여 패턴을 확인합니다. 이를 위해 지도 API를 사용 설정하여 지도에서 BigQuery로 쿼리를 전송하는 웹페이지를 만든 다음 지도에 결과를 그립니다.

Maps JavaScript API 사용 설정

이 Codelab에서는 프로젝트에서 Google Maps Platform의 Maps JavaScript API를 사용 설정해야 합니다. 이 작업을 수행하려면 다음과 같이 하세요.

  1. Google Cloud Platform Console에서 Marketplace로 이동합니다.
  2. 마켓플레이스에서 'Maps JavaScript API'를 검색합니다.
  3. 검색결과에서 Maps JavaScript API 타일을 클릭합니다.
  4. '사용' 버튼 클릭

API 키 생성

Google Maps Platform에 요청하려면 API 키를 생성하여 모든 요청과 함께 전송해야 합니다. API 키를 생성하려면 다음 단계를 따르세요.

  1. Google Cloud Platform Console에서 왼쪽 햄버거 메뉴를 클릭하여 왼쪽 탐색 메뉴를 엽니다.
  2. 'API & Service' > _Credentials'를 선택하세요.
  3. '사용자 인증 정보 만들기' 버튼을 클릭하고 'API 키'를 선택합니다.
  4. 새 API 키 복사

코드 다운로드 및 웹 서버 설정

다음 버튼을 클릭하여 이 Codelab의 모든 코드를 다운로드합니다.

다운로드한 ZIP 파일의 압축을 해제합니다. 그러면 이 Codelab의 각 단계에 필요한 폴더 1개와 필요한 모든 리소스가 포함된 루트 폴더 (bigquery)가 압축 해제됩니다.

stepN 폴더에는 이 Codelab의 각 단계에 원하는 최종 상태가 포함되어 있습니다. 참고용으로 제공됩니다. work라는 디렉터리에서 모든 코딩 작업을 진행합니다.

로컬 웹 서버 설정하기

자체 웹 서버를 자유롭게 사용할 수 있지만 이 Codelab은 Chrome 웹 서버에서 잘 작동하도록 설계되었습니다. 아직 앱이 설치되어 있지 않다면 Chrome 웹 스토어에서 설치할 수 있습니다.

설치가 완료되면 앱을 엽니다. Chrome에서 다음과 같이 할 수 있습니다.

  1. Chrome을 엽니다.
  2. 상단의 주소 표시줄에 chrome://apps를 입력합니다.
  3. Enter 키를 누르세요.
  4. 창이 열리면 웹 서버 아이콘을 클릭합니다. 앱을 마우스 오른쪽 버튼으로 클릭하여 일반 탭이나 고정된 탭, 전체 화면 또는 새 창에서 열 수도 있습니다. A3ed00e79b8bfee7.png You'다음 대화상자가 표시되면 로컬 웹 서버를 구성할 수 있습니다. 81b6151c3f60c948.png
  5. 'CHOOSE Folder''를 클릭하고 Codelab 샘플 파일을 다운로드한 위치를 선택합니다.
  6. '옵션'' 섹션에서 'index.html 자동 표시' 옆의 체크박스를 선택합니다. 17f4913500faa86f.png
  7. 라벨이 'Web Server: STARTED'로 라벨이 지정된 전환 버튼을 왼쪽으로 슬라이드한 다음 오른쪽으로 슬라이드하여 중지 후 웹 서버를 다시 시작합니다.

A5d554d0d4a91851.png

5. 지도 및 그리기 도구 로드하기

기본 지도 페이지 만들기

Maps JavaScript API와 몇 줄의 자바스크립트를 사용하여 Google 지도를 로드하는 간단한 HTML 페이지로 시작하세요. Google Maps Platform의 간단한 지도 샘플부터 시작해 보세요. 선택한 텍스트 편집기 또는 IDE를 복사하여 붙여넣기하여 붙여넣을 수 있으며, 다운로드한 저장소에서 index.html를 열어 확인할 수도 있습니다.

  1. index.html를 저장소의 로컬 사본에 있는 work 폴더에 복사합니다.
  2. img/ 폴더를 저장소의 로컬 사본에 있는 작업/ 폴더에 복사합니다.
  3. 텍스트 편집기 또는 IDE에서 작업/index.html 열기
  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 키가 올바른지, 로컬 웹 서버가 활성 상태인지 확인하세요.

기본 위치 및 확대/축소 수준 변경하기

위치와 확대/축소 수준을 설정하는 코드는 index.html의 27번 줄과 28번째 줄에 있으며, 현재 오스트레일리아 시드니를 중심으로 하고 있습니다.

<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>

다음으로, 브라우저에서 지도를 새로고침하여 결과를 확인합니다.

그리기 및 시각화 라이브러리 로드

지도에 그리기 기능을 추가하려면 Google Maps Platform에 그리기 라이브러리를 사용 설정하도록 지시하는 선택적 매개변수를 추가하여 Maps JavaScript API를 로드하는 스크립트를 변경합니다.

이 Codelab에서는 HeatmapLayer도 사용하므로 시각화 라이브러리를 요청하는 스크립트도 업데이트합니다. 이렇게 하려면 libraries 매개변수를 추가하고 visualizationdrawing 라이브러리를 쉼표로 구분된 값으로 지정합니다(예: libraries=visualization,drawing).

다음과 같이 나타납니다.

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

DrawingManager 추가

사용자가 그린 도형을 쿼리의 입력으로 사용하려면 Circle, Rectangle, Polygon 도구가 사용 설정된 상태로 DrawingManager를 지도에 추가합니다.

모든 DrawingManager 설정 코드를 새 함수에 넣는 것이 좋습니다. 따라서 index.html 사본에서 다음을 실행하세요.

  1. 다음 코드를 사용하여 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);
}
  1. 지도 객체가 생성된 후 initMap() 함수에서 setUpDrawingTools() 호출
function initMap() {
  map = new google.maps.Map(document.getElementById('map'), {
    center: {lat: 40.744593, lng: -73.990370}, // Manhattan, New York.
    zoom: 12
  });

  setUpDrawingTools();
}
  1. index.html을 새로고침하고 그리기 도구가 표시되는지 확인합니다. 또한 원, 직사각형, 다각형을 그릴 수도 있습니다.

클릭한 후 드래그하여 원과 직사각형을 그릴 수 있지만 각 꼭짓점을 클릭하고 다각형을 그려서 도형을 그려야 합니다.

그리기 이벤트 처리

그려진 도형의 좌표가 SQL 쿼리 작성처럼 필요할 때 사용자가 도형 그리기를 완료하면 실행되는 이벤트를 처리하기 위한 코드가 필요합니다.

이후 단계에서 이 코드를 추가할 예정이지만 지금은 rectanglecomplete, circlecomplete, polygoncomplete 이벤트를 처리하기 위해 빈 이벤트 핸들러 3개를 스텁 처리하겠습니다. 핸들러는 이 단계에서 코드를 실행할 필요가 없습니다.

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 클라이언트 API 사용

Google BigQuery Client API를 사용하면 요청을 빌드하고 응답을 파싱하며 인증을 처리하는 데 필요한 상용구 코드를 많이 작성하지 않아도 됩니다. 이 Codelab에서는 브라우저 기반 애플리케이션을 개발하기 때문에 자바스크립트용 Google API 클라이언트 라이브러리를 통해 BigQuery API를 사용합니다.

그런 다음 웹페이지에 이 API를 로드하고 BigQuery와 상호작용하는 데 사용하는 코드를 추가합니다.

자바스크립트용 Google 클라이언트 API 추가

자바스크립트용 Google 클라이언트 API를 사용하여 BigQuery에 대해 쿼리를 실행합니다. work 사본의 index.html 사본에서 다음과 같이 <script> 태그를 사용하여 API를 로드합니다. 태그를 Maps API를 로드하는 <script> 태그 바로 아래에 배치합니다.

<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의 탐색 메뉴에서 API 및 서비스 & 사용자 인증 정보를 선택합니다.

사용자 인증 정보를 설정하기 전에 애플리케이션 최종 사용자가 대신 BigQuery 데이터에 액세스하도록 승인할 때 사용자에게 표시되는 승인 화면에 몇 가지 구성을 추가해야 합니다.

이렇게 하려면 OAuth 동의 화면 탭을 클릭합니다. 2. Big Query API를 이 토큰의 범위에 추가해야 합니다. Google API의 범위 섹션에서 범위 추가 버튼을 클릭합니다. 3. 목록에서 ../auth/bigquery 범위와 함께 Big Query API 항목 옆에 있는 체크박스를 선택합니다. 4. 추가를 클릭합니다. 5. {0}애플리케이션 이름' 필드에 이름을 입력합니다. 6. 저장을 클릭하여 설정을 저장합니다. 7. 다음으로 OAuth 클라이언트 ID를 만듭니다. 이렇게 하려면 사용자 인증 정보 만들기를 클릭합니다.

4d18a965fc760e39.png

  1. 드롭다운 메뉴에서 OAuth 클라이언트 ID를 클릭합니다. 1f8b36a1c27c75f0.png
  2. 애플리케이션 유형에서 웹 애플리케이션을 선택합니다.
  3. 애플리케이션 이름 필드에 프로젝트 이름을 입력합니다. 예: 'BigQuery 및 지도'.
  4. 제한사항의 승인된 자바스크립트 원본 입력란에 포트 번호를 포함하여 localhost의 URL을 입력합니다. 예: http://localhost:8887
  1. 만들기 버튼을 클릭합니다.

클라이언트 ID와 클라이언트 비밀번호가 표시된 팝업이 표시됩니다. BigQuery에 대한 인증을 수행하려면 클라이언트 ID가 필요합니다. 복사한 후 work/index.htmlclientId라는 새 전역 자바스크립트 변수로 붙여넣습니다.

let clientId = 'YOUR_CLIENT_ID';

7. 승인 및 초기화

사용자가 지도를 초기화하기 전에 웹페이지에서 BigQuery에 액세스하도록 승인해야 합니다. 이 예시에서는 자바스크립트 클라이언트 API 문서의 승인 섹션에 설명된 대로 OAuth 2.0을 사용합니다. 쿼리를 보내려면 OAuth 클라이언트 ID와 프로젝트 ID를 사용해야 합니다.

Google Client API가 웹페이지에 로드되면 다음 단계를 수행해야 합니다.

  • 사용자를 승인합니다.
  • 승인되면 BigQuery API를 로드합니다.
  • 지도를 로드하고 초기화합니다.

완성된 HTML 페이지가 표시되는 방식의 예는 step3/map.html을 참고하세요.

사용자 승인

애플리케이션의 최종 사용자는 자신을 대신하여 BigQuery에서 데이터에 액세스하도록 애플리케이션을 승인해야 합니다. 자바스크립트용 Google 클라이언트 API는 이를 위해 OAuth 로직을 처리합니다.

실제 애플리케이션에서는 승인 단계를 통합하는 방법에 여러 가지 방법이 있습니다.

예를 들어 버튼과 같은 UI 요소에서 authorize()을 호출하거나 페이지가 로드될 때 호출할 수 있습니다. 여기서는 gapi.load() 메서드의 콜백 함수를 사용하여 자바스크립트용 Google Client API가 로드된 후 사용자를 승인하도록 선택했습니다.

자바스크립트 라이브러리와 Google 클라이언트 API를 로드하는 <script> 태그 바로 뒤에 코드를 작성하여 클라이언트 라이브러리와 인증 모듈을 모두 로드하여 사용자를 바로 인증할 수 있습니다.

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

승인 시 BigQuery API 로드

사용자가 승인되면 BigQuery API를 로드합니다.

먼저 이전 단계에서 추가한 clientId 변수를 사용하여 gapi.auth.authorize()를 호출합니다. handleAuthResult이라는 콜백 함수에서 응답을 처리합니다.

immediate 매개변수는 사용자에게 팝업을 표시할지 제어합니다. 사용자가 이미 승인된 경우 승인 팝업을 표시하지 않으려면 true로 설정합니다.

페이지에 handleAuthResult()라는 함수를 추가합니다. 함수는 authresult 매개변수를 사용해야 합니다. 이 매개변수를 사용하면 사용자의 승인 여부에 따라 로직의 흐름을 제어할 수 있습니다.

또한 사용자가 성공적으로 인증되면 BigQuery API를 로드하는 loadApi 함수도 추가합니다.

함수에 전달된 authResult 객체가 있고 객체의 error 속성에 false 값이 있는 경우 loadApi()를 호출하는 handleAuthResult() 함수에 로직을 추가합니다.

loadApi() 함수에 코드를 추가하여 gapi.client.load() 메서드를 사용하여 BigQuery API를 로드합니다.

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');
}

지도 로드하기

마지막 단계는 지도를 초기화하는 것입니다. 이를 위해 로직 순서를 약간 변경해야 합니다. 현재 Maps API 자바스크립트가 로드된 후 초기화됩니다.

이렇게 하려면 gapi.client 객체의 load() 메서드 뒤에 있는 then() 메서드에서 initMap() 함수를 호출하면 됩니다.

// 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에 추가하여 API를 사용하여 쿼리를 전송하고, 몇 가지 변수를 사용하여 쿼리할 테이블이 포함된 BigQuery 데이터 세트와 프로젝트의 값과 요금이 청구되는 프로젝트 ID를 저장합니다.

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

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

작업 상태 확인

아래의 checkJobStatus 함수는 get API 메서드와 원래 쿼리 요청에서 반환된 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 메서드를 수정하여 request.execute() 메서드를 콜백에서 checkJobStatus() 메서드를 콜백으로 호출합니다. 작업 ID를 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 호출을 사용합니다. 페이지에 jobId라는 매개변수를 허용하는 getQueryResults()라는 함수를 추가합니다.

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에서 데이터에 대해 공간 쿼리를 실행하는 방법은 세 가지가 있습니다.

  • 직사각형 (또는 경계 상자라고도 함)으로 선택
  • 반경 선택
  • 강력한 사용자 정의 함수 기능

BigQuery legacy SQL 참조의 수학 함수 섹션 중 고급 예에 있는 경계 상자 및 반경 쿼리의 예시가 있습니다.

경계 상자 및 반경 쿼리의 경우 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입니다. Legacy SQL에서 project.dataset.tablename입니다.

NYC 택시 여행에는 다양한 테이블이 있습니다. 이를 확인하려면 BigQuery 웹 콘솔로 이동하여 '공개 데이터 세트' 메뉴 항목을 펼치세요. new_york라는 데이터 세트를 찾아 펼쳐 테이블을 확인합니다. 노란색 택시 이동표(bigquery-public-data.new_york_taxi_trips.tlc_yellow_trips_2016)를 선택합니다.

프로젝트 ID 지정

API 호출에서 결제 목적으로 Google Cloud Platform 프로젝트의 이름을 지정해야 합니다. 이 Codelab에서는 테이블이 포함된 프로젝트와 동일한 프로젝트가 아닙니다. 데이터를 업로드하여 자신의 프로젝트에서 만든 테이블로 작업하고 있다면 이 프로젝트 ID는 SQL 문의 ID와 동일합니다.

자바스크립트 변수를 코드에 추가하여 쿼리하는 테이블과 테이블 이름, 데이터 세트 이름이 포함된 공개 데이터 세트 프로젝트에 대한 참조를 보유합니다. 자체 결제 프로젝트 ID를 참조하려면 별도의 변수도 필요합니다.

index.html 사본에 billingProjectId, publicProjectId, datasetIdtableName라는 전역 자바스크립트 변수를 추가합니다.

BigQuery 공개 데이터 세트 프로젝트의 세부정보를 사용하여 'publicProjectId', 'datasetId', 'tableName' 변수를 초기화합니다. 이 Codelab 앞부분에서 '설정하기'에서 만든 프로젝트 ID로 billingProjectId를 초기화합니다.

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

이제 코드에 두 함수를 추가하여 SQL을 생성하고 이전 단계에서 만든 sendQuery 함수를 사용하여 쿼리를 BigQuery로 전송합니다.

첫 번째 함수는 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에서 확인할 수 있습니다.

이 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 함수에서 히트맵을 만드는 새 자바스크립트 함수인 doHeatMap()response.result.rows 배열을 전달합니다.

각 행에는 열의 배열인 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 NYC 옐로우 택시 데이터에 대한 직사각형 쿼리 결과의 예입니다. 다음은 7월 토요일에 토요일에 엠파이어 스테이트 빌딩을 중심으로 픽업 배포 내역을 보여줍니다.

7b1face0e7c71c78.png

11. 점 주변의 반경으로 쿼리

반경 쿼리는 매우 유사합니다. BigQuery의 Legacy SQL 수학 함수를 사용하면 지표면의 원형 면적을 근사하는 해저 수식을 사용하여 SQL 쿼리를 구성할 수 있습니다.

직사각형에 동일한 기술을 사용하면 OverlayComplete 이벤트를 처리하여 사용자가 그린 원의 중심 및 반경을 가져오고 동일한 방식으로 쿼리의 SQL을 빌드할 수 있습니다.

이 단계의 코드의 실제 예는 코드 단계에 step6/map.html로 포함되어 있습니다.

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

index.html 사본에 새 함수 두 개(circleQuery()haversineSQL())를 추가합니다.

그런 다음 중심 및 반경을 circleQuery().이라는 새 함수에 전달하는 circlecomplete 이벤트 핸들러를 추가합니다.

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;
}

사용해 보기

위의 코드를 추가하고 '원'' 도구를 사용하여 지도 영역을 선택하세요. 다음과 같은 결과가 표시됩니다.

845418166b7cc7a3.png

12. 임의의 도형 쿼리

요약: SQL은 직사각형과 원 이외의 임의의 도형을 사용하여 쿼리를 지원하지 않습니다. BigQuery에는 네이티브 도형 데이터 유형이 없으므로 다각형 도형을 사용하여 쿼리를 실행하려면 간단한 SQL 쿼리에 다른 접근 방식이 필요합니다.

이를 사용할 수 있는 매우 강력한 BigQuery 기능 중 하나는 사용자 정의 함수 (UDF)입니다. UDF는 SQL 쿼리 내에서 자바스크립트 코드를 실행합니다.

이 단계의 작업 코드는 step7/map.html에 있습니다.

BigQuery API의 UDF

UDF용 BigQuery API 접근 방식은 웹 콘솔과 약간 다릅니다. jobs.insert method을 호출해야 합니다.

API를 통한 표준 SQL 쿼리의 경우 사용자 정의 함수를 사용하려면 SQL 문 하나만 있으면 됩니다. useLegacySql 값을 false로 설정해야 합니다. 아래의 자바스크립트 예에서는 새 작업을 삽입하는 요청 객체(이 경우 사용자 정의 함수가 있는 쿼리)를 만들고 전송하는 함수를 보여줍니다.

이러한 접근 방식의 예가 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 문을 만듭니다. 다각형 좌표는 JSON.stringify(poly) 메서드 호출을 사용하여 삽입되어 x,y 좌표 쌍의 자바스크립트 배열을 문자열로 변환합니다. 다각형 객체는 SQL을 빌드하는 함수에 인수로 전달됩니다.

둘째, 코드가 기본 SQL SELECT 문을 빌드합니다. UDF는 이 예의 WHERE 표현식에서 호출됩니다.

지도 API와 통합

Maps API 그리기 라이브러리에서 이를 사용하려면 사용자가 그린 다각형을 저장하고 SQL 쿼리의 UDF 부분에 전달해야 합니다.

먼저 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 자바스크립트 함수와 UDF 함수를 호출하는 SQL 문을 구성할 수 있습니다.

step7/map.html에서 실제 예시를 확인하세요.

출력 예시

다음은 선택한 데이터가 히트맵으로 그려진 자유 다각형을 사용하여 BigQuery에서 2016년 NYC TLC 옐로우 택시 데이터의 수령을 쿼리한 결과의 예입니다.

Screen Shot 2017-05-09 10.00.48 AM.png

13. 한 단계 더 나아가

다음은 이 Codelab을 확장하여 데이터의 다른 측면을 살펴볼 수 있는 몇 가지 제안사항입니다. 코드 저장소의 step8/map.html에서 이러한 아이디어의 실제 예를 확인할 수 있습니다.

매핑 중단

지금까지 픽업 위치만 매핑되었습니다. dropoff_latitudedropoff_longitude 열을 요청하고 히트맵 코드를 대신 표시하여 도표를 대신 표시하여 특정 위치에서 시작된 택시 여정의 목적지를 확인할 수 있습니다.

예를 들어 엠파이어 스테이트 빌딩 주변에서 픽업을 요청할 때 택시의 하차 경향을 알아보세요.

수령 위치 외에 이러한 열을 요청하도록 polygonSql()의 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;
}

대신 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년 엠파이어 스테이트 빌딩 바로 근처에서 수령한 모든 승차 지점의 하차 분포를 보여주는 히트맵입니다. 특히 타임스 스퀘어 주변의 5번가에는 물론 23번가와 14번가 사이에 위치한 5번가에 집중된 곳 (빨간색 blob)이 많이 표시됩니다. 확대/축소 수준에서 표현되지 않은 기타 고밀도 위치는 라과디아 공항, JFK 공항, 세계무역센터, 배터리 공원입니다.

Screen Shot 2017-05-09 10.40.01 AM.png

기본 지도 스타일 지정

Maps JavaScript API를 사용하여 Google 지도를 만들 때 JSON 객체를 사용하여 지도 스타일을 설정할 수 있습니다. 데이터 시각화의 경우 지도의 색상을 음소거하는 것이 유용할 수 있습니다. mapstyle.withgoogle.com에서 Google 지도 API 스타일 지정 마법사를 사용하여 지도 스타일을 만들고 사용해 볼 수 있습니다.

지도 객체를 초기화할 때 또는 그 이후에 언제든지 지도 스타일을 설정할 수 있습니다. 다음은 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() 함수의 응답을 보여주는 UI와 쿼리가 진행 중임을 나타내는 애니메이션 그래픽을 추가합니다.

표시할 수 있는 정보에는 쿼리 기간, 반환된 데이터 양, 처리된 데이터 양이 포함됩니다.

<div> 뒤에 HTML을 추가하여 쿼리에서 반환하는 행 수, 쿼리 소요 시간, 처리된 데이터 양을 표시하는 패널을 만듭니다.

<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;
}

애니메이션 그래픽은 페이지에 추가할 수 있지만 필요할 때까지 숨길 수 있으며, BigQuery 작업이 실행 중일 때 이를 표시하는 데 사용되는 자바스크립트와 CSS 코드를 사용할 수 있습니다.

애니메이션 그래픽을 표시하려면 HTML을 추가하세요. 코드 저장소의 img 폴더에 loader.gif라는 이미지 파일이 있습니다.

<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;
}

마지막으로 자바스크립트를 실행하여 상태 패널을 업데이트하고 쿼리가 실행 중일 때 그래픽을 표시하거나 숨깁니다. response 객체를 사용하여 사용 가능한 정보에 따라 패널을 업데이트할 수 있습니다.

현재 작업을 확인할 때 사용할 수 있는 response.statistics 속성이 있습니다. 작업이 완료되면 response.totalRowsresponse.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);
  })
}

페이지가 다음과 같이 표시됩니다.

Screen Shot 2017-05-10 2.32.19 PM.png

step8/map.html에서 전체 예시를 살펴보세요.

14. 고려 사항

마커가 너무 많음

매우 큰 테이블로 작업하는 경우 쿼리가 너무 많은 행을 반환하여 지도에 효율적으로 표시할 수 있습니다. WHERE 절 또는 LIMIT 문을 추가하여 결과를 제한합니다.

마커를 많이 그리면 지도를 읽을 수 없게 될 수 있습니다. HeatmapLayer를 사용해 밀도를 표시하거나 클러스터 마커를 사용하여 클러스터당 단일 기호를 사용하여 여러 데이터 포인트가 배치된 위치를 나타내는 방법을 고려해 보세요. 자세한 내용은 마커 클러스터링 튜토리얼을 참조하세요.

쿼리 최적화

BigQuery에서 모든 쿼리에 전체 테이블을 검색합니다. BigQuery 할당량 사용을 최적화하려면 쿼리에 필요한 열만 선택하세요.

위도와 경도를 문자열이 아닌 부동 소수점으로 저장하면 쿼리 속도가 더 빨라집니다.

흥미로운 결과 내보내기

이 예시에서는 모든 사용자가 사용할 수 있는 BigQuery 테이블에 대해 최종 사용자를 인증해야 합니다. 흥미로운 패턴을 발견하면 Google 지도 데이터 영역을 사용하여 BigQuery에서 결과를 내보내고 정적 데이터 세트를 만들어 더 많은 잠재고객과 공유할 수 있습니다.

Google Maps Platform 서비스 약관에 유의하세요. Google Maps Platform 가격 책정에 대한 자세한 내용은 온라인 문서를 참조하세요.

더 많은 데이터로 플레이하세요

BigQuery에는 위도 및 경도 열이 있는 여러 공개 데이터 세트가 있습니다(예: 2009~2016년 NNYC 택시 데이터 세트, Uber 및 Lyft NYC 여행 데이터, GDELT 데이터 세트).

15. 수고하셨습니다.

BigQuery 테이블에서 지역 쿼리를 빠르게 시작하고 실행하여 패턴을 파악하고 Google 지도에서 패턴을 시각화할 수 있기를 바랍니다. 즐겁게 매핑하시기 바랍니다.

다음 단계

Google Maps Platform 또는 BigQuery에 대해 자세히 알아보려면 다음과 같은 제안을 살펴보세요.

페타바이트급의 서버리스 데이터 웨어하우스 서비스에 대한 자세한 내용은 BigQuery란 무엇인가요?를 참고하세요.

BigQuery API를 사용하여 간단한 애플리케이션을 만드는 방법 가이드를 살펴봅니다.

Google 지도에 사용자와 상호작용하여 도형을 그리는 방법에 관한 자세한 내용은 그리기 라이브러리 개발자 가이드를 참고하세요.

Google 지도에서 데이터를 시각화하는 다른 방법을 알아보세요.

자바스크립트 클라이언트 AP 시작 가이드를 참고하여 클라이언트 API를 사용하여 다른 Google API에 액세스하는 기본 개념을 알아보세요.