Interroger et visualiser des données de localisation dans BigQuery avec Google Maps Platform (JavaScript)
À propos de cet atelier de programmation
1. Présentation
Maps peut être un outil très puissant pour visualiser les modèles d'un ensemble de données lié à la position d'une manière ou d'une autre. Cette relation peut correspondre au nom d'un lieu, à une valeur de latitude et de longitude spécifique, ou au nom d'une zone comportant une limite spécifique telle qu'un secteur de recensement ou un code postal.
Lorsque ces ensembles de données sont très volumineux, il peut être difficile de les interroger et de les visualiser à l'aide d'outils conventionnels. En utilisant Google BigQuery pour interroger les données et les API Google Maps afin de créer la requête et de visualiser la sortie, vous pouvez rapidement explorer les modèles géographiques de vos données avec très peu de configuration ou de codage, et sans gérer un système pour stocker de très grands ensembles de données.
Ce que vous allez faire
Dans cet atelier de programmation, vous allez écrire et exécuter des requêtes qui montrent comment fournir des insights basés sur la position à des ensembles de données publics très volumineux à l'aide de BigQuery. Vous allez également créer une page Web qui charge une carte à l'aide de l'API JavaScript Google Maps Platform, puis exécute et visualise des requêtes spatiales sur les mêmes ensembles de données publics très volumineux à l'aide de la bibliothèque cliente des API Google pour JavaScript et de l'API BigQuery.
Points abordés
- Interroger des ensembles de données de localisation à l'échelle du pétaoctet en quelques secondes avec BigQuery, à l'aide de requêtes SQL, de fonctions définies par l'utilisateur et de l'API BigQuery
- Utiliser Google Maps Platform pour ajouter une carte Google Maps à une page Web et permettre aux utilisateurs de dessiner des formes
- Comment visualiser les requêtes par rapport à de grands ensembles de données sur une carte Google (comme dans l'exemple ci-dessous), qui montre la densité des lieux de prise en charge des taxis en 2016 à partir de découvertes du quartier autour de l'Empire State Building.
Ce dont vous avez besoin
- Connaissances de base en HTML, CSS, JavaScript, SQL et Chrome DevTools
- Navigateur Web moderne, tel que les versions récentes de Chrome, Firefox, Safari ou Edge.
- Un éditeur de texte ou un IDE de votre choix
La technologie
BigQuery
BigQuery est le service d'analyse de données de Google sur des ensembles de données très volumineux. Il comporte une API RESTful et accepte les requêtes écrites en SQL. Si vous disposez de données de latitude et de longitude, vous pouvez les interroger en fonction de leur position. L'avantage est que vous pouvez explorer visuellement de très grands ensembles de données pour examiner les tendances sans avoir à gérer d'infrastructure de serveur ou de base de données. Vous pourrez obtenir des réponses à vos questions en quelques secondes, quelle que soit la taille de vos tables grâce à l'évolutivité massive et l'infrastructure gérée de BigQuery.
Google Maps Platform
Google Maps Platform offre un accès programmatique aux données cartographiques, de lieu et d'itinéraire de Google. Plus de deux millions de sites Web et d'applications l'utilisent actuellement pour proposer des cartes intégrées et des requêtes basées sur la position à leurs utilisateurs.
Le calque de dessin de l'API JavaScript Google Maps Platform vous permet de dessiner des formes sur la carte. Celles-ci peuvent être converties en entrée pour exécuter des requêtes sur des tables BigQuery dont les valeurs de latitude et de longitude sont stockées dans des colonnes.
Pour commencer, vous avez besoin d'un projet Google Cloud Platform avec BigQuery et les API Google Maps activées.
2. Préparation
Compte Google
Si vous ne possédez pas encore de compte Google (Gmail ou Google Apps), vous devez en créer un.
Créer un projet
Connectez-vous à la console Google Cloud Platform ( console.cloud.google.com) et créez un projet. En haut de votre écran, vous trouverez un menu déroulant "Projet".
Lorsque vous cliquez sur le menu déroulant de ce projet, un élément de menu vous permet de créer un projet:
Dans le champ indiquant "Saisissez un nouveau nom pour votre projet", saisissez un nom pour ce nouveau projet, par exemple "Atelier de programmation BigQuery" :
Un ID de projet sera généré pour vous. L'ID du projet est un nom unique parmi tous les projets Google Cloud. Mémorisez l'ID de projet, car vous en aurez besoin plus tard. Le nom ci-dessus a déjà été utilisé et ne fonctionnera pas pour vous. Dans cet atelier de programmation, insérez votre propre ID de projet chaque fois que vous voyez YOUR_PROJECT_ID.
Activer la facturation
Pour vous inscrire à BigQuery, utilisez le projet sélectionné ou créé à l'étape précédente. La facturation doit être activée sur ce projet. Une fois la facturation activée, vous pouvez activer l'API BigQuery.
La procédure d'activation de la facturation varie selon que vous créez un projet ou que vous réactivez la facturation pour un projet existant.
Google vous offre un essai offert de 12 mois pour jusqu'à 300 $d'utilisation de Google Cloud Platform. Vous pouvez l'utiliser pour cet atelier de programmation. Pour en savoir plus, consultez https://cloud.google.com/free/.
Nouveaux projets
Lorsque vous créez un projet, vous êtes invité à choisir les comptes de facturation que vous souhaitez associer au projet. Si vous n'avez qu'un seul compte de facturation, celui-ci est automatiquement associé à votre projet.
Si vous ne possédez pas de compte de facturation, vous devez en créer un et activer la facturation sur votre projet avant de pouvoir utiliser de nombreuses fonctionnalités de Google Cloud Platform. Pour créer un compte de facturation et activer la facturation pour votre projet, suivez les instructions de l'article Créer un compte de facturation.
Projets existants
Si vous avez temporairement désactivé la facturation pour un projet, vous pouvez la réactiver:
- Accédez à la console Cloud Platform.
- Dans la liste des projets, sélectionnez le projet sur lequel vous souhaitez réactiver la facturation.
- Ouvrez le menu de gauche de la console, puis sélectionnez Facturation
. Vous êtes invité à sélectionner un compte de facturation.
- Cliquez sur Définir le compte.
Créer un compte de facturation
Pour créer un compte de facturation :
- Accédez à la console Cloud Platform et connectez-vous ou, si vous n'avez pas encore de compte, inscrivez-vous.
- Ouvrez le menu sur la gauche de la console, puis sélectionnez Facturation
.
- Cliquez sur le bouton Nouveau compte de facturation. Notez que s'il ne s'agit pas de votre premier compte de facturation, vous devez d'abord ouvrir la liste des comptes de facturation en cliquant sur son nom en haut de la page, puis sur Gérer les comptes de facturation.
- Saisissez le nom du compte de facturation et vos informations de facturation. Les options qui s'affichent dépendent du pays de votre adresse de facturation. Notez que pour les comptes aux États-Unis, vous ne pouvez pas modifier le statut fiscal une fois le compte créé.
- Cliquez sur Envoyer et activer la facturation.
Par défaut, la personne qui crée le compte de facturation est un administrateur de la facturation sur ce compte.
Pour savoir comment valider des comptes bancaires et ajouter un mode de paiement secondaire, consultez Ajouter, supprimer ou mettre à jour un mode de paiement.
Activer l'API BigQuery
Pour activer l'API BigQuery dans votre projet, accédez à la page "Place de marché" de l'API BigQuery dans la console et cliquez sur le bouton bleu "Activer".
3. Interroger des données de localisation dans BigQuery
Il existe trois façons d'interroger des données de localisation stockées en tant que valeurs de latitude et de longitude dans BigQuery.
- Requêtes rectangulaires: définissez la zone d'intérêt sous la forme d'une requête qui sélectionne toutes les lignes situées dans une plage de latitude et de longitude minimales et maximales.
- Requêtes de rayon: spécifiez la zone d'intérêt en calculant un cercle autour d'un point à l'aide de la formule de mathématiques et des fonctions mathématiques pour modéliser la forme de la Terre.
- Requêtes polygonales: spécifiez une forme personnalisée et utilisez une fonction définie par l'utilisateur pour exprimer la logique de point d'entrée nécessaire au test afin de déterminer si chaque latitude et sa longitude appartiennent à la forme.
Pour commencer, utilisez l'éditeur de requête de la section BigQuery de la console Google Cloud Platform afin d'exécuter les requêtes suivantes sur les données des taxis new-yorkais.
SQL standard ou ancien SQL
BigQuery est compatible avec deux versions de SQL : Legacy SQL et Standard SQL. La deuxième méthode est la norme ANSI de 2011. Dans ce tutoriel, nous utiliserons le SQL standard, car il assure une meilleure conformité aux normes.
Pour exécuter l'ancien SQL dans l'éditeur BigQuery, procédez comme suit:
- Cliquez sur le bouton "Plusc".
- Sélectionnez "Paramètres de requête" dans le menu déroulant.
- Sous "Dialecte SQL", sélectionnez la case d'option "Ancien et n° 39" ;
- Cliquez sur le bouton "Enregistrer".
Requêtes Rectangle
Les requêtes rectangulaires sont assez simples à créer dans BigQuery. Il vous suffit d'ajouter une clause WHERE
qui limite les résultats renvoyés à ceux dont les lieux sont compris entre les valeurs minimale et maximale de latitude et de longitude.
Essayez l'exemple ci-dessous dans la console BigQuery. Cette requête permet d'obtenir des statistiques moyennes sur les trajets effectués dans une zone rectangulaire contenant des quartiers de Midtown et Lower Manhattan. Vous pouvez essayer deux emplacements différents. Annulez la mise en commentaire de la deuxième clause WHERE
pour exécuter la requête sur les trajets commencés à l'aéroport 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
Les résultats des deux requêtes montrent qu'il existe de grandes différences en termes de distance moyenne, de tarif et de conseil pour les retraits dans les deux lieux.
Manhattan
moyenne | fare_fare | avg_distance | avg_tip_pc | avg_fare_mile |
2,52 | 12,03 | 9,97 | 22,39 | 5,97 |
CDG
moyenne | fare_fare | avg_distance | avg_tip_pc | avg_fare_mile |
09,22 | 48,49 | 41,19 | 22,48 | 4,36 |
Requêtes par rayon
Les requêtes par rayon sont également faciles à créer en SQL si vous connaissez les principes mathématiques. À l'aide de l'ancien SQL des fonctions mathématiques de BigQuery, vous pouvez créer une requête SQL à l'aide de la formule de Haversine, qui se rapproche d'une zone circulaire ou d'une boule sphérique sur la surface de la Terre.
Voici un exemple d'instruction SQL BigQuery pour une requête circulaire centrée sur 40.73943, -73.99585
et dont le rayon est de 0,1 km.
Il utilise une valeur constante de 111,045 kilomètres pour estimer la distance représentée par un degré.
Il est basé sur un exemple disponible à l'adresse suivante : 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
La requête SQL pour la formule de Haversine semble compliquée, mais il vous suffit de brancher le centre du cercle, le rayon, et les noms du projet, de l'ensemble de données et de la table pour BigQuery.
Voici un exemple de requête qui calcule certaines statistiques moyennes relatives aux trajets effectués dans un rayon de 100 mètres autour de l'Empire State Building. Copiez et collez ce code dans la console Web BigQuery pour afficher les résultats. Modifiez la latitude et la longitude pour comparer d'autres lieux, comme le lieu situé dans le 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
Les résultats de la requête sont présentés ci-dessous. Vous pouvez constater des différences majeures entre le pourboire moyen, le tarif, la distance du trajet, la taille proportionnelle du pourboire par rapport au tarif et le tarif moyen par mile parcouru.
Empire State Building:
moyenne | fare_fare | avg_distance | avg_tip_pc | avg_fare_mile |
1.17 | 11,08 | 45,28 | 10,53 | 6,42 |
En Seine-Saint-Denis
moyenne | fare_fare | avg_distance | avg_tip_pc | avg_fare_mile |
0,52 | 17,63 | 4,75 | 4,74 | 10.9 |
Requêtes polygonales
SQL n'est pas compatible avec les requêtes utilisant des formes arbitraires autres que des rectangles et des cercles. BigQuery ne dispose d'aucun type de données de géométrie natif ni d'index spatial. Pour exécuter des requêtes à l'aide de formes polygonales, vous devez adopter une approche différente pour les requêtes SQL simples. Une approche consiste à définir une fonction de géométrie en JavaScript et à l'exécuter en tant que fonction définie par l'utilisateur (UDF) dans BigQuery.
Il est possible d'écrire de nombreuses opérations géométriques en JavaScript pour pouvoir en prendre une facilement et l'exécuter sur une table BigQuery contenant des valeurs de latitude et de longitude. Vous devez transmettre le polygone personnalisé via une UDF et effectuer un test sur chaque ligne, en renvoyant uniquement les lignes où la latitude et la longitude se trouvent à l'intérieur du polygone. Pour en savoir plus sur les UDF, consultez la documentation de référence sur BigQuery.
Algorithme Point In Polygon
Il existe de nombreuses façons de calculer si un point se trouve dans un polygone en JavaScript. Ici, un port C qui est une mise en œuvre connue et qui utilise un algorithme de suivi des rayons pour déterminer si un point se trouve à l'intérieur ou à l'extérieur d'un polygone en comptant le nombre de fois qu'une ligne infinie traverse la limite de la forme. Il ne nécessite que quelques lignes de code:
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;
}
Transfert vers JavaScript
La version JavaScript de cet algorithme se présente comme suit:
/* 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;
}
Lorsque vous utilisez le langage SQL standard dans BigQuery, l'approche UDF ne nécessite qu'une seule instruction, mais elle doit être définie en tant que fonction temporaire dans l'instruction. Voici un exemple. Collez l'instruction SQL ci-dessous dans la fenêtre de l'éditeur de requête.
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
Félicitations !
Vous avez à présent exécuté trois types de requêtes spatiales à l'aide de BigQuery. Comme vous l'avez vu, l'emplacement fait une grande différence dans les données des résultats pour les requêtes sur cet ensemble de données. Toutefois, sauf si vous pensez où exécuter vos requêtes, il est difficile de découvrir des modèles ad hoc à l'aide de requêtes SQL uniquement.
Si seulement nous pouvions visualiser les données sur une carte, et les explorer en définissant des zones d'intérêt arbitraires ! Les API Google Maps sont là pour vous aider. Pour commencer, vous devez activer l'API Google Maps, configurer une page Web simple qui s'exécute sur votre ordinateur local et commencer à utiliser l'API BigQuery pour envoyer des requêtes à partir de votre page Web.
4. Utiliser les API Google Maps
Après avoir exécuté quelques requêtes spatiales simples, l'étape suivante consiste à visualiser le résultat pour afficher les schémas. Pour ce faire, vous allez activer l'API Google Maps, créer une page Web qui envoie des requêtes d'une carte à BigQuery, puis génère les résultats sur la carte.
Activer l'API Maps JavaScript
Pour cet atelier de programmation, vous devez activer l'API Maps JavaScript de Google Maps Platform dans votre projet. Pour ce faire, procédez comme suit :
- Dans la console Google Cloud Platform, accédez à Marketplace.
- Dans la section "Place de marché", recherchez "API Maps JavaScript'".
- Cliquez sur la vignette pour l'API Maps JavaScript dans les résultats de recherche.
- Cliquez sur le bouton "Activerc".
Générer une clé API
Pour envoyer des requêtes à Google Maps Platform, vous devez générer une clé API et l'envoyer avec toutes vos requêtes. Pour générer une clé API:
- Dans la console Google Cloud Platform, cliquez sur le menu à trois barres pour ouvrir la barre de navigation de gauche.
- Sélectionnez API et AMP, Servicec Credentials'.
- Cliquez sur le bouton "Créer un identifiant", puis sélectionnez "Clé API".
- Copier la nouvelle clé API
Télécharger le code et configurer un serveur Web
Cliquez sur le bouton suivant pour télécharger l'ensemble du code de cet atelier de programmation:
Décompressez le fichier ZIP téléchargé. Cela a pour effet de décompresser un dossier racine (bigquery
), qui contient un dossier pour chaque étape de cet atelier de programmation, ainsi que toutes les ressources dont vous aurez besoin.
Les dossiers stepN
contiennent l'état final souhaité de chaque étape de cet atelier de programmation. Ils sont fournis à titre de référence. Tout le travail de codage sera effectué dans le répertoire appelé work
.
Configurer un serveur Web local
Vous êtes libre d'utiliser votre propre serveur Web, mais cet atelier de programmation est conçu pour bien fonctionner avec le serveur Web Chrome. Si vous n'avez pas encore installé cette application, vous pouvez l'installer depuis le Chrome Web Store.
Une fois l'application installée, ouvrez-la dans Chrome. Pour ce faire, procédez comme suit:
- Ouvrez Chrome.
- Dans la barre d'adresse en haut de la page, saisissez "chrome://apps".
- Appuyez sur "Entrée".
- Dans la fenêtre qui s'ouvre, cliquez sur l'icône Serveur Web. Vous pouvez également effectuer un clic droit sur une application pour l'ouvrir dans un onglet standard ou épinglé, en plein écran ou dans une nouvelle fenêtre.
Vous verrez cette boîte de dialogue permettant de configurer le serveur Web local:
.
- Cliquez sur "CHOOSE FOLDER'" (CHOISIR UN DOSSIER). Sélectionnez ensuite l'emplacement dans lequel vous avez téléchargé les exemples de fichiers de l'atelier de programmation.
- Dans la section "Options", cochez la case "Afficher automatiquement index.html' :
.
- Faites glisser le bouton "Web Server: STARTED'" vers la gauche, puis vers la droite pour arrêter et redémarrer le serveur Web.
5. Charger la carte et les outils de dessin
Créer une page de carte de base
Commencez par une page HTML simple qui charge une carte Google Maps à l'aide de l'API Maps JavaScript et de quelques lignes de JavaScript. Le code de l'exemple de carte simple de Google Maps Platform est un bon point de départ. Il est reproduit ici afin que vous puissiez le copier et le coller dans l'éditeur de texte ou l'IDE de votre choix. Vous pouvez également le retrouver en ouvrant index.html
à partir du dépôt que vous avez téléchargé.
- Copier
index.html
dans le dossierwork
de votre copie locale du dépôt - Copiez le fichier/ img dans le dossier "travail/" de votre copie locale du dépôt.
- Ouvrir le travail/
index.html
dans votre éditeur de texte ou votre IDE - Remplacez
YOUR_API_KEY
par la clé API que vous avez créée précédemment
<script src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&callback=initMap"
async defer></script>
- Dans votre navigateur, ouvrez
localhost:<port>/work
, oùport
est le numéro de port spécifié dans la configuration de votre serveur Web local. Le port par défaut est8887
. Vos premières cartes doivent s'afficher.
Si vous recevez un message d'erreur dans le navigateur, vérifiez que votre clé API est correcte et que votre serveur Web local est actif.
Modifier le lieu et le niveau de zoom par défaut
Le code qui définit le lieu et le niveau de zoom se trouve sur les lignes 27 et 28 du fichier index.html, et il est actuellement centré sur Sydney en Australie:
<script>
let map;
function initMap() {
map = new google.maps.Map(document.getElementById('map'), {
center: {lat: -34.397, lng: 150.644},
zoom: 8
});
}
</script>
Ce tutoriel traite des données BigQuery sur les trajets en taxi à New York. Vous allez ensuite modifier le code d'initialisation de la carte pour centrer un lieu à New York avec un niveau de zoom approprié (13 ou 14).
Pour ce faire, mettez à jour le bloc de code ci-dessus pour centrer la carte sur l'Empire State Building et réglez le niveau de zoom à 14:
<script>
let map;
function initMap() {
map = new google.maps.Map(document.getElementById('map'), {
center: {lat: 40.7484405, lng: -73.9878531},
zoom: 14
});
}
</script>
Ensuite, actualisez la carte dans votre navigateur pour voir les résultats.
Charger les bibliothèques de dessins et de visualisations
Pour ajouter des fonctionnalités de dessin à votre carte, vous devez modifier le script qui charge l'API Maps JavaScript en ajoutant un paramètre facultatif indiquant à Google Maps Platform d'activer la bibliothèque de dessins.
Cet atelier de programmation utilise également HeatmapLayer
. Vous devez donc également mettre à jour le script pour demander la bibliothèque de visualisation. Pour ce faire, ajoutez le paramètre libraries
, puis spécifiez les bibliothèques visualization
et drawing
en tant que valeurs séparées par une virgule (par exemple, libraries=
visualization,drawing
).
Ils doivent se présenter comme ceci :
<script src='http://maps.googleapis.com/maps/api/js?libraries=visualization,drawing&callback=initMap&key=YOUR_API_KEY' async defer></script>
Ajouter le DrawingManager
Pour utiliser des formes dessinées par les utilisateurs comme entrée d'une requête, ajoutez DrawingManager
à votre carte, avec les outils Circle
, Rectangle
et Polygon
activés.
Nous vous recommandons d'insérer l'ensemble du code de configuration DrawingManager
dans une nouvelle fonction. Par conséquent, dans votre copie du fichier index.html, procédez comme suit:
- Ajoutez une fonction appelée
setUpDrawingTools()
avec le code suivant pour créerDrawingManager
et définir sa propriétémap
pour référencer l'objet de carte sur la page.
Les options transmises à google.maps.drawing.DrawingManager(options)
définissent le type de dessin par défaut et les options d'affichage des formes dessinées. Pour sélectionner des zones de la carte à envoyer en tant que requêtes, l'opacité des formes doit être égale à zéro. Pour en savoir plus sur les options disponibles, consultez DrawingManager Options.
function setUpDrawingTools() {
// Initialize drawing manager
drawingManager = new google.maps.drawing.DrawingManager({
drawingMode: google.maps.drawing.OverlayType.CIRCLE,
drawingControl: true,
drawingControlOptions: {
position: google.maps.ControlPosition.TOP_LEFT,
drawingModes: [
google.maps.drawing.OverlayType.CIRCLE,
google.maps.drawing.OverlayType.POLYGON,
google.maps.drawing.OverlayType.RECTANGLE
]
},
circleOptions: {
fillOpacity: 0
},
polygonOptions: {
fillOpacity: 0
},
rectangleOptions: {
fillOpacity: 0
}
});
drawingManager.setMap(map);
}
- Appelez
setUpDrawingTools()
dans votre fonctioninitMap()
après la création de l'objet de carte
function initMap() {
map = new google.maps.Map(document.getElementById('map'), {
center: {lat: 40.744593, lng: -73.990370}, // Manhattan, New York.
zoom: 12
});
setUpDrawingTools();
}
- Actualisez index.html et vérifiez que les outils de dessin sont visibles. Vérifiez également que vous pouvez les utiliser pour dessiner des cercles, des rectangles et des polygones.
Vous pouvez cliquer sur les cercles et les faire glisser pour dessiner des cercles et des rectangles, mais vous devez dessiner des polygones en cliquant dessus et en double-cliquant dessus pour terminer la forme.
Gérer les événements de dessin
Vous avez besoin de code pour gérer les événements qui se déclenchent lorsqu'un utilisateur termine le tracé d'une forme, de la même manière que vous avez besoin des coordonnées des formes dessinées pour créer des requêtes SQL.
Nous ajouterons du code pour plus tard, mais pour l'instant, nous allons bouchonner trois gestionnaires d'événements vides pour gérer les événements rectanglecomplete
, circlecomplete
et polygoncomplete
. À ce stade, les gestionnaires n'ont pas besoin d'exécuter du code.
Ajoutez le code suivant en bas de la fonction 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.
});
Vous trouverez un exemple pratique de ce code dans votre copie locale du dépôt, dans le dossier step2
: step2/map.html.
6. Utiliser l'API cliente BigQuery
Grâce à l'API cliente Google BigQuery, vous n'avez pas besoin d'écrire beaucoup de code récurrent pour créer des requêtes, analyser les réponses et gérer l'authentification. Cet atelier de programmation utilise l'API BigQuery via la bibliothèque cliente des API Google pour JavaScript, car nous allons développer une application basée sur un navigateur.
Vous allez ensuite ajouter du code pour charger cette API sur une page Web et l'utiliser pour interagir avec BigQuery.
Ajouter l'API cliente Google pour JavaScript
Vous utiliserez l'API cliente Google pour JavaScript pour exécuter des requêtes sur BigQuery. Dans votre copie de index.html
(dans votre dossier work
), chargez l'API à l'aide d'une balise <script>
de ce type. Placez la balise immédiatement sous la balise <script>
qui charge l'API Google Maps:
<script src='https://apis.google.com/js/client.js'></script>
Après avoir chargé l'API cliente Google, autorisez l'utilisateur à accéder aux données dans BigQuery. Pour ce faire, vous pouvez utiliser OAuth 2.0. Vous devez d'abord configurer des identifiants dans votre projet Google Cloud Console.
Créer des identifiants OAuth 2.0
- Dans le menu de navigation de Google Cloud Console, sélectionnez APIs & Services > Credentials (Identifiants).
Avant de pouvoir définir vos identifiants, vous devez ajouter une configuration pour l'écran d'autorisation qu'un utilisateur final de votre application verra lorsqu'il autorisera votre application à accéder aux données BigQuery en son nom.
Pour ce faire, cliquez sur l'onglet Écran de consentement OAuth. 2. Vous devez ajouter l'API BigQuery à la portée des jetons de ce jeton. Cliquez sur le bouton Add Scope (Ajouter un champ d'application) dans la section "Scopes for Google APIs" (Portées pour les API Google). 3. Dans la liste, cochez la case correspondant à l'entrée API BigQuery avec le champ d'application ../auth/bigquery
. 4. Cliquez sur Add (Ajouter). 5. Saisissez un nom dans le champ "Nom de l'application". 6. Cliquez sur Enregistrer pour sauvegarder vos paramètres. 7. Vous allez ensuite créer votre ID client OAuth. Pour ce faire, cliquez sur Créer des identifiants :
- Dans le menu déroulant, cliquez sur ID client OAuth.
- Sous "Type d'application", sélectionnez Application Web.
- Dans le champ "Application Name" (Nom de l'application), saisissez un nom pour votre projet. Par exemple, "BigQuery" et "Maps".
- Sous Restrictions, dans le champ "Origines JavaScript autorisées", saisissez l'URL de l'hôte local, y compris les numéros de port. Par exemple :
http://localhost:8887
- Cliquez sur le bouton Create (Créer).
Une fenêtre pop-up affiche l'ID et le code secret du client. Vous avez besoin de l'ID client pour effectuer l'authentification auprès de BigQuery. Copiez-le et collez-le dans work/index.html
en tant que nouvelle variable JavaScript globale appelée clientId
.
let clientId = 'YOUR_CLIENT_ID';
7. Autorisation et initialisation
Votre page Web doit autoriser l'utilisateur à accéder à BigQuery avant d'initialiser la carte. Dans cet exemple, nous utilisons OAuth 2.0 comme décrit dans la section relative à l'autorisation de la documentation concernant l'API client JavaScript. Vous devez utiliser l'ID client OAuth et l'ID de votre projet pour envoyer des requêtes.
Lorsque l'API cliente Google est chargée sur la page Web, vous devez effectuer les opérations suivantes:
- Autorisez l'utilisateur.
- Si vous y êtes autorisé, chargez l'API BigQuery.
- Chargez et initialisez la carte.
Consultez le fichier step3/map.html pour voir un exemple de page HTML terminée.
Autoriser l'utilisateur
L'utilisateur final de l'application doit l'autoriser à accéder aux données BigQuery en son nom. Pour ce faire, l'API cliente Google pour JavaScript gère la logique OAuth.
Dans une application réelle, de nombreuses options s'offrent à vous pour intégrer l'étape d'autorisation.
Vous pouvez par exemple appeler authorize()
à partir d'un élément d'interface utilisateur tel qu'un bouton ou le faire une fois la page chargée. Ici, nous avons choisi d'autoriser l'utilisateur une fois que l'API cliente Google pour JavaScript a été chargée, en utilisant une fonction de rappel dans la méthode gapi.load()
.
Écrivez un code immédiatement après la balise <script>
qui charge l'API cliente Google pour JavaScript afin de charger la bibliothèque cliente et le module d'authentification, afin que nous puissions authentifier l'utilisateur immédiatement.
<script src='https://apis.google.com/js/client.js'></script>
<script type='text/javascript'>
gapi.load('client:auth', authorize);
</script>
Lors de l'autorisation, chargez l'API BigQuery
Une fois l'autorisation accordée, chargez l'API BigQuery.
Tout d'abord, appelez gapi.auth.authorize()
avec la variable clientId
que vous avez ajoutée à l'étape précédente. Gérez la réponse dans une fonction de rappel appelée handleAuthResult
.
Le paramètre immediate
détermine si un pop-up est affiché pour l'utilisateur. Définissez-la sur true
pour supprimer le pop-up d'autorisation si l'utilisateur est déjà autorisé.
Ajoutez à votre page une fonction appelée handleAuthResult()
. La fonction doit utiliser un paramètre authresult
, qui vous permet de contrôler le flux de logique selon que l'utilisateur a bien été autorisé ou non.
Ajoutez également une fonction appelée loadApi
pour charger l'API BigQuery si l'utilisateur est bien autorisé.
Ajoutez une logique dans la fonction handleAuthResult()
pour appeler loadApi()
si un objet authResult
est transmis à la fonction et si la propriété error
de l'objet a une valeur false
.
Ajoutez du code à la fonction loadApi()
pour charger l'API BigQuery à l'aide de la méthode 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');
}
Charger la carte
La dernière étape consiste à initialiser la carte. Pour ce faire, vous devez modifier légèrement l'ordre de la logique. Elle s'initialise actuellement lorsque le code JavaScript de l'API Google Maps a été chargé.
Pour ce faire, appelez la fonction initMap()
de la méthode then()
après la méthode load()
sur l'objet gapi.client
.
// Load BigQuery client API
function loadApi(){
gapi.client.load('bigquery', 'v2').then(
() => initMap()
);
}
8. Concepts de l'API BigQuery
Les appels d'API BigQuery s'exécutent généralement en quelques secondes, mais peuvent ne pas renvoyer de réponse immédiatement. Vous devez utiliser une logique pour interroger BigQuery afin de connaître l'état de tâches à exécution longue et ne récupérer les résultats qu'une fois la tâche terminée.
Le code complet de cette étape se trouve à l'étape step4/map.html.
Envoyer une requête
Ajoutez une fonction JavaScript à work/index.html
pour envoyer une requête à l'aide de l'API, ainsi que certaines variables permettant de stocker les valeurs de l'ensemble de données et du projet BigQuery contenant la table à interroger, ainsi que l'ID du projet qui sera facturé pour tous les frais associés.
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.
});
}
Vérifier l'état d'une tâche
La fonction checkJobStatus
ci-dessous montre comment vérifier régulièrement l'état d'une tâche à l'aide de la méthode API get
et de la jobId
renvoyée par la requête de requête d'origine. Voici un exemple qui s'exécute toutes les 500 millisecondes jusqu'à la fin de la tâche.
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]);
});
}
Modifiez la méthode sendQuery
pour appeler la méthode checkJobStatus()
en tant que rappel dans l'appel request.execute()
. Transmettez l'ID de tâche à checkJobStatus
. Le champ jobReference.jobId
est exposé par l'objet de réponse.
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));
}
Obtenir les résultats d'une requête
Pour obtenir les résultats d'une requête lorsqu'elle est terminée, utilisez l'appel d'API jobs.getQueryResults
. Ajoutez à votre page une fonction appelée getQueryResults()
, qui accepte un paramètre de jobId
:
function getQueryResults(jobId){
let request = gapi.client.bigquery.jobs.getQueryResults({
'projectId': billingProjectId,
'jobId': jobId
});
request.execute(response => {
// Do something with the results.
})
}
9. Interroger des données de localisation à l'aide de l'API BigQuery
Il existe trois manières d'utiliser SQL pour exécuter des requêtes spatiales sur des données BigQuery:
- Sélectionner par rectangle (également appelé cadre de délimitation)
- sélectionner par rayon
- la fonctionnalité Fonctions définies par l'utilisateur.
Vous trouverez des exemples de requêtes de cadre de délimitation et de rayon dans la section "Fonctions mathématiques" de la documentation de référence de l'ancien SQL de BigQuery, sous "Exemples avancés'".
Pour les requêtes par cadre et par rayon, vous pouvez appeler la méthode API query
de BigQuery. Créez le SQL pour chaque requête et transmettez-le à la fonction sendQuery
que vous avez créée à l'étape précédente.
Vous trouverez un exemple concret du code de cette étape dans step4/map.html.
Requêtes rectangulaires
Le moyen le plus simple d'afficher les données BigQuery sur une carte consiste à demander toutes les lignes dont la latitude et la longitude se situent dans un rectangle, à l'aide d'une comparaison inférieure ou supérieure à la comparaison. Il peut s'agir de la vue actuelle ou d'une forme dessinée sur la carte.
Pour utiliser une forme dessinée par l'utilisateur, modifiez le code dans index.html
pour gérer l'événement de dessin déclenché lorsqu'un rectangle est terminé. Dans cet exemple, le code utilise getBounds()
sur l'objet rectangulaire pour obtenir un objet représentant l'étendue du rectangle dans les coordonnées de la carte et le transmettre à une fonction appelée rectangleQuery
:
drawingManager.addListener('rectanglecomplete', rectangle => rectangleQuery(rectangle.getBounds()));
La fonction rectangleQuery
doit simplement utiliser les coordonnées en haut à droite (nord-est) et en bas à gauche (sud-ouest) pour construire une valeur inférieure ou supérieure à celle comparée à chaque ligne de la table BigQuery. Voici un exemple de requête qui interroge une table comportant des colonnes intitulées 'pickup_latitude'
et 'pickup_longitude'
, qui stockent les valeurs de la zone géographique.
Spécifier la table BigQuery
Pour interroger une table à l'aide de l'API BigQuery, vous devez fournir le nom de la table au format complet dans votre requête SQL. En SQL standard, le format est project.dataset.tablename
. En ancien SQL, il s'agit de project.dataset.tablename
.
De nombreuses tables sont organisées en taxis new-yorkais. Pour les afficher, accédez à la console Web BigQuery et développez l'élément de menu "ensembles de données publics". Recherchez l'ensemble de données new_york
, puis développez-le pour afficher les tables. Sélectionnez le tableau jaune "Trajets en taxi" : bigquery-public-data.new_york_taxi_trips.tlc_yellow_trips_2016
.
Spécifier l'ID du projet
Dans l'appel d'API, vous devez spécifier le nom de votre projet Google Cloud Platform à des fins de facturation. Dans cet atelier de programmation, il ne s'agit pas du même projet que celui contenant la table. Si vous utilisez une table que vous avez créée dans votre propre projet en important des données, cet ID est identique à celui figurant dans votre instruction SQL.
Ajoutez des variables JavaScript à votre code pour contenir des références au projet d'ensembles de données publics contenant la table que vous interrogez, ainsi que le nom de la table et son nom. Vous avez également besoin d'une variable distincte pour faire référence à votre propre ID de projet de facturation.
Ajoutez les variables JavaScript globales appelées billingProjectId, publicProjectId, datasetId
et tableName
à votre copie du fichier index.html.
Initialisez les variables 'publicProjectId'
, 'datasetId'
et 'tableName'
avec les détails du projet BigQuery Public Datasets. Initialisez billingProjectId
avec votre propre ID de projet (celui que vous avez créé dans la section"Premiers pas de cet atelier de programmation").
let billingProjectId = 'YOUR_PROJECT_ID';
let publicProjectId = 'bigquery-public-data';
let datasetId = 'new_york_taxi_trips';
let tableName = 'tlc_yellow_trips_2016';
Ajoutez maintenant deux fonctions à votre code pour générer le code SQL et envoyer la requête à BigQuery à l'aide de la fonction sendQuery
que vous avez créée à l'étape précédente.
La première fonction doit être appelée rectangleSQL()
et doit accepter deux arguments, une paire d'objets google.Maps.LatLng
représentant les angles du rectangle dans les coordonnées de la carte.
La deuxième fonction doit être appelée rectangleQuery()
. Le texte de la requête est transmis à la fonction 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;
}
À ce stade, vous avez suffisamment de code pour envoyer une requête à BigQuery pour toutes les lignes contenues dans un rectangle tracé par l'utilisateur. Avant d'ajouter d'autres méthodes de requête pour les cercles et les formes à main levée, voyons comment gérer les données provenant d'une requête.
10. Visualiser la réponse
Les tables BigQuery peuvent être très volumineuses (plusieurs pétaoctets de données) et peuvent présenter des centaines de milliers de lignes par seconde. Il est donc important d'essayer de limiter la quantité de données renvoyées afin qu'elles puissent être représentées sur la carte. Si vous tracez l'emplacement de chaque ligne dans un ensemble de résultats très volumineux (des dizaines de milliers de lignes ou plus), une carte est illisible. Il existe de nombreuses techniques d'agrégation de lieux à la fois dans la requête SQL et sur la carte. Vous pouvez limiter les résultats renvoyés par une requête.
Le code complet de cette étape est disponible dans step5/map.html.
Pour réduire la quantité de données transférées à votre page Web dans une taille raisonnable pour cet atelier de programmation, modifiez la fonction rectangleSQL()
pour ajouter une instruction qui limite la réponse à 10 000 lignes. Dans l'exemple ci-dessous, cette option est spécifiée dans une variable globale appelée recordLimit
, de sorte que toutes les fonctions de requête puissent utiliser la même valeur.
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;
}
Pour visualiser la densité d'emplacement, vous pouvez utiliser une carte de densité. L'API Maps JavaScript dispose d'une classe HeatmapLayer à cette fin. Le calque de carte de densité utilise un tableau de coordonnées de latitude et de longitude, ce qui permet de convertir facilement les lignes renvoyées par la requête en carte de densité.
Dans la fonction getQueryResults
, transmettez le tableau response.result.rows
à une nouvelle fonction JavaScript appelée doHeatMap()
qui créera une carte de densité.
Chaque ligne comporte une propriété appelée f
, qui est un tableau de colonnes. Chaque colonne comporte une propriété v
contenant la valeur.
Votre code doit faire une boucle sur les colonnes de chaque ligne et extraire les valeurs.
Dans la requête SQL, vous avez demandé seulement les valeurs de latitude et de longitude des retraits de taxi. Il n'y aura donc que deux colonnes dans la réponse.
N'oubliez pas d'appeler setMap()
sur le calque de la carte de densité lorsque vous lui avez attribué le tableau des positions Cela les mettra en évidence sur la carte.
Exemple :
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);
}
À ce stade, vous devriez pouvoir:
- Ouvrir la page et autoriser l'accès à BigQuery
- Dessiner un rectangle à New York
- Visualisez les résultats de la requête affichés sous forme de carte de densité.
Voici un exemple du résultat d'une requête rectangulaire sur les données de Taxi jaunes de New York en 2016, dessinées en tant que carte de densité. Vous trouverez ici la répartition des retraits autour de l'Empire State Building le samedi de juillet:
11. Interroger des données par rayon autour d'un point
Les requêtes par rayon sont très similaires. À l'aide de l'ancien SQL des fonctions mathématiques de BigQuery, vous pouvez créer une requête SQL à l'aide de la formule de Makers, qui équivaut à une zone circulaire à la surface de la Terre.
À l'aide de la même technique pour les rectangles, vous pouvez gérer un événement OverlayComplete
pour obtenir le centre et le rayon d'un cercle dessiné par l'utilisateur, et créer le SQL pour la requête de la même manière.
Un exemple pratique du code de cette étape est inclus dans le dépôt de code en tant que step6/map.html.
drawingManager.addListener('circlecomplete', circle => circleQuery(circle));
Dans votre copie du fichier index.html, ajoutez deux nouvelles fonctions vides: circleQuery()
et haversineSQL()
.
Ajoutez ensuite un gestionnaire d'événements circlecomplete
qui transmet le centre et le rayon à une nouvelle fonction appelée circleQuery().
.
La fonction circleQuery()
appelle haversineSQL()
pour construire le SQL de la requête, puis envoie la requête en appelant la fonction sendQuery()
conformément à l'exemple de code suivant.
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;
}
Essayer
Ajoutez le code ci-dessus, puis essayez l'outil "Cercle'" pour sélectionner une zone de la carte. Le résultat doit ressembler à ceci :
12. Interroger des formes arbitraires
Récapitulatif: SQL ne permet pas d'effectuer des requêtes à l'aide de formes arbitraires autres que des rectangles et des cercles. BigQuery n'a pas de type de données de géométrie natif. Pour exécuter des requêtes à l'aide de formes polygonales, vous devez adopter une approche différente pour les requêtes SQL simples.
Les fonctions définies par l'utilisateur (UDF) constituent une fonctionnalité BigQuery très puissante. Les fonctions définies par l'utilisateur exécutent du code JavaScript dans une requête SQL.
Le code qui fonctionne pour cette étape se trouve dans step7/map.html.
UDF dans l'API BigQuery
L'approche de l'API BigQuery pour les UDF est légèrement différente de celle de la console Web: vous devez appeler la jobs.insert method
.
Pour les requêtes en SQL standard via l'API, une seule instruction SQL est nécessaire pour utiliser une fonction définie par l'utilisateur. La valeur de useLegacySql
doit être définie sur false
. L'exemple JavaScript ci-dessous illustre une fonction qui crée et envoie un objet de requête pour insérer une nouvelle tâche. Dans le cas présent, une requête comportant une fonction définie par l'utilisateur.
Vous trouverez un exemple concret de cette approche dans 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 requête SQL se construit comme suit:
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;
}
Il se passe deux choses ici. Tout d'abord, le code crée l'instruction CREATE TEMPORARY FUNCTION
qui encapsule le code JavaScript pour s'entraîner si un point donné se trouve dans un polygone. Les coordonnées du polygone sont insérées à l'aide de l'appel de méthode JSON.stringify(poly)
pour convertir un tableau JavaScript de paires de coordonnées x,y en chaîne. L'objet polygone est transmis en tant qu'argument à la fonction qui crée le SQL.
Ensuite, le code crée l'instruction SQL SELECT
principale. Dans cet exemple, la fonction UDF est appelée dans l'expression WHERE
.
Intégrer à l'API Google Maps
Pour l'utiliser avec la bibliothèque de dessins de l'API Google Maps, nous devons enregistrer le polygone dessiné par l'utilisateur et le transmettre à l'utilisateur dans la partie UDF de la requête SQL.
Tout d'abord, nous devons gérer l'événement de dessin polygoncomplete
, afin d'obtenir les coordonnées de la forme sous forme de tableau de paires longitude et latitude:
drawingManager.addListener('polygoncomplete', polygon => {
let path = polygon.getPaths().getAt(0);
let queryPolygon = path.map(element => {
return [element.lng(), element.lat()];
});
polygonQuery(queryPolygon);
});
La fonction polygonQuery
peut ensuite créer les fonctions JavaScript UDF en tant que chaîne, ainsi que l'instruction SQL qui appelle la fonction UDF.
Pour voir un exemple concret de cette étape, consultez le site step7/map.html.
Exemple de résultat
Voici un exemple de résultat de requête portant sur les retraits effectués à partir des données 2016 NYC TLC Yellow Taxi dans BigQuery à l'aide d'un polygone de main levée, les données sélectionnées étant représentées par une carte de densité.
13. Pour aller plus loin
Voici quelques suggestions pour améliorer cet atelier de programmation afin d'examiner d'autres aspects des données. Vous trouverez un exemple pratique de ces idées à l'adresse step8/map.html dans le dépôt de code.
Abandons de mise en correspondance
Jusqu'à présent, nous n'avons mappé que les lieux de prise en charge. En demandant aux colonnes dropoff_latitude
et dropoff_longitude
et en modifiant le code de la carte de densité pour les tracer, vous pouvez voir les destinations des trajets en taxi qui ont commencé à un endroit spécifique.
Par exemple, voyons à quel endroit les taxis ont tendance à déposer les usagers lorsqu'ils cherchent à récupérer le véhicule dans l'Empire State Building.
Modifiez le code de l'instruction SQL dans polygonSql()
pour demander ces colonnes en plus de l'adresse de retrait.
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 fonction doHeatMap
peut ensuite utiliser les valeurs de liste déroulante. L'objet de résultat comporte un schéma qui peut être inspecté pour trouver l'emplacement de ces colonnes dans le tableau. Dans ce cas, elle se trouvera en position 2 et 3. Ces index peuvent être lus à partir d'une variable pour faciliter la gestion du code. La valeur maxIntensity
de la carte de densité est définie pour afficher la densité maximale de 20 abandons par pixel.
Ajoutez des variables vous permettant de modifier les colonnes que vous utilisez pour les données de la carte de densité.
// 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);
}
Voici une carte de densité montrant la répartition des abandons de tous les retraits immédiatement autour de l'Empire State Building en 2016. Vous pouvez observer de grandes concentrations (les blobs rouges) des destinations de Midtown, notamment autour de Times Square, ainsi que le long de la cinquième Avenue entre 23rd St et 14th St. D'autres lieux très fréquentés non visibles à ce niveau de zoom incluent les aéroports La Guardia et JFK, le World Trade Center et Battery Park.
Styliser la carte de base
Lorsque vous créez une carte Google Maps à l'aide de l'API Maps JavaScript, vous pouvez définir le style de la carte en utilisant un objet JSON. Pour les visualisations de données, il peut être utile de couper les couleurs de la carte. Vous pouvez créer et tester des styles de carte à l'aide de l'assistant de style de l'API Google Maps sur mapstyle.withgoogle.com.
Vous pouvez définir un style de carte lorsque vous initialisez un objet de carte ou ultérieurement. Voici comment ajouter un style personnalisé dans la fonction 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();
}
L'exemple de style ci-dessous montre une carte en nuances de gris avec des libellés de points d'intérêt.
[
{
"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"
}
]
}
]
Envoyer des commentaires aux utilisateurs
Même si BigQuery donne généralement une réponse en quelques secondes, il est parfois utile d'indiquer à l'utilisateur qu'un événement se produit pendant l'exécution de la requête.
Ajoutez une interface utilisateur à votre page Web qui affiche la réponse de la fonction checkJobStatus()
, ainsi qu'un graphique animé pour indiquer que la requête est en cours d'exécution.
Les informations que vous pouvez afficher incluent la durée de la requête, la quantité de données renvoyées et la quantité de données traitées.
Ajoutez du code HTML après la carte <div>
pour créer un panneau sur la page indiquant le nombre de lignes renvoyées par une requête, l'heure de la requête et la quantité de données traitées.
<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'apparence et la position de ce panneau sont contrôlées par le CSS. Ajoutez du code CSS pour placer le panneau dans l'angle supérieur gauche de la page, sous les boutons de type de carte et la barre d'outils du dessin, comme dans l'extrait ci-dessous.
#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'image animée peut être ajoutée à la page, mais masquée jusqu'à ce qu'elle soit obligatoire. Du code JavaScript et CSS peut également l'afficher lorsqu'une tâche BigQuery est en cours d'exécution.
Ajoutez du code HTML pour afficher une image animée. Le dépôt de code contient un fichier image nommé loader.gif
img
.
<img id="spinner" src="img/loader.gif">
Ajoutez du code CSS pour positionner l'image et la masquer par défaut jusqu'à ce qu'elle soit nécessaire.
#spinner {
position: absolute;
top: 50%;
left: 50%;
margin-left: -32px;
margin-top: -32px;
opacity: 0;
z-index: -1000;
}
Enfin, ajoutez du code JavaScript pour mettre à jour le panneau d'état, et afficher ou masquer le graphique lorsqu'une requête est en cours d'exécution. Vous pouvez utiliser l'objet response
pour mettre à jour le panneau en fonction des informations disponibles.
Lorsque vous vérifiez une tâche en cours, vous pouvez utiliser une propriété response.statistics
. Une fois le travail terminé, vous pouvez accéder aux propriétés response.totalRows
et response.totalBytesProcessed
. Il peut être utile pour l'utilisateur de convertir des millisecondes en secondes et des octets en gigaoctets pour les afficher, comme illustré dans l'exemple de code ci-dessous.
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';
}
}
Appelez cette méthode en cas de réponse à un appel checkJobStatus()
et lorsque les résultats de la requête sont récupérés. Exemple :
// 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);
})
}
Pour activer/désactiver l'image animée, ajoutez une fonction permettant de contrôler sa visibilité. Cette fonction permet d'activer et de désactiver l'opacité des éléments DOM HTML qui lui sont transmis.
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;
}
}
Enfin, appelez cette méthode avant de traiter une requête et après que le résultat de la requête est revenu de BigQuery.
Ce code appelle la fonction fadeToggle
lorsque l'utilisateur a terminé de dessiner un rectangle.
drawingManager.addListener('rectanglecomplete', rectangle => {
//show an animation to indicate that something is happening.
fadeToggle(document.getElementById('spinner'));
rectangleQuery(rectangle.getBounds());
});
Une fois la réponse à la requête reçue, appelez à nouveau fadeToggle()
pour masquer l'image animée.
// 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 page doit ressembler à ceci.
Consultez l'exemple complet sur step8/map.html.
14. Points à prendre en compte
Trop de repères
Si vous travaillez avec des tables très volumineuses, votre requête risque de renvoyer trop de lignes pour qu'elles s'affichent correctement sur une carte. Limitez les résultats en ajoutant une clause WHERE
ou une instruction LIMIT
.
Dessiner de nombreux repères peut rendre la carte illisible. Utilisez un HeatmapLayer
pour afficher la densité ou des repères pour regrouper des points de données en utilisant un seul symbole par groupe. Vous trouverez plus d'informations dans notre tutoriel sur le regroupement de repères.
Optimiser les requêtes
BigQuery analyse la table entière à chaque requête. Pour optimiser votre utilisation du quota BigQuery, sélectionnez uniquement les colonnes dont vous avez besoin dans votre requête.
Les requêtes sont plus rapides si vous stockez la latitude et la longitude sous forme de nombres flottants plutôt que de chaînes.
Exporter les résultats intéressants
Dans ces exemples, l'utilisateur final doit être authentifié avec la table BigQuery, ce qui ne convient pas à tous les cas d'utilisation. Une fois que vous avez découvert des modèles intéressants, il peut être plus facile de les partager avec une audience plus large en exportant les résultats depuis BigQuery et en créant un ensemble de données statique à l'aide de la couche de données Google Maps.
Le bit juridique ennuyeux
N'oubliez pas les Conditions d'utilisation de Google Maps Platform. Pour en savoir plus sur les tarifs de Google Maps Platform, consultez la documentation en ligne.
Jouez avec plus de données !
Un certain nombre d'ensembles de données publics dans BigQuery sont associés à des colonnes de latitude et de longitude. Par exemple, les ensembles de données NYC Taxi de 2009-2016, les données de trajet Uber et Lyft NYC et l'ensemble de données GDELT.
15. Félicitations !
Nous espérons que cette fonctionnalité vous permettra de lancer rapidement des requêtes géographiques sur des tables BigQuery afin de découvrir des modèles et de les visualiser sur une carte Google Maps. Bon mappage !
Étape suivante
Si vous souhaitez en savoir plus sur Google Maps Platform ou BigQuery, consultez les suggestions suivantes.
Consultez Qu'est-ce que BigQuery ? pour en savoir plus sur le service d'entrepôt de données sans serveur à l'échelle du pétaoctet de Google.
Consultez le guide d'utilisation pour créer une application simple à l'aide de l'API BigQuery.
Pour en savoir plus sur la façon dont les utilisateurs peuvent interagir avec les formes à dessiner sur une carte Google Maps, consultez le guide du développeur de la bibliothèque de dessins.
Découvrez d'autres façons de visualiser les données sur Google Maps.
Consultez le guide de démarrage pour le point d'accès JavaScript pour comprendre les concepts de base de l'utilisation de l'API cliente afin d'accéder à d'autres API Google.