Eseguire query e visualizzare dati sulla posizione in BigQuery con Google Maps Platform (JavaScript)
Informazioni su questo codelab
1. Panoramica
Maps può essere uno strumento molto potente quando si visualizzano i pattern in un set di dati correlati alla località in qualche modo. Questa relazione può essere il nome di un luogo, un valore di latitudine e longitudine specifico o il nome di un'area con un confine specifico, ad esempio una sezione censita o un codice postale.
Quando questi set di dati diventano molto grandi, possono essere difficili da eseguire query e visualizzare utilizzando strumenti convenzionali. Utilizzando Google BigQuery per eseguire query sui dati e sulle API di Google Maps per creare la query e visualizzare l'output, puoi esplorare rapidamente i pattern geografici nei dati con pochissima configurazione o codifica, senza dover gestire un sistema per archiviare set di dati di grandi dimensioni.
Cosa devi creare
In questo codelab scriverai ed eseguirai alcune query che mostrano come fornire insight basati sulla posizione in set di dati pubblici molto grandi utilizzando BigQuery. Dovrai anche creare una pagina web che carichi una mappa utilizzando l'API JavaScript di Google Maps Platform, quindi eseguirà e visualizzerà le query spaziali sugli stessi set di dati pubblici di grandi dimensioni utilizzando la libreria client delle API di Google per JavaScript e l'API BigQuery.
Obiettivi didattici
- Come eseguire query su set di dati di località su scala petabyte in pochi secondi con BigQuery utilizzando query SQL, funzioni definite dall'utente e l'API BigQuery
- Come utilizzare Google Maps Platform per aggiungere una mappa Google a una pagina web e consentire agli utenti di tracciarle
- Come visualizzare le query rispetto a grandi set di dati su una mappa Google, come nell'immagine di esempio riportata di seguito, che mostra la densità di abbandono dei taxi nel 2016 rispetto ai viaggi iniziati a partire dall'area dell'Empire State Building.
Che cosa ti serve
- Conoscenza di base di HTML, CSS, JavaScript, SQL e Chrome DevTools
- Un browser web moderno, come versioni recenti di Chrome, Firefox, Safari o Edge.
- Un editor di testo o un IDE di tua scelta
La tecnologia
BigQuery
BigQuery è il servizio di analisi dei dati di Google per set di dati di grandi dimensioni. Dispone di un'API RESTful e supporta query scritte in SQL. Se disponi di dati con valori di latitudine e longitudine, puoi utilizzarli per eseguire query sui tuoi dati in base alla località. Il vantaggio è che puoi esplorare visivamente set di dati di grandi dimensioni per esaminare i pattern senza dover gestire alcuna infrastruttura di server o database. Puoi ottenere risposte alle tue domande in pochi secondi, indipendentemente da quanto siano grandi le tue tabelle utilizzando la grande scalabilità e l'infrastruttura gestita di BigQuery.
Google Maps Platform
Google Maps Platform offre accesso programmatico ai dati di mappe, luoghi e percorsi di Google. Attualmente, più di 2 milioni di siti web e app lo utilizzano per fornire mappe incorporate e query basate sulla posizione geografica ai propri utenti.
Il Livello di disegno API Google Maps Platform JavaScript ti consente di disegnare forme sulla mappa. Possono essere convertite in input per eseguire query su tabelle BigQuery con valori di latitudine e longitudine memorizzati in colonne.
Per iniziare, devi avere un progetto Google Cloud Platform e attivare le API di BigQuery e Maps.
2. Preparazione
Account Google
Se non hai ancora un Account Google (Gmail o Google Apps), devi crearne uno.
Crea un progetto
Accedi alla console di Google Cloud Platform (console.cloud.google.com) e crea un nuovo progetto. Nella parte superiore dello schermo è presente un menu a discesa Progetto:
Dopo aver fatto clic sul menu a discesa di questo progetto, viene visualizzata una voce di menu che ti consente di creare un nuovo progetto:
Nella casella "Inserisci un nuovo nome per il progetto", inserisci un nome per il nuovo progetto, ad esempio "BigQuery Codelab".
Verrà generato un ID progetto. L'ID progetto è un nome univoco tra tutti i progetti Google Cloud. Ricorda il tuo ID progetto, perché lo utilizzerai in un secondo momento. Il nome riportato sopra è già stato preso e non funzionerà per te. Inserisci il tuo ID progetto ovunque tu veda YOUR_PROJECT_ID in questo codelab.
Abilita fatturazione
Per registrarti a BigQuery, utilizza il progetto selezionato o creato nel passaggio precedente. La fatturazione deve essere abilitata su questo progetto. Una volta abilitata la fatturazione, puoi abilitare l'API BigQuery.
Il modo in cui abiliti la fatturazione dipende dal fatto che tu stia creando un nuovo progetto o che tu riattivi la fatturazione per un progetto esistente.
Google offre una prova senza costi di 12 mesi per un massimo di 300 $di utilizzo di Google Cloud Platform, che potresti utilizzare per questo codelab. Scopri di più all'indirizzo https://cloud.google.com/free/.
Nuovi progetti
Quando crei un nuovo progetto, ti viene chiesto di scegliere gli account di fatturazione da collegare al progetto. Se hai un solo account di fatturazione, tale account viene collegato automaticamente al progetto.
Se non hai un account di fatturazione, devi crearne uno e abilitare la fatturazione per il progetto per poter utilizzare molte funzionalità di Google Cloud Platform. Per creare un nuovo account di fatturazione e abilitare la fatturazione per il tuo progetto, segui le istruzioni riportate in Creare un nuovo account di fatturazione.
Progetti esistenti
Se hai un progetto per il quale hai temporaneamente disattivato la fatturazione, puoi riattivarlo:
- Vai alla console di Cloud Platform.
- Nell'elenco dei progetti, seleziona il progetto per cui riattivare la fatturazione.
- Apri il menu laterale a sinistra e seleziona Fatturazione
. Ti verrà chiesto di selezionare un account di fatturazione.
- Fai clic su Imposta account.
Crea un nuovo account di fatturazione
Per creare un nuovo account di fatturazione:
- Vai alla console di Cloud Platform ed esegui l'accesso oppure, se non hai già un account, registrati.
- Apri il menu a sinistra della console e seleziona Fatturazione
- Fai clic sul pulsante Nuovo account di fatturazione. Tieni presente che, se non si tratta del tuo primo account di fatturazione, devi prima aprire l'elenco degli account di fatturazione facendo clic sul nome dell'account di fatturazione esistente nella parte superiore della pagina e poi su Gestisci account di fatturazione.
- Inserisci il nome dell'account di fatturazione e i dati di fatturazione. Le opzioni visualizzate dipendono dal paese del tuo indirizzo di fatturazione. Tieni presente che, una volta creato l'account, gli account degli Stati Uniti non possono più essere modificati.
- Fai clic su Invia e attiva la fatturazione.
Per impostazione predefinita, la persona che crea l'account di fatturazione è un amministratore di fatturazione per l'account.
Per informazioni su come verificare i conti bancari e aggiungere metodi di pagamento alternativi, consulta l'articolo Aggiungere, rimuovere o aggiornare un metodo di pagamento.
Abilita l'API BigQuery
Per attivare l'API BigQuery nel tuo progetto, vai alla pagina delle pagine dell'API BigQuery nella console e fai clic sul pulsante blu "Attiva'".
3. Esecuzione di query sui dati di località in BigQuery
Esistono tre modi per eseguire query sui dati della posizione archiviati in BigQuery, come valori di longitudine e longitudine.
- Query rettangolari: specifica l'area di interesse come query che seleziona tutte le righe all'interno di un intervallo di latitudine e longitudine minimo e massimo.
- Query per raggio: specifica l'area di interesse calcolando un cerchio intorno a un punto mediante la formula di Haversine e le funzioni matematiche per modellare la forma della terra.
- Query poligonali: specifica una forma personalizzata e utilizza una funzione definita dall'utente per esprimere la logica point-in-poly necessaria per verificare se la latitudine e la longitudine di ogni riga rientrano nella forma.
Per iniziare, utilizza l'Editor query nella sezione BigQuery della console di Google Cloud Platform per eseguire le seguenti query sui dati dei taxi di New York.
SQL standard e SQL precedente a confronto
BigQuery supporta due versioni di SQL: SQL precedente e SQL standard. Quest'ultimo è lo standard ANSI del 2011. Ai fini di questo tutorial, utilizzeremo SQL standard perché è conforme agli standard.
Se vuoi eseguire SQL precedente nell'editor BigQuery, puoi farlo seguendo questi passaggi:
- Fai clic sul pulsante "Altro".
- Seleziona "Impostazioni query" dal menu a discesa.
- In "Dialetto SQL", seleziona il pulsante di opzione "Precedente'
- Fai clic sul pulsante "Salva'
Query rettangolari
Le query rettangolari sono molto semplici da creare in BigQuery. Devi solo aggiungere una clausola WHERE
che limiti i risultati restituiti a coloro che hanno una posizione compresa tra i valori minimo e massimo per la latitudine e la longitudine.
Prova l'esempio riportato di seguito nella console di BigQuery. Query relative ad alcune statistiche medie di corsa per le corse avviate in un'area rettangolare che comprende il centro e la Manhattan inferiore. Puoi provare due località diverse. Rimuovi il commento dalla seconda clausola WHERE
per eseguire la query sulle corse avviate all'aeroporto 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
I risultati delle due query mostrano che ci sono differenze significative in termini di distanza media delle corse, tariffe e suggerimento per il ritiro nelle due località.
Manhattan
avg_tip | avg_fare | avg_distance | avg_tip_pc | avg_fare_mile |
2,52 | 12,03 | 9,97 | 22,39 | 5,97 |
JFK
avg_tip | avg_fare | avg_distance | avg_tip_pc | avg_fare_mile |
9,22 | 48,49 | 41,19 | 22,48 | 4,36 |
Query per raggio
Inoltre, le query per raggio sono facili da creare in SQL se conosci un po' di matematica. Utilizzando le funzioni matematiche SQL precedenti di BigQuery, puoi creare una query SQL utilizzando la formula restrittiva che si avvicina a un'area circolare o a una calotta sferica sulla superficie terrestre.
Di seguito è riportato un'istruzione BigQuery SQL di esempio per una query circolare centrata su 40.73943, -73.99585
con un raggio di 0,1 km.
Utilizza un valore costante di 111,045 km per approssimare la distanza rappresentata da un grado.
Si basa su un esempio riportato alla pagina 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
L'SQL per la formula Haversine sembra complicato, ma tutto quello che devi fare è collegare la coordinata centrale, il raggio e i nomi del progetto, del set di dati e della tabella per BigQuery.
Di seguito è riportata una query di esempio che calcola alcune statistiche medie delle corse per il ritiro entro 100 m dall'Empire State Building. Copialo e incollalo nella console web di BigQuery per visualizzare i risultati. Modifica la latitudine e la longitudine per confrontarle con altre aree, ad esempio la località del 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
Di seguito sono riportati i risultati della query. Puoi notare che ci sono grandi differenze nella mancia media, nella tariffa, nella distanza percorsa, nelle dimensioni proporzionali della mancia alla tariffa e alla tariffa media per miglio percorso.
Empire State Building:
avg_tip | avg_fare | avg_distance | avg_tip_pc | avg_fare_mile |
1,17 | 11,08 | 45,28 | 10,53 | 6,42 |
Firenze
avg_tip | avg_fare | avg_distance | avg_tip_pc | avg_fare_mile |
0,52 | 17,63 | 4,75 | 4,74 | 10.9 |
Query per poligono
SQL non supporta l'esecuzione di query utilizzando forme arbitrarie diverse da rettangoli e cerchi. BigQuery non dispone di alcun tipo di dati geometrici o indici spaziali nativi, quindi per eseguire le query utilizzando forme poligonali è necessario un approccio diverso alle query SQL semplici. Uno degli approcci consiste nel definire una funzione geometrica in JavaScript ed eseguirla come funzione definita dall'utente (UDF) in BigQuery.
Molte operazioni di geometria possono essere scritte in JavaScript, in modo che sia facile prenderne una ed eseguirla su una tabella BigQuery contenente valori di latitudine e longitudine. Devi trasmettere il poligono personalizzato tramite una funzione definita dall'utente ed eseguire un test su ogni riga, restituendo solo le righe in cui la latitudine e la longitudine rientrano all'interno del poligono. Scopri di più sugli UDF nel riferimento BigQuery.
Algoritmo Point In poligono
Esistono tanti modi per calcolare se un punto rientra in un poligono in JavaScript. Questa è una porta da C di un'implementazione molto nota che utilizza un algoritmo di ray tracing per determinare se un punto si trova all'interno o all'esterno di un poligono e conta il numero di volte in cui una linea infinitamente lunga attraversa il confine della forma. Occorrono solo poche righe di codice:
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;
}
Trasferimento a JavaScript
La versione JavaScript di questo algoritmo ha il seguente aspetto:
/* 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;
}
Quando utilizzi SQL standard in BigQuery, l'approccio UDF richiede una singola istruzione, ma tale UDF deve essere definita come una funzione temporanea nell'istruzione. Ecco un esempio. Incolla l'istruzione SQL di seguito nella finestra di Editor query.
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
Complimenti!
Hai eseguito tre tipi di query spaziali utilizzando BigQuery. Come hai visto, la località fa una grande differenza per i dati dei risultati delle query su questo set di dati. Tuttavia, a meno che tu non sappia dove eseguire le tue query, è difficile individuare i pattern spaziali ad hoc utilizzando solo query SQL.
Solo se potessimo visualizzare i dati su una mappa ed esplorare i dati definendo aree di interesse arbitrarie! Bene, con le API di Google Maps puoi farlo. Per prima cosa, devi abilitare l'API di Google Maps, configurare una semplice pagina web in esecuzione sulla tua macchina locale e iniziare a utilizzare l'API BigQuery per inviare query dalla tua pagina web.
4. Utilizzo delle API di Google Maps
Dopo aver eseguito alcune semplici query spaziali, il passaggio successivo è visualizzare l'output per vedere i pattern. A questo scopo, attivi l'API di Google Maps, crea una pagina web che invii le query da una mappa a BigQuery e poi disegna i risultati sulla mappa.
Attiva l'API Maps JavaScript
Per questo codelab, dovrai attivare l'API Maps JavaScript di Google Maps Platform nel tuo progetto. Procedi nel modo seguente:
- Nella console di Google Cloud Platform, vai al Marketplace
- Nel Marketplace, cerca "API JavaScript JavaScript'
- Fai clic sul riquadro dell'API Maps JavaScript nei risultati di ricerca.
- Fai clic sul pulsante "Attiva"
Genera una chiave API
Per effettuare richieste a Google Maps Platform, devi generare una chiave API e inviarla con tutte le richieste. Per generare una chiave API, procedi nel seguente modo:
- Nella console di Google Cloud Platform, fai clic sul menu con il menu hamburger per aprire la barra di navigazione a sinistra
- Seleziona "API & Service' > "Credentials'
- Fai clic sul pulsante "Create Credential', quindi seleziona "Chiave API'
- Copia la nuova chiave API
Scarica il codice e configura un server web
Fai clic sul pulsante seguente per scaricare tutto il codice di questo codelab:
Apri il file ZIP scaricato. Verrà decompressa una cartella root (bigquery
), che contiene una cartella per ogni passaggio di questo codelab, insieme a tutte le risorse necessarie.
Le cartelle stepN
contengono lo stato finale desiderato di ogni passaggio di questo codelab. Sono disponibili come riferimento. Eseguiremo tutte le operazioni di programmazione nella directory denominata work
.
Configurare un server web locale
Puoi utilizzare il tuo server web, ma questo codelab è progettato per funzionare bene con il Chrome Web Server. Se non hai ancora installato l'app, puoi installarla dal Chrome Web Store.
Una volta installata, apri l'app. In Chrome puoi svolgere le operazioni che seguono.
- Apri Chrome
- Nella barra degli indirizzi in alto, digita chrome://apps.
- Premi Invio
- Nella finestra che si apre, fai clic sull'icona del server web. Puoi anche fare clic con il pulsante destro del mouse su un'app per aprirla in una scheda normale o bloccata, a schermo intero o in una nuova finestra.
Verrà visualizzata questa finestra di dialogo, che ti consente di configurare il server web locale:
- Fai clic su "SCEGLI CARTELLA' e seleziona la posizione in cui hai scaricato i file di esempio del codelab
- Nella sezione "Opzioni", seleziona la casella accanto a "Mostra automaticamente index.html'
.
- Fai scorrere il pulsante di attivazione/disattivazione "Web Server: STARTED' verso sinistra e poi di nuovo verso destra per interrompere l'operazione e riavvia il server web
5. Caricamento della mappa e degli strumenti di disegno
Creare una pagina di base della mappa
Inizia con una semplice pagina HTML che carica una mappa di Google Maps utilizzando l'API JavaScript di Maps e alcune righe di JavaScript. Il codice dell'esempio di mappa semplice di Google Maps Platform è un ottimo punto di partenza, Viene riprodotto qui per consentirti di copiarlo e incollarlo nell'editor di testo o nell'IDE di tua scelta. In alternativa, puoi trovarlo aprendo index.html
dal repository che hai scaricato.
- Copia
index.html
nella cartellawork
della tua copia locale del repository - Copia la cartella img/ cartella nella cartella di lavoro/ nella tua copia locale del repository
- Apri il lavoro/
index.html
nell'editor di testo o nell'IDE - Sostituisci
YOUR_API_KEY
con la chiave API creata in precedenza
<script src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&callback=initMap"
async defer></script>
- Nel browser, apri
localhost:<port>/work
, doveport
è il numero di porta specificato nella configurazione del server web locale. La porta predefinita è8887
. Dovresti visualizzare le prime mappe.
Se ricevi un messaggio di errore nel browser, verifica che la tua chiave API sia corretta e che il tuo server web locale sia attivo.
Modificare la posizione predefinita e il livello di zoom
Il codice che imposta la posizione e il livello di zoom si trova sulle righe 27 & 28 di index.html e al momento è centrato su Sydney, Australia:
<script>
let map;
function initMap() {
map = new google.maps.Map(document.getElementById('map'), {
center: {lat: -34.397, lng: 150.644},
zoom: 8
});
}
</script>
Questo tutorial funziona con i dati dei viaggi in taxi di BigQuery per New York. Pertanto, in seguito, modificherai il codice di inizializzazione della mappa per centrare una località a New York a un livello di zoom appropriato. 13 o 14 dovrebbero funzionare correttamente.
Per farlo, aggiorna il blocco di codice qui sopra per centrare la mappa sull'Empire State Building e regolare il livello di zoom su 14:
<script>
let map;
function initMap() {
map = new google.maps.Map(document.getElementById('map'), {
center: {lat: 40.7484405, lng: -73.9878531},
zoom: 14
});
}
</script>
Successivamente, ricarica la mappa nel browser per visualizzare i risultati.
Caricare le librerie di disegno e visualizzazione
Per aggiungere funzionalità di disegno alla tua mappa, cambierai lo script che carica l'API Maps JavaScript aggiungendo un parametro facoltativo che indica a Google Maps Platform di abilitare la libreria dei disegni.
Questo codelab utilizza anche HeatmapLayer
, quindi aggiornerà lo script anche per richiedere la libreria di visualizzazione. A tale scopo, aggiungi il parametro libraries
e specifica le librerie visualization
e drawing
come valori separati da virgole, ad esempio libraries=
visualization,drawing
.
Dovrebbe avere questo aspetto:
<script src='http://maps.googleapis.com/maps/api/js?libraries=visualization,drawing&callback=initMap&key=YOUR_API_KEY' async defer></script>
Aggiungi a DrawingManager
Per utilizzare le forme disegnate dall'utente come input per una query, aggiungi DrawingManager
alla mappa con gli strumenti Circle
, Rectangle
e Polygon
attivati.
È consigliabile inserire tutto il codice di configurazione di DrawingManager
in una nuova funzione, quindi nella tua copia di index.html:
- Aggiungi una funzione chiamata
setUpDrawingTools()
con il codice seguente per creareDrawingManager
e impostare la relativa proprietàmap
in modo che faccia riferimento all'oggetto mappa nella pagina.
Le opzioni trasmesse a google.maps.drawing.DrawingManager(options)
determinano il tipo di disegno predefinito e le opzioni di visualizzazione per le forme tracciate. Per selezionare le aree di mappa da inviare come query, le forme devono avere un'opacità pari a zero. Per ulteriori informazioni sulle opzioni disponibili, consulta la sezione Opzioni di 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);
}
- Chiama
setUpDrawingTools()
nella funzioneinitMap()
dopo aver creato l'oggetto mappa
function initMap() {
map = new google.maps.Map(document.getElementById('map'), {
center: {lat: 40.744593, lng: -73.990370}, // Manhattan, New York.
zoom: 12
});
setUpDrawingTools();
}
- Ricarica index.html e verifica di avere gli strumenti di disegno visibili. Assicurati inoltre di poterle utilizzare per disegnare cerchi, rettangoli e forme poligonali.
Puoi fare clic e trascinare per disegnare cerchi e rettangoli, ma i poligoni devono essere tracciati facendo clic su ciascun vertice e facendo doppio clic per terminare la forma.
Disegna eventi
Hai bisogno di un codice per gestire gli eventi che vengono attivati quando un utente completa il disegno di una forma, così come hai bisogno delle coordinate delle forme disegnate per costruire query SQL.
Aggiungeremo il codice per questo passaggio in un secondo momento, ma per ora abbiamo tre gestori di eventi vuoti per gestire gli eventi rectanglecomplete
, circlecomplete
e polygoncomplete
. In questa fase, i gestori non devono eseguire alcun codice.
Aggiungi quanto segue in fondo alla funzione 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.
});
Puoi trovare un esempio pratico di questo codice nella tua copia locale del repository, nella cartella step2
: step2/map.html.
6. Utilizzo dell'API Client BigQuery
L'API client Google BigQuery ti aiuterà a evitare di scrivere molto codice boilerplate necessario per creare le richieste, analizzare le risposte e gestire l'autenticazione. Questo codelab utilizza l'API BigQuery tramite la libreria client delle API di Google per JavaScript, poiché stiamo sviluppando un'applicazione basata su browser.
Successivamente, aggiungerai codice per caricare questa API in una pagina web e utilizzarla per interagire con BigQuery.
Aggiungere l'API Google Client per JavaScript
Userai l'API Google Client per JavaScript per eseguire query su BigQuery. Nella tua copia di index.html
(nella cartella work
), carica l'API utilizzando un tag <script>
come questo. Inserisci il tag subito sotto il tag <script>
che carica l'API di Maps:
<script src='https://apis.google.com/js/client.js'></script>
Dopo aver caricato l'API Google Client, autorizza l'utente ad accedere ai dati in BigQuery. Per farlo, puoi utilizzare OAuth 2.0. Innanzitutto, devi configurare alcune credenziali nel tuo progetto Google Cloud Console.
Creare le credenziali OAuth 2.0
- In Google Cloud Console, dal menu di navigazione, seleziona API e servizi > Credenziali.
Prima di poter impostare le tue credenziali, devi aggiungere una configurazione per la schermata Autorizzazione che verrà visualizzata quando un utente finale della tua applicazione autorizza la tua app ad accedere ai dati di BigQuery per suo conto.
Per farlo, fai clic sulla scheda Schermata consenso OAuth. 2. Devi aggiungere l'API Big Query agli ambiti di questo token. Nella sezione Ambiti per le API Google, fai clic sul pulsante Aggiungi ambito. 3. Nell'elenco, seleziona la casella accanto alla voce Big Query API con l'ambito ../auth/bigquery
. 4. Fai clic su Aggiungi. 5. Inserisci un nome nel campo "Nome applicazione". 6. Fai clic su Salva per salvare le impostazioni. 7. Dovrai poi creare il tuo ID client OAuth. Per farlo, fai clic su Crea credenziali:
- Nel menu a discesa, fai clic su ID client OAuth.
- Nella sezione Tipo di applicazione seleziona Applicazione web.
- Nel campo Nome applicazione, digita un nome per il progetto. Ad esempio "BigQuery e Maps".
- In Restrizioni, nel campo Origini JavaScript autorizzate, inserisci l'URL dell'host locale, inclusi i numeri di porta. Ad esempio:
http://localhost:8887
.
- Fai clic su pulsante Crea.
Un popup mostra l'ID client e il client secret. Per eseguire l'autenticazione contro BigQuery, è necessario l'ID client. Copialo e incollalo in work/index.html
come nuova variabile JavaScript globale denominata clientId
.
let clientId = 'YOUR_CLIENT_ID';
7. Autorizzazione e inizializzazione
La tua pagina web dovrà autorizzare l'utente ad accedere a BigQuery prima di inizializzare la mappa. In questo esempio, viene utilizzato OAuth 2.0 come descritto nella sezione relativa all'autorizzazione della documentazione dell'API JavaScript JavaScript. Per inviare le query, devi utilizzare l'ID client OAuth e l'ID progetto.
Una volta caricata l'API Google Client nella pagina web, devi eseguire i seguenti passaggi:
- Autorizza l'utente.
- Se hai l'autorizzazione, carica l'API BigQuery.
- Carica e inizializza la mappa.
Consulta la sezione step3/map.html per un esempio di aspetto della pagina HTML completata.
Autorizzare l'utente
L'utente finale dell'applicazione deve autorizzare l'applicazione ad accedere ai dati in BigQuery per suo conto. A questo scopo, è l'API Google Client per JavaScript a gestire la logica OAuth.
In un'applicazione reale ci sono molte opzioni su come integrare il passaggio di autorizzazione.
Ad esempio, potresti chiamare authorize()
da un elemento dell'interfaccia utente come un pulsante o farlo quando la pagina è stata caricata. Qui abbiamo scelto di autorizzare l'utente dopo il caricamento dell'API Google Client per JavaScript utilizzando una funzione di callback nel metodo gapi.load()
.
Scrivi del codice subito dopo il tag <script>
che carica l'API Google Client per JavaScript per caricare la libreria client e il modulo di autenticazione, in modo da poter autenticare immediatamente l'utente.
<script src='https://apis.google.com/js/client.js'></script>
<script type='text/javascript'>
gapi.load('client:auth', authorize);
</script>
Al momento dell'autorizzazione, carica l'API BigQuery
Dopo che l'utente è stato autorizzato, carica l'API BigQuery.
Innanzitutto, chiama gapi.auth.authorize()
con la variabile clientId
che hai aggiunto nel passaggio precedente. Gestisci la risposta in una funzione di callback chiamata handleAuthResult
.
Il parametro immediate
consente di stabilire se un popup deve essere mostrato all'utente. Imposta il criterio true
per eliminare il popup di autorizzazione se l'utente è già autorizzato.
Aggiungi alla pagina una funzione chiamata handleAuthResult()
. La funzione deve acquisire un parametro authresult
che ti consentirà di controllare il flusso della logica a seconda che l'utente sia stato autorizzato o meno.
Aggiungi anche una funzione denominata loadApi
per caricare l'API BigQuery se l'utente è stato autorizzato.
Aggiungi logica nella funzione handleAuthResult()
per chiamare loadApi()
se viene trasmesso un oggetto authResult
alla funzione e se la proprietà error
dell'oggetto ha un valore false
.
Aggiungi il codice alla funzione loadApi()
per caricare l'API BigQuery utilizzando il metodo 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');
}
Caricare la mappa
Il passaggio finale consiste nell'inizializzazione della mappa. Per farlo, devi modificare leggermente l'ordine della logica. Al momento si inizializza quando viene caricato il codice JavaScript dell'API di Google Maps.
Puoi farlo chiamando la funzione initMap()
dal metodo then()
dopo il metodo load()
sull'oggetto gapi.client
.
// Load BigQuery client API
function loadApi(){
gapi.client.load('bigquery', 'v2').then(
() => initMap()
);
}
8. Concetti dell'API BigQuery
Le chiamate all'API BigQuery vengono generalmente eseguite in pochi secondi, ma potrebbero non restituire una risposta immediatamente. Hai bisogno di una logica per eseguire il polling di BigQuery per scoprire lo stato dei job di lunga durata e recuperare i risultati solo quando il job è stato completato.
Il codice completo di questo passaggio è disponibile in step4/map.html.
Invio di una richiesta
Aggiungi una funzione JavaScript a work/index.html
per inviare una query utilizzando l'API e alcune variabili per archiviare i valori del set di dati e del progetto BigQuery contenenti la tabella su cui eseguire la query, nonché l'ID progetto che verrà fatturato per eventuali addebiti.
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.
});
}
Controllare lo stato di un job
La funzione checkJobStatus
riportata di seguito mostra come verificare periodicamente lo stato di un job, utilizzando il metodo API get
e il metodo jobId
restituito dalla richiesta di query originale. Ecco un esempio che viene eseguito ogni 500 millisecondi fino al completamento del job.
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]);
});
}
Modifica il metodo sendQuery
per richiamare il metodo checkJobStatus()
come callback nella chiamata request.execute()
. Passa l'ID job a checkJobStatus
. Viene esposto dall'oggetto di risposta 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));
}
Come ottenere i risultati di una query
Per ottenere i risultati di una query quando è stata eseguita, utilizza la chiamata API jobs.getQueryResults
. Aggiungi alla pagina una funzione denominata getQueryResults()
, che accetti un parametro jobId
.
function getQueryResults(jobId){
let request = gapi.client.bigquery.jobs.getQueryResults({
'projectId': billingProjectId,
'jobId': jobId
});
request.execute(response => {
// Do something with the results.
})
}
9. Esecuzione di query sui dati di posizione con l'API BigQuery
Esistono tre modi per utilizzare SQL per eseguire query spaziali contro i dati in BigQuery:
- selezionare per rettangolo (altrimenti noto come riquadro di delimitazione),
- seleziona per raggio
- La potente funzione Funzioni definite dall'utente.
Puoi trovare esempi di query per riquadri di delimitazione e raggio nella sezione Funzioni matematiche del riferimento SQL precedente di BigQuery in "Esempi avanzati".
Per le query relative a riquadri di delimitazione e raggio, puoi chiamare il metodo API query
di BigQuery. Crea l'SQL per ogni query e passalo alla funzione sendQuery
che hai creato nel passaggio precedente.
Un esempio pratico del codice per questo passaggio è in step4/map.html.
Query rettangolari
Il modo più semplice per visualizzare i dati BigQuery su una mappa è richiedere tutte le righe in cui la latitudine e la longitudine rientrano in un rettangolo utilizzando un metodo di confronto con meno e maggiore di. Potrebbe essere la visualizzazione corrente della mappa o una forma tracciata sulla mappa.
Per usare una forma disegnata dall'utente, modifica il codice in index.html
per gestire l'evento di disegno attivato quando viene completato un rettangolo. In questo esempio il codice usa getBounds()
nell'oggetto rettangolare per ottenere un oggetto che rappresenta l'estensione del rettangolo nelle coordinate della mappa e lo trasmette a una funzione chiamata rectangleQuery
:
drawingManager.addListener('rectanglecomplete', rectangle => rectangleQuery(rectangle.getBounds()));
La funzione rectangleQuery
deve solo utilizzare le coordinate in alto a destra (nord est) e in basso a sinistra (sud-ovest) per creare un rapporto minore/maggiore rispetto a ogni riga della tabella BigQuery. Di seguito è riportato un esempio che esegue una query su una tabella con colonne denominate 'pickup_latitude'
e 'pickup_longitude'
che memorizzano i valori della località.
Specifica della tabella BigQuery
Per eseguire una query su una tabella utilizzando l'API BigQuery, devi fornire il nome della tabella in forma completa nella query SQL. Il formato in SQL standard è project.dataset.tablename
. Nella versione precedente di SQL è presente project.dataset.tablename
.
Sono disponibili molte tabelle dei viaggi in taxi a New York. Per visualizzarle, vai alla console web di BigQuery ed espandi la voce di menu "quot;set di dati pubblici". Trova il set di dati denominato new_york
ed espandilo per visualizzare le tabelle. Scegli la tabella delle corse sui taxi gialli: bigquery-public-data.new_york_taxi_trips.tlc_yellow_trips_2016
.
Specifica dell'ID progetto
Nella chiamata API, devi specificare il nome del tuo progetto Google Cloud Platform ai fini della fatturazione. In questo codelab, non è lo stesso progetto che contiene la tabella. Se utilizzi una tabella creata nel tuo progetto caricando i dati, questo ID progetto sarà lo stesso dell'istruzione SQL.
Aggiungi variabili JavaScript al codice per contenere riferimenti al progetto Public Datasets che contiene la tabella su cui stai eseguendo la query, oltre al nome della tabella e del nome del set di dati. Hai anche bisogno di una variabile separata che faccia riferimento al tuo ID progetto di fatturazione.
Aggiungi le variabili JavaScript globali, chiamate billingProjectId, publicProjectId, datasetId
e tableName
, alla tua copia di index.html.
Inizializza le variabili 'publicProjectId'
, 'datasetId'
e 'tableName'
con i dettagli del progetto BigQuery Public Datasets. Inizializza billingProjectId
con il tuo ID progetto (quello creato in"Come iniziare", in questo codelab).
let billingProjectId = 'YOUR_PROJECT_ID';
let publicProjectId = 'bigquery-public-data';
let datasetId = 'new_york_taxi_trips';
let tableName = 'tlc_yellow_trips_2016';
Ora aggiungi due funzioni al codice per generare l'SQL e inviare la query a BigQuery utilizzando la funzione sendQuery
creata nel passaggio precedente.
La prima funzione deve essere chiamata rectangleSQL()
e deve accettare due argomenti, una coppia di oggetti google.Maps.LatLng
che rappresenta gli angoli del rettangolo nelle coordinate della mappa.
La seconda funzione deve essere denominata rectangleQuery()
. In questo modo, il testo della query passa alla funzione 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;
}
A questo punto, hai abbastanza codice per inviare una query a BigQuery per tutte le righe contenute in un rettangolo disegnato dall'utente. Prima di aggiungere altri metodi di query per cerchi e forme a mano libera, diamo un'occhiata a come gestire i dati che vengono restituiti da una query.
10. Visualizzazione della risposta
Le tabelle BigQuery possono essere molto grandi ( Petebyte di dati) e possono crescere di centinaia di migliaia di righe al secondo. Per questo motivo è importante provare a limitare la quantità di dati restituiti in modo che possano essere tracciati sulla mappa. Se disegni la posizione di ogni riga in un set di risultati molto grande (decine di migliaia di righe o più), verrà creata una mappa illeggibile. Esistono molte tecniche per aggregare le località sia nella query SQL che sulla mappa e puoi limitare i risultati restituiti da una query.
Il codice completo di questo passaggio è disponibile alla pagina step5/map.html.
Per mantenere la quantità di dati trasferiti alla tua pagina web in un formato ragionevole per questo codelab, modifica la funzione rectangleSQL()
per aggiungere un'istruzione che limiti la risposta a 10.000 righe. Nell'esempio seguente, questo valore è specificato in una variabile globale denominata recordLimit
, in modo che tutte le funzioni di query possano utilizzare lo stesso valore.
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;
}
Per visualizzare la densità dei luoghi, puoi utilizzare una mappa termica. A questo scopo, l'API Maps JavaScript ha una classe Heatmap Layer. La mappa termica prende un array di coordinate di latitudine e longitudine in modo da poter convertire facilmente le righe restituite dalla query in una mappa termica.
Nella funzione getQueryResults
, passa l'array response.result.rows
a una nuova funzione JavaScript chiamata doHeatMap()
, che creerà una mappa termica.
Ogni riga contiene una proprietà denominata f
, che è un array di colonne. Ogni colonna avrà una proprietà v
contenente il valore.
Il codice deve includere le colonne in ogni riga ed estrarre i valori.
Nella query SQL, hai chiesto solo i valori Latitudine e Longitudine dei ritiri dei taxi, pertanto la risposta conterrà solo due colonne.
Non dimenticare di chiamare setMap()
sul livello della mappa termica dopo avergli assegnato l'array di posizioni. In questo modo sarà visibile sulla mappa.
Ecco un esempio:
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);
}
A questo punto, dovresti essere in grado di:
- Apri la pagina e autorizza l'accesso a BigQuery
- Disegna un rettangolo da qualche parte a New York
- Visualizza i risultati delle query risultanti che vengono visualizzati come mappa termica.
Ecco un esempio relativo al risultato di una query rettangolare sui dati dei taxi gialli di New York del 2016, disegnato come una mappa termica. Mostra la distribuzione dei ritiri attorno all'Empire State Building di sabato a luglio:
11. Esecuzione di query in base a un raggio intorno a un punto
Le query per raggio sono molto simili. Utilizzando le funzioni matematiche SQL precedenti di BigQuery, puoi creare una query SQL utilizzando la Formula di Haversine, che si avvicina a un'area circolare sulla superficie della terra.
Utilizzando la stessa tecnica per i rettangoli, puoi gestire un evento OverlayComplete
per ottenere il centro e il raggio di un cerchio disegnato dall'utente e creare il SQL per la query nello stesso modo.
Un esempio pratico del codice per questo passaggio è incluso nel repository di codice come step6/map.html.
drawingManager.addListener('circlecomplete', circle => circleQuery(circle));
Nella copia di index.html, aggiungi due nuove funzioni vuote: circleQuery()
e haversineSQL()
.
Quindi, aggiungi un gestore di eventi circlecomplete
che trasferisce il centro e il raggio a una nuova funzione chiamata circleQuery().
La funzione circleQuery()
richiama haversineSQL()
per creare il SQL per la query, quindi invia la query richiamando la funzione sendQuery()
come illustrato nel seguente codice di esempio.
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;
}
Prova
Aggiungi il codice riportato sopra e prova a utilizzare lo strumento "Cerchio' per selezionare un'area della mappa. Il risultato dovrebbe essere simile al seguente:
12. Esecuzione di query su forme arbitrarie
Riepilogo: SQL non supporta l'esecuzione di query utilizzando forme arbitrarie diverse da rettangoli e cerchi. BigQuery non dispone di alcun tipo di dati di geometria nativi, quindi per eseguire le query utilizzando forme poligonali è necessario un approccio diverso alle query SQL confortevoli.
Una funzionalità BigQuery molto potente che può essere utilizzata a questo scopo è la funzionalità definita dall'utente. Gli UDF eseguono il codice JavaScript all'interno di una query SQL.
Il codice di lavoro di questo passaggio è in step7/map.html.
UDF nell'API BigQuery
L'approccio dell'API BigQuery per gli UDF è leggermente diverso da quello della console web: è necessario chiamare jobs.insert method
.
Per le query SQL standard tramite l'API, è necessaria una singola istruzione SQL per utilizzare una funzione definita dall'utente. Il valore di useLegacySql
deve essere impostato su false
. L'esempio JavaScript riportato di seguito mostra una funzione che crea e invia un oggetto richiesta per inserire un nuovo job, in questo caso una query con una funzione definita dall'utente.
Un esempio pratico di questo approccio è in 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));
}
La query SQL è creata come segue:
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;
}
Ci sono due cose. Innanzitutto, il codice crea l'istruzione CREATE TEMPORARY FUNCTION
che incapsula il codice JavaScript per determinare se un determinato punto si trova all'interno di un poligono. Le coordinate del poligono vengono inserite utilizzando la chiamata metodo JSON.stringify(poly)
per convertire un array JavaScript di coppie di coordinate x,y in una stringa. L'oggetto poligono viene passato come argomento alla funzione che crea l'SQL.
In secondo luogo, il codice crea l'istruzione SQL SELECT
principale. In questo esempio, l'UDF viene chiamato nell'espressione WHERE
.
Integrazione con l'API di Google Maps
Per utilizzarla con la libreria di disegni dell'API di Google Maps, dobbiamo salvare il poligono disegnato dall'utente e passarlo nella parte UDF della query SQL.
Innanzitutto, devi gestire l'evento di disegno polygoncomplete
per ottenere le coordinate della forma sotto forma di array di coppie di longitudine e latitudine:
drawingManager.addListener('polygoncomplete', polygon => {
let path = polygon.getPaths().getAt(0);
let queryPolygon = path.map(element => {
return [element.lng(), element.lat()];
});
polygonQuery(queryPolygon);
});
La funzione polygonQuery
può quindi strutturare le funzioni JavaScript UDF come stringa, nonché l'istruzione SQL che chiamerà la funzione UDF.
Vedi step7/map.html per un esempio pratico di questo esempio.
Output di esempio
Ecco un esempio di risultato dell'interrogazione dei dati di prelievo dai dati dei taxi gialli NYC TLC 2016 di BigQuery in un poligono a mano libera con i dati selezionati come mappa termica.
13. Andare oltre
Ecco alcuni suggerimenti per estenderlo in modo da esaminare altri aspetti dei dati. Per un esempio pratico di queste idee, consulta il sito step8/map.html nel repository del codice.
Abbandoni alla mappa
Finora abbiamo mappato solo i luoghi di ritiro. Se richiedi le colonne dropoff_latitude
e dropoff_longitude
e modifichi il codice della mappa termica per tracciare questi dati, puoi visualizzare le destinazioni dei viaggi in taxi che hanno avuto inizio in una località specifica.
Ad esempio, vediamo dove i taxi tendono a lasciare le persone quando chiedono un ritiro nei pressi dell'Empire State Building.
Modifica il codice per l'istruzione SQL in polygonSql()
per richiedere queste colonne oltre alla località di ritiro.
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;
}
La funzione doHeatMap
può quindi utilizzare i valori di abbandono. L'oggetto del risultato ha uno schema che può essere controllato per trovare la posizione di queste colonne nell'array. In questo caso sarebbero nelle posizioni indice 2 e 3. Questi indici possono essere letti da una variabile per rendere il codice più gestibile. Nota: il maxIntensity
della mappa termica è impostato per mostrare una densità massima di 20 abbandoni per pixel come massimo.
Aggiungi alcune variabili per modificare le colonne che utilizzi per i dati della mappa termica.
// 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);
}
Ecco una mappa termica che mostra la distribuzione dei punti di abbandono da tutti i ritiri nelle vicinanze dell'Empire State Building nel 2016. Puoi vedere grandi concentrazioni (i blob rossi) delle destinazioni del centro, in particolare intorno a Times Square, così come lungo la 5th Avenue tra la 23rd St e la 14th St. Altre località ad alta densità non mostrate a questo livello di zoom includono La Guardia e gli aeroporti JFK, il World Trade Center e Battery Park.
Applicazione di stili alla mappa di base
Quando crei una mappa Google con l'API Maps JavaScript, puoi impostare lo stile della mappa utilizzando un oggetto JSON. Per le visualizzazioni dei dati, può essere utile disattivare l'audio dei colori presenti nella mappa. Puoi creare e provare gli stili della mappa utilizzando la Procedura guidata per l'applicazione di stili dell'API di Google Maps all'indirizzo mapstyle.withgoogle.com.
Puoi impostare uno stile di mappa quando inizializzi un oggetto mappa o in un secondo momento. Ecco come aggiungere uno stile personalizzato nella funzione 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();
}
Lo stile di esempio riportato di seguito mostra una mappa in scala di grigi con etichette dei punti di interesse.
[
{
"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"
}
]
}
]
Fornire il feedback degli utenti
Anche se di solito BigQuery fornisce una risposta in pochi secondi, a volte è utile mostrare all'utente che sta succedendo qualcosa mentre la query è in esecuzione.
Aggiungi una UI alla pagina web che mostri la risposta della funzione checkJobStatus()
e un'immagine animata per indicare che la query è in corso.
Le informazioni che puoi visualizzare includono la durata della query, la quantità di dati restituiti e la quantità di dati elaborati.
Aggiungi del codice HTML dopo la mappa <div>
per creare un riquadro nella pagina che mostri il numero di righe restituite da una query, il tempo impiegato dalla query e la quantità di dati elaborati.
<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>
L'aspetto e la posizione di questo riquadro sono controllati dal CSS. Aggiungi un CSS per posizionare il riquadro nell'angolo in alto a sinistra della pagina, sotto i pulsanti del tipo di mappa e la barra degli strumenti di disegno come nello snippet riportato di seguito.
#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;
}
L'immagine animata può essere aggiunta alla pagina, ma nascosta fino a quando è necessario, e alcuni codici JavaScript e CSS utilizzati per mostrarla quando è in esecuzione un job BigQuery.
Aggiungi del codice HTML per mostrare un'immagine animata. Nel repository di codice è presente un file immagine denominato loader.gif
nella cartella img
.
<img id="spinner" src="img/loader.gif">
Aggiungi del codice CSS per posizionare l'immagine e nasconderla per impostazione predefinita finché non sarà necessario.
#spinner {
position: absolute;
top: 50%;
left: 50%;
margin-left: -32px;
margin-top: -32px;
opacity: 0;
z-index: -1000;
}
Infine, aggiungi del codice JavaScript per aggiornare il riquadro di stato e mostrare o nascondere l'immagine quando è in esecuzione una query. Puoi utilizzare l'oggetto response
per aggiornare il riquadro in base alle informazioni disponibili.
Quando controlli un job attuale, è disponibile una proprietà response.statistics
. Una volta completato il lavoro, puoi accedere alle proprietà response.totalRows
e response.totalBytesProcessed
. È utile convertire l'utente in millisecondi in secondi, in byte, per la visualizzazione, come illustrato nell'esempio di codice riportato di seguito.
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';
}
}
Richiama questo metodo quando arriva una risposta a una chiamata checkJobStatus()
e quando vengono recuperati i risultati della query. Ad esempio:
// 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);
})
}
Per attivare/disattivare l'immagine animata, aggiungi una funzione per controllarne la visibilità. Questa funzione attiva/disattiva l'opacità di qualsiasi elemento DOM HTML trasmesso.
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;
}
}
Infine, chiama questo metodo prima di elaborare una query e, dopo che il risultato della query è tornato da BigQuery.
Questo codice chiama la funzione fadeToggle
quando l'utente ha finito di disegnare un rettangolo.
drawingManager.addListener('rectanglecomplete', rectangle => {
//show an animation to indicate that something is happening.
fadeToggle(document.getElementById('spinner'));
rectangleQuery(rectangle.getBounds());
});
Dopo aver ricevuto la risposta alla query, chiama nuovamente il numero fadeToggle()
per nascondere l'immagine animata.
// 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);
})
}
La pagina dovrebbe avere un aspetto simile al seguente.
Guarda l'esempio completo in step8/map.html.
14. Aspetti da considerare
Troppi indicatori
Se lavori con tabelle molto grandi, la query potrebbe restituire troppe righe per essere visualizzate in modo efficiente su una mappa. Limita i risultati aggiungendo una clausola WHERE
o un'istruzione LIMIT
.
Disegnare molti indicatori può rendere la mappa illeggibile. Potresti usare HeatmapLayer
per mostrare la densità o gli indicatori del cluster per indicare dove si trovano molti punti dati utilizzando un singolo simbolo per cluster. Puoi trovare ulteriori informazioni nel tutorial su Marker Clustering.
Ottimizzazione delle query
BigQuery analizzerà l'intera tabella con ogni query. Per ottimizzare l'utilizzo della quota di BigQuery, seleziona solo le colonne necessarie nella query.
Le query saranno più veloci se memorizzi latitudine e longitudine come numero in virgola mobile anziché stringhe.
Esporta risultati interessanti
Gli esempi riportati di seguito richiedono che l'utente finale venga autenticato nella tabella BigQuery, che non si adatta a ogni caso d'uso. Quando hai scoperto alcuni pattern interessanti, potrebbe essere più facile condividerli con un pubblico più ampio, esportando i risultati da BigQuery e creando un set di dati statico utilizzando Livello dati di Google Maps.
La nota legale
Ricorda i Termini di servizio di Google Maps Platform. Per ulteriori dettagli sui prezzi di Google Maps Platform, consulta la documentazione online.
Gioca con più dati!
In BigQuery sono presenti diversi set di dati pubblici con colonne di latitudine e longitudine, ad esempio i set di dati Taxi di New York del 2009-2016, i dati di viaggio di NYC e Ubisoft e il set di dati GDELT.
15. Complimenti!
Ci auguriamo che questo ti consenta di iniziare a lavorare rapidamente con alcune query geografiche sulle tabelle BigQuery, in modo da poter scoprire i pattern e visualizzarli su una mappa Google. Buona mappatura!
Passaggi successivi
Se vuoi scoprire di più su Google Maps Platform o BigQuery, dai un'occhiata ai seguenti suggerimenti.
Vedi Che cos'è BigQuery per saperne di più sul servizio di data warehouse serverless su scala nell'ordine di petabyte di Google.
Dai un'occhiata alla guida illustrativa per creare un'applicazione semplice con l'API BigQuery.
Consulta la guida per gli sviluppatori della raccolta di disegni per maggiori dettagli su come consentire l'interazione dell'utente per disegnare forme su una mappa Google.
Scopri altri modi per visualizzare i dati su una mappa di Google Maps.
Consulta la Guida introduttiva per l'API client JavaScript per comprendere i concetti di base sull'utilizzo dell'API client per accedere ad altre API di Google.