Interroger et visualiser des données de localisation dans BigQuery avec Google Maps Platform (JavaScript)

1. Présentation

Les cartes peuvent être un outil très puissant pour visualiser les tendances d'un ensemble de données liées à un emplacement. Cette relation peut être le nom d'un lieu, une valeur de latitude et de longitude spécifique, ou le nom d'une zone ayant une limite spécifique, comme un secteur de recensement ou un code postal.

Lorsque ces ensembles de données deviennent très volumineux, il peut être difficile de les interroger et de les visualiser à l'aide d'outils classiques. En utilisant Google BigQuery pour interroger les données et les API Google Maps pour construire la requête et visualiser le résultat, vous pouvez explorer rapidement les tendances géographiques de vos données avec très peu de configuration ou de codage, et sans avoir à 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 localisation dans de très grands ensembles de données publics à 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écuter et visualiser 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

  • Comment 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 à une page Web et permettre aux utilisateurs de dessiner des formes dessus
  • Comment visualiser des requêtes sur de grands ensembles de données sur une carte Google, comme dans l'exemple ci-dessous, qui montre la densité des lieux de dépose de taxis en 2016 pour les trajets ayant commencé dans le pâté de maisons autour de l'Empire State Building.

Screen Shot 2017-05-09 at 11.01.12 AM.png

Prérequis

  • Connaissances de base en HTML, CSS, JavaScript, SQL et des outils pour les développeurs Chrome
  • Un navigateur Web moderne, comme les dernières versions de Chrome, Firefox, Safari ou Edge.
  • Éditeur de texte ou IDE de votre choix

La technologie

BigQuery

BigQuery est le service d'analyse de données de Google pour les très grands ensembles de données. Elle dispose d'une API RESTful et accepte les requêtes écrites en SQL. Si vous disposez de données avec des valeurs de latitude et de longitude, vous pouvez les utiliser pour interroger vos données par emplacement. L'avantage est que vous pouvez explorer visuellement de très grands ensembles de données pour examiner les modèles sans avoir à gérer d'infrastructure de serveur ni de base de données. Grâce à l'énorme évolutivité et à l'infrastructure gérée de BigQuery, vous pouvez obtenir des réponses à vos questions en quelques secondes, quelle que soit la taille de vos tables.

Google Maps Platform

Google Maps Platform offre un accès programmatique aux données de cartes, de lieux et d'itinéraires de Google. Plus de deux millions de sites Web et d'applications l'utilisent actuellement pour fournir des cartes intégrées et des requêtes basées sur la localisation à leurs utilisateurs.

Le calque de dessin de l'API Google Maps Platform JavaScript vous permet de dessiner des formes sur la carte. Elles peuvent être converties en entrées pour exécuter des requêtes sur des tables BigQuery contenant des valeurs de latitude et de longitude stockées dans des colonnes.

Pour commencer, vous avez besoin d'un projet Google Cloud Platform avec les API BigQuery et Maps activées.

2. Configuration

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 l'écran, vous trouverez un menu déroulant "Projet" :

f2a353c3301dc649.png

Lorsque vous cliquez sur le menu déroulant du projet, un élément de menu vous permet de créer un projet :

56a42dfa7ac27a35.png

Dans la zone "Saisissez un nom pour votre projet", saisissez un nom pour votre nouveau projet, par exemple "Atelier de programmation BigQuery" :

Codelab - create project (1).png

Un ID de projet sera généré pour vous. L'ID du projet est un nom unique parmi tous les projets Google Cloud. Notez votre ID de projet, car vous en aurez besoin plus tard. Le nom ci-dessus est déjà pris. Vous devez en trouver un autre. Dans cet atelier de programmation, remplacez YOUR_PROJECT_ID par votre propre ID de projet.

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 pour ce projet. Une fois la facturation activée, vous pouvez activer l'API BigQuery.

Vous activez la facturation de différentes manières selon que vous créez un projet ou que vous réactivez la facturation pour un projet existant.

Google propose un essai sans frais de 12 mois pour une utilisation de Google Cloud Platform jusqu'à 300 $. Vous pourrez peut-être 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 ne possédez qu'un seul compte de facturation, ce compte 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 les nombreuses fonctionnalités de Google Cloud Platform. Pour créer un compte de facturation et activer la facturation sur votre projet, suivez les instructions de la section Créer un compte de facturation.

Projets existants

Si vous avez temporairement désactivé la facturation pour un projet, vous pouvez la réactiver :

  1. Accédez à la console Cloud Platform.
  2. Dans la liste des projets, sélectionnez le projet sur lequel vous souhaitez réactiver la facturation.
  3. Ouvrez le menu sur la gauche de la console, puis sélectionnez Facturation Facturation. Vous êtes invité à sélectionner un compte de facturation.
  4. Cliquez sur Définir le compte.

Créer un compte de facturation

Pour créer un compte de facturation :

  1. Accédez à la console Cloud Platform, puis connectez-vous ou, si vous ne possédez pas encore de compte, inscrivez-vous.
  2. Ouvrez le menu sur la gauche de la console, puis sélectionnez Facturation Facturation.
  3. 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 le nom de votre compte de facturation existant en haut de la page, puis sur Gérer les comptes de facturation.)
  4. 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 basés aux États-Unis, vous ne pouvez pas modifier le statut fiscal après avoir créé le compte.
  5. 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 des modes de paiement secondaires, 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 Marketplace de l'API BigQuery dans la console, puis cliquez sur le bouton bleu "Activer".

3. Interroger les données de localisation dans BigQuery

Il existe trois façons d'interroger les données de localisation stockées sous forme de valeurs de latitude et de longitude dans BigQuery.

  • Requêtes de rectangle : spécifiez la zone d'intérêt sous la forme d'une requête qui sélectionne toutes les lignes dans une plage de latitude et de longitude minimale et maximale.
  • 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 Haversine et des fonctions mathématiques pour modéliser la forme de la Terre.
  • Requêtes de polygones : spécifiez une forme personnalisée et utilisez une fonction définie par l'utilisateur pour exprimer la logique point dans polygone nécessaire pour tester si la latitude et la longitude de chaque ligne se trouvent à l'intérieur de la forme.

Pour commencer, utilisez l'éditeur de requête dans la section BigQuery de la console Google Cloud Platform pour 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 : l'ancien SQL et le SQL standard. La seconde est la norme ANSI 2011. Dans ce tutoriel, nous utiliserons le langage SQL standard, car il est plus conforme aux normes.

Si vous souhaitez exécuter l'ancien SQL dans l'éditeur BigQuery, procédez comme suit :

  1. Cliquez sur le bouton "Plus".
  2. Sélectionnez "Paramètres de la requête" dans le menu déroulant.
  3. Sous "Dialecte SQL", sélectionnez la case d'option "Ancien".
  4. Cliquez sur le bouton "Enregistrer".

Requêtes de rectangle

Il est assez simple de créer des requêtes rectangulaires dans BigQuery. Il vous suffit d'ajouter une clause WHERE qui limite les résultats renvoyés à ceux dont les coordonnées géographiques se situent entre les valeurs minimale et maximale de latitude et de longitude.

Essayez l'exemple ci-dessous dans la console BigQuery. Cette requête demande des statistiques moyennes sur les trajets ayant commencé dans une zone rectangulaire comprenant Midtown et Lower Manhattan. Vous pouvez essayer deux emplacements différents. Décommentez la deuxième clause WHERE pour exécuter la requête sur les trajets qui ont commencé à 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 des trajets, de tarifs et de pourboires pour les prises en charge dans les deux zones.

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

Requêtes par rayon

Il est également facile de créer des requêtes par rayon en SQL si vous avez quelques connaissances en mathématiques. En utilisant les fonctions mathématiques de l'ancien SQL de BigQuery, vous pouvez créer une requête SQL à l'aide de la formule de Haversine, qui permet d'approximer une zone circulaire ou une calotte sphérique à la surface de la Terre.

Voici un exemple d'instruction SQL BigQuery pour une requête de cercle centrée sur 40.73943, -73.99585 avec un rayon de 0,1 km.

Il utilise une valeur constante de 111,045 kilomètres pour approximer la distance représentée par un degré.

Cet exemple est basé sur celui disponible à l'adresse 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 

Le code SQL de la formule de Haversine semble compliqué, mais il vous suffit d'insérer les coordonnées du centre de votre cercle, le rayon, ainsi que les noms du projet, de l'ensemble de données et de la table pour BigQuery.

Voici un exemple de requête qui calcule des statistiques moyennes sur les trajets pour les prises en charge à moins de 100 m de l'Empire State Building. Copiez et collez cette requête dans la console Web BigQuery pour afficher les résultats. Modifiez la latitude et la longitude pour comparer avec d'autres zones, comme celle du 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 qu'il existe de grandes différences en termes de pourboire moyen, de course, de distance parcourue, de proportion du pourboire par rapport au prix de la course et de prix moyen par kilomètre parcouru.

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

En Seine-Saint-Denis

avg_tip

avg_fare

avg_distance

avg_tip_pc

avg_fare_mile

0,52

17,63

4,75

4.74

10.9

Requêtes de polygones

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 géométriques ni d'aucun index spatial natifs. Par conséquent, pour exécuter des requêtes à l'aide de formes polygonales, vous devez adopter une approche différente des 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.

De nombreuses opérations de géométrie peuvent être écrites en JavaScript. Il est donc facile d'en prendre une et de 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 ne renvoyant que les lignes où la latitude et la longitude se trouvent à l'intérieur du polygone. En savoir plus sur les UDF dans la documentation de référence BigQuery

Algorithme Point In Polygon

Il existe de nombreuses façons de déterminer si un point se trouve à l'intérieur d'un polygone en JavaScript. Voici un portage depuis C d'une implémentation bien connue qui utilise un algorithme de lancer de rayon 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 infiniment longue croise la limite de la forme. Cela 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;
}

Portage 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 l'UDF doit être définie comme 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 maintenant exécuté trois types de requêtes spatiales à l'aide de BigQuery. Comme vous l'avez vu, la localisation a une grande incidence sur les données de résultats des requêtes exécutées sur cet ensemble de données. Toutefois, à moins de deviner où exécuter vos requêtes, il est difficile de découvrir des tendances spatiales de manière ad hoc en utilisant uniquement des requêtes SQL.

Si seulement nous pouvions visualiser les données sur une carte et les explorer en définissant des zones d'intérêt arbitraires ! Eh bien, c'est exactement ce que vous pouvez faire avec les API Google Maps. Vous devez d'abord activer l'API Maps, configurer une page Web simple exécutée sur votre machine locale et commencer à utiliser l'API BigQuery pour envoyer des requêtes depuis votre page Web.

4. Utiliser les API Google Maps

Après avoir exécuté des requêtes spatiales simples, l'étape suivante consiste à visualiser la sortie pour identifier les tendances. Pour ce faire, vous allez activer l'API Maps, créer une page Web qui envoie des requêtes d'une carte à BigQuery, puis dessiner 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 :

  1. Dans la console Google Cloud Platform, accédez à Marketplace.
  2. Dans le Marketplace, recherchez "Maps JavaScript API".
  3. Cliquez sur la tuile de l'API Maps JavaScript dans les résultats de recherche.
  4. Cliquez sur le bouton "Activer".

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 les requêtes. Pour générer une clé API :

  1. Dans la console Google Cloud Platform, cliquez sur le menu hamburger pour ouvrir le panneau de navigation de gauche.
  2. Sélectionnez API et services > Identifiants.
  3. Cliquez sur le bouton "Create Credential" (Créer des identifiants), puis sélectionnez "API Key" (Clé API).
  4. 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é. Cette action décompresse le 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 work.

Configurer un serveur Web local

Bien que vous soyez libre d'utiliser votre propre serveur Web, cet atelier de programmation est conçu pour fonctionner correctement avec le serveur Web Chrome. Si l'application n'est pas encore installée, vous pouvez le faire depuis le Chrome Web Store.

Une fois l'application installée, ouvrez-la. Dans Chrome, vous pouvez procéder comme suit :

  1. Ouvrez Chrome.
  2. Dans la barre d'adresse en haut de la page, saisissez chrome://apps.
  3. Appuyez sur "Entrée".
  4. 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 normal ou épinglé, en plein écran ou dans une nouvelle fenêtre a3ed00e79b8bfee7.png. La boîte de dialogue suivante s'affiche. Elle vous permet de configurer votre serveur Web local : 81b6151c3f60c948.png
  5. Cliquez sur "CHOISIR UN DOSSIER" et sélectionnez l'emplacement où vous avez téléchargé les fichiers exemples du codelab.
  6. Dans la section "Options", cochez la case "Afficher automatiquement l'index.html" : 17f4913500faa86f.png
  7. Faites glisser le bouton "Serveur Web : DÉMARRÉ" vers la gauche, puis de nouveau vers la droite pour arrêter et redémarrer le serveur Web.

a5d554d0d4a91851.png

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 à l'aide de l'API Maps JavaScript et de quelques lignes de code JavaScript. Le code de l'exemple de carte simple de Google Maps Platform constitue un bon point de départ. Il est reproduit ici pour que vous puissiez le copier et le coller dans l'éditeur de texte ou l'IDE de votre choix. Vous pouvez également le trouver en ouvrant index.html à partir du dépôt que vous avez téléchargé.

  1. Copiez index.html dans le dossier work de votre copie locale du dépôt.
  2. Copiez le dossier img/ dans le dossier work/ de votre copie locale du dépôt.
  3. Ouvrez work/index.html dans votre éditeur de texte ou IDE.
  4. 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>
  1. 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 est 8887. Vos premières cartes devraient s'afficher.

Si un message d'erreur s'affiche dans le navigateur, vérifiez que votre clé API est correcte et que votre serveur Web local est actif.

Modifier l'emplacement et le niveau de zoom par défaut

Le code qui définit l'emplacement et le niveau de zoom se trouve aux lignes 27 et 28 d'index.html. 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 fonctionne avec les données BigQuery sur les courses de taxi à New York. Vous allez donc modifier le code d'initialisation de la carte pour la centrer sur un lieu à New York, avec un niveau de zoom approprié (13 ou 14 devrait convenir).

Pour ce faire, remplacez le bloc de code ci-dessus par le code suivant afin de centrer la carte sur l'Empire State Building et d'ajuster 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 dessin et de visualisation

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 qui indique à Google Maps Platform d'activer la bibliothèque Drawing.

Cet atelier de programmation utilise également HeatmapLayer. Vous allez donc également mettre à jour le script pour demander la bibliothèque de visualisation. Pour ce faire, ajoutez le paramètre libraries et spécifiez les bibliothèques visualization et drawing sous forme de valeurs séparées par une virgule, par exemple libraries=visualization,drawing.

Elle devrait 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 DrawingManager

Pour utiliser des formes dessinées par l'utilisateur comme entrée d'une requête, ajoutez DrawingManager à votre carte, avec les outils Circle, Rectangle et Polygon activés.

Il est judicieux de placer tout le code de configuration DrawingManager dans une nouvelle fonction. Dans votre copie de index.html, procédez comme suit :

  1. Ajoutez une fonction nommée setUpDrawingTools() avec le code suivant pour créer le DrawingManager et définir sa propriété map afin de référencer l'objet de carte sur la page.

Les options transmises à google.maps.drawing.DrawingManager(options) définissent le type de dessin de forme 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, les formes doivent avoir une opacité nulle. Pour en savoir plus sur les options disponibles, consultez Options DrawingManager.

function setUpDrawingTools() {
  // Initialize drawing manager
  drawingManager = new google.maps.drawing.DrawingManager({
    drawingMode: google.maps.drawing.OverlayType.CIRCLE,
    drawingControl: true,
    drawingControlOptions: {
      position: google.maps.ControlPosition.TOP_LEFT,
      drawingModes: [
        google.maps.drawing.OverlayType.CIRCLE,
        google.maps.drawing.OverlayType.POLYGON,
        google.maps.drawing.OverlayType.RECTANGLE
      ]
    },
    circleOptions: {
      fillOpacity: 0
    },
    polygonOptions: {
      fillOpacity: 0
    },
    rectangleOptions: {
      fillOpacity: 0
    }
  });
  drawingManager.setMap(map);
}
  1. Appelez setUpDrawingTools() dans votre fonction initMap() 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();
}
  1. Rechargez 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 et faire glisser pour dessiner des cercles et des rectangles, mais les polygones doivent être dessinés en cliquant sur chaque sommet, puis en double-cliquant pour terminer la forme.

Gérer les événements de dessin

Vous avez besoin de code pour gérer les événements déclenchés lorsqu'un utilisateur termine de dessiner une forme, tout comme vous avez besoin des coordonnées des formes dessinées pour construire des requêtes SQL.

Nous ajouterons le code correspondant dans une étape ultérieure, mais pour l'instant, nous allons créer trois gestionnaires d'événements vides pour gérer les événements rectanglecomplete, circlecomplete et polygoncomplete. Les gestionnaires n'ont pas besoin d'exécuter de code à ce stade.

Ajoutez le code suivant en bas de votre 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 fonctionnel de ce code dans votre copie locale du dépôt, dans le dossier step2 : step2/map.html.

6. Utiliser l'API BigQuery Client

L'API cliente Google BigQuery vous aidera à éviter d'écrire de nombreuses lignes de code passe-partout nécessaires pour créer les 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 dans une page Web et l'utiliser pour interagir avec BigQuery.

Ajouter l'API Google Client pour JavaScript

Vous utiliserez l'API cliente Google pour JavaScript afin d'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> comme celle-ci. Placez le tag immédiatement sous le tag <script> qui charge l'API Maps :

<script src='https://apis.google.com/js/client.js'></script>

Après avoir chargé l'API Client 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 de la console Google Cloud.

Créer des identifiants OAuth 2.0

  1. Dans le menu de navigation de la console Google Cloud, sélectionnez API et services > Identifiants.

Avant de pouvoir configurer 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 d'autorisation OAuth. 2. Vous devez ajouter l'API BigQuery aux niveaux d'accès de ce jeton. Cliquez sur le bouton Ajouter un champ d'application dans la section "Champs d'application pour les API Google". 3. Dans la liste, cochez la case à côté de 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 enregistrer vos paramètres. 7. Vous allez ensuite créer votre ID client OAuth. Pour ce faire, cliquez sur Créer des identifiants :

4d18a965fc760e39.png

  1. Dans le menu déroulant, cliquez sur ID client OAuth. 1f8b36a1c27c75f0.png
  2. Sous "Type d'application", sélectionnez Application Web.
  3. Dans le champ "Nom de l'application", saisissez le nom de votre projet. Par exemple, "BigQuery et Maps".
  4. Sous Restrictions, dans le champ "Origines JavaScript autorisées", saisissez l'URL de localhost, y compris les numéros de port. Par exemple : http://localhost:8887
  1. Cliquez sur le bouton Créer.

Une fenêtre pop-up affiche l'ID client et le code secret du client. Vous avez besoin de l'ID client pour vous authentifier 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 devra 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 sur l'autorisation de la documentation de l'API JavaScript Client. Vous devez utiliser l'ID client OAuth et l'ID de votre projet pour envoyer des requêtes.

Lorsque l'API Google Client est chargée sur la page Web, vous devez procéder comme suit :

  • Autorisez l'utilisateur.
  • Si vous y êtes autorisé, chargez l'API BigQuery.
  • Chargez et initialisez la carte.

Consultez step3/map.html pour voir à quoi ressemblera la page HTML finale.

Autoriser l'utilisateur

L'utilisateur final de l'application doit autoriser l'application à accéder aux données dans BigQuery en son nom. L'API Client Google pour JavaScript gère la logique OAuth pour ce faire.

Dans une application réelle, vous avez le choix entre de nombreuses façons d'intégrer l'étape d'autorisation.

Par exemple, vous pouvez appeler authorize() à partir d'un élément d'interface utilisateur tel qu'un bouton, ou le faire lorsque la page est chargée. Ici, nous avons choisi d'autoriser l'utilisateur après le chargement de l'API Google Client pour JavaScript, en utilisant une fonction de rappel dans la méthode gapi.load().

Écrivez du code immédiatement après la balise <script> qui charge l'API Google Client pour JavaScript afin de charger à la fois la bibliothèque cliente et le module d'authentification, ce qui nous permettra d'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>

Charger l'API BigQuery lors de l'autorisation

Une fois l'utilisateur autorisé, 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 contrôle si un pop-up s'affiche pour l'utilisateur. Définissez-le sur true pour supprimer le pop-up d'autorisation si l'utilisateur est déjà autorisé.

Ajoutez une fonction à votre page appelée handleAuthResult(). La fonction doit accepter un paramètre authresult, qui vous permettra de contrôler le flux logique selon que l'utilisateur a été autorisé ou non.

Ajoutez également une fonction appelée loadApi pour charger l'API BigQuery si l'utilisateur est correctement autorisé.

Ajoutez une logique dans la fonction handleAuthResult() pour appeler loadApi() s'il existe un objet authResult transmis à la fonction et si la propriété error de l'objet a une valeur de 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. Actuellement, il s'initialise lorsque l'API Maps JavaScript est chargée.

Pour ce faire, appelez la fonction initMap() à partir 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 à l'API BigQuery s'exécutent généralement en quelques secondes, mais il est possible qu'ils ne renvoient pas de réponse immédiatement. Vous avez besoin d'une logique pour interroger BigQuery afin de connaître l'état des tâches de longue durée et de n'extraire les résultats que lorsque la tâche est terminée.

Le code complet de cette étape se trouve dans 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 des variables pour stocker les valeurs de l'ensemble de données et du projet BigQuery contenant la table à interroger, et l'ID du projet auquel les frais seront facturé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'un job

La fonction checkJobStatus ci-dessous montre comment vérifier périodiquement l'état d'un job à l'aide de la méthode d'API get et de l'ID jobId renvoyé par la requête "query" d'origine. Voici un exemple qui s'exécute toutes les 500 millisecondes jusqu'à ce que le job soit terminé.

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 du job à checkJobStatus. Il est exposé par l'objet de réponse en tant que 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));
}

Obtenir les résultats d'une requête

Pour obtenir les résultats d'une requête une fois son exécution terminée, utilisez l'appel d'API jobs.getQueryResults. Ajoutez une fonction à votre page 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 avec l'API BigQuery

Il existe trois façons d'utiliser SQL pour exécuter des requêtes spatiales sur des données dans BigQuery :

La section "Fonctions mathématiques" de la documentation de référence sur l'ancien SQL de BigQuery, sous "Exemples avancés", contient des exemples de requêtes basées sur un rectangle englobant ou sur le rayon d'un cercle.

Pour les requêtes de zone de sélection et de rayon, vous pouvez appeler la méthode query de l'API BigQuery. Créez le code SQL pour chaque requête et transmettez-le à la fonction sendQuery que vous avez créée à l'étape précédente.

Un exemple de code fonctionnel pour cette étape est disponible dans step4/map.html.

Requêtes de rectangle

La façon la plus simple d'afficher des données BigQuery sur une carte consiste à demander toutes les lignes dont la latitude et la longitude se trouvent dans un rectangle, en utilisant une comparaison "inférieur à" et "supérieur à". Il peut s'agir de la vue de carte 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 afin de gérer l'événement de dessin déclenché lorsqu'un rectangle est terminé. Dans cet exemple, le code utilise getBounds() sur l'objet rectangle pour obtenir un objet représentant l'étendue du rectangle dans les coordonnées de la carte, et le transmet à 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 effectuer une comparaison inférieure/supérieure à chaque ligne de votre table BigQuery. Voici un exemple de requête qui interroge une table comportant des colonnes appelées 'pickup_latitude' et 'pickup_longitude', qui stockent les valeurs de localisation.

Spécifier la table BigQuery

Pour interroger une table à l'aide de l'API BigQuery, vous devez fournir le nom complet de la table dans votre requête SQL. Le format en SQL standard est project.dataset.tablename. Dans l'ancien SQL, il s'agit de project.dataset.tablename.

De nombreux tableaux de courses en taxi à New York sont disponibles. 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 et développez-le pour afficher les tables. Choisissez la table "Yellow Taxi trips" (Courses de taxis jaunes) : 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 travailliez avec une table que vous aviez créée dans votre propre projet en important des données, cet ID de projet serait identique à celui de votre instruction SQL.

Ajoutez des variables JavaScript à votre code pour conserver les références au projet "Ensembles de données publics" contenant la table que vous interrogez, ainsi que le nom de la table et de l'ensemble de données. Vous avez également besoin d'une variable distincte pour faire référence à votre propre ID de projet de facturation.

Ajoutez des variables JavaScript globales appelées billingProjectId, publicProjectId, datasetId et tableName à votre copie de index.html.

Initialisez les variables 'publicProjectId', 'datasetId' et 'tableName' avec les informations du projet "Ensembles de données publics BigQuery". Initialisez billingProjectId avec votre propre ID de projet (celui que vous avez créé dans la section "Configuration" 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 disposez de suffisamment de code pour envoyer une requête à BigQuery pour toutes les lignes contenues dans un rectangle dessiné par l'utilisateur. Avant d'ajouter d'autres méthodes de requête pour les cercles et les formes à main levée, examinons comment gérer les données renvoyées par une requête.

10. Visualiser la réponse

Les tables BigQuery peuvent être très volumineuses (pétaoctets de données) et s'enrichir de 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 affichées sur la carte. Si vous dessinez l'emplacement de chaque ligne dans un ensemble de résultats très volumineux (des dizaines de milliers de lignes ou plus), la carte sera illisible. Il existe de nombreuses techniques pour agréger les emplacements dans la requête SQL et sur la carte. Vous pouvez également limiter les résultats renvoyés par une requête.

Le code complet de cette étape est disponible dans step5/map.html.

Pour que la quantité de données transférées vers votre page Web reste raisonnable pour cet atelier de programmation, modifiez la fonction rectangleSQL() afin d'ajouter une instruction qui limite la réponse à 10 000 lignes. Dans l'exemple ci-dessous, cette valeur est spécifiée dans une variable globale appelée recordLimit, afin 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é des lieux, vous pouvez utiliser une carte de densité. L'API Maps JavaScript dispose d'une classe HeatmapLayer à cet effet. Le HeatmapLayer prend 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 aura une propriété appelée f, qui est un tableau de colonnes. Chaque colonne aura une propriété v contenant la valeur.

Votre code doit parcourir les colonnes de chaque ligne et extraire les valeurs.

Dans la requête SQL, vous n'avez demandé que les valeurs de latitude et de longitude des lieux de prise en charge des taxis. La réponse ne comportera donc que deux colonnes.

N'oubliez pas d'appeler setMap() sur le calque de carte de densité lorsque vous lui avez attribué le tableau de positions. Elle sera alors visible 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 être en mesure de :

  • Ouvrir la page et autoriser l'accès à BigQuery
  • Dessiner un rectangle quelque part à New York
  • Visualisez les résultats de la requête sous forme de carte de densité.

Voici un exemple de résultat d'une requête rectangulaire sur les données des taxis jaunes de New York en 2016, représenté sous forme de carte de densité. Voici la répartition des prises en charge autour de l'Empire State Building un samedi de juillet :

7b1face0e7c71c78.png

11. Interroger par rayon autour d'un point

Les requêtes par rayon sont très similaires. À l'aide des fonctions mathématiques de l'ancien SQL de BigQuery, vous pouvez créer une requête SQL à l'aide de la formule de Haversine, qui permet d'approximer une zone circulaire à la surface de la Terre.

En utilisant 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 code SQL pour la requête de la même manière.

Un exemple fonctionnel du code pour cette étape est inclus dans le dépôt de code sous step6/map.html.

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

Dans votre copie d'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() appellera haversineSQL() pour construire le code SQL de la requête, puis enverra la requête en appelant la fonction sendQuery(), comme dans 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;
}

Essayez !

Ajoutez le code ci-dessus et essayez l'outil "Cercle" pour sélectionner une zone de la carte. Le résultat doit ressembler à ceci :

845418166b7cc7a3.png

12. Interroger des formes arbitraires

Pour récapituler, SQL ne permet pas d'interroger des formes arbitraires autres que des rectangles et des cercles. BigQuery ne dispose d'aucun type de données géométriques natif. Par conséquent, pour exécuter des requêtes à l'aide de formes polygonales, vous devez adopter une approche différente des requêtes SQL simples.

Les fonctions définies par l'utilisateur (UDF) sont une fonctionnalité très puissante de BigQuery qui peut être utilisée à cette fin. Les UDF exécutent du code JavaScript dans une requête SQL.

Le code fonctionnel de cette étape se trouve dans step7/map.html.

Fonctions définies par l'utilisateur dans l'API BigQuery

L'approche de l'API BigQuery pour les fonctions définies par l'utilisateur est légèrement différente de celle de la console Web : vous devrez appeler jobs.insert method.

Pour les requêtes SQL standards 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 montre une fonction qui crée et envoie un objet de requête pour insérer un nouveau job, en l'occurrence une requête avec une fonction définie par l'utilisateur.

Vous trouverez un exemple fonctionnel 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 est construite 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;
}

Deux choses se passent ici. Tout d'abord, le code crée l'instruction CREATE TEMPORARY FUNCTION qui encapsule le code JavaScript permettant de déterminer si un point donné se trouve à l'intérieur d'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 code SQL.

Ensuite, le code crée l'instruction SQL SELECT principale. Dans cet exemple, la fonction définie par l'utilisateur est appelée dans l'expression WHERE.

Intégrer l'API Maps

Pour l'utiliser avec la bibliothèque de dessin de l'API Maps, nous devons enregistrer le polygone dessiné par l'utilisateur et le transmettre à la partie UDF de la requête SQL.

Tout d'abord, nous devons gérer l'événement de dessin polygoncomplete pour obtenir les coordonnées de la forme sous forme de tableau de paires de longitude et de 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 construire les fonctions JavaScript UDF sous forme de chaîne, ainsi que l'instruction SQL qui appellera la fonction UDF.

Pour obtenir un exemple fonctionnel, consultez step7/map.html.

Exemple de résultat :

Voici un exemple de résultat de requête sur les lieux de prise en charge à partir des données 2016 des taxis jaunes TLC de New York dans BigQuery à l'aide d'un polygone à main levée, avec les données sélectionnées représentées sous forme de carte de densité.

Screen Shot 2017-05-09 at 10.00.48 AM.png

13. Aller plus loin

Voici quelques suggestions pour étendre cet atelier de programmation et examiner d'autres aspects des données. Vous trouverez un exemple fonctionnel de ces idées dans step8/map.html dans le dépôt de code.

Mappage des points de dépose

Jusqu'à présent, nous n'avons cartographié que les points de retrait. En demandant les colonnes dropoff_latitude et dropoff_longitude, et en modifiant le code de la carte de densité pour les représenter à la place, vous pouvez voir les destinations des trajets en taxi qui ont commencé à un endroit spécifique.

Par exemple, voyons où les taxis ont tendance à déposer les passagers qui demandent à être pris en charge près de l'Empire State Building.

Modifiez le code de l'instruction SQL dans polygonSql() pour demander ces colonnes en plus du lieu de prise en charge.

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 alors utiliser les valeurs de dépose. L'objet de résultat possède un schéma qui peut être inspecté pour trouver l'emplacement de ces colonnes dans le tableau. Dans ce cas, ils se trouveraient aux positions d'index 2 et 3. Ces index peuvent être lus à partir d'une variable pour rendre le code plus facile à gérer. Notez que la maxIntensity de la carte de densité est définie pour afficher une densité maximale de 20 points de dépose par pixel.

Ajoutez des variables pour pouvoir 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 points de dépose de toutes les prises en charge à proximité de l'Empire State Building en 2016. Vous pouvez voir de fortes concentrations (les blobs rouges) de destinations du centre-ville, en particulier autour de Times Square, ainsi que le long de la 5e Avenue entre la 23e Rue et la 14e Rue. D'autres lieux à forte densité non visibles à ce niveau de zoom incluent les aéroports de La Guardia et JFK, le World Trade Center et Battery Park.

Screen Shot 2017-05-09 at 10.40.01 AM.png

Appliquer un style à la carte de base

Lorsque vous créez une carte Google à l'aide de l'API Maps JavaScript, vous pouvez définir le style de la carte à l'aide d'un objet JSON. Pour les visualisations de données, il peut être utile de désactiver 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 à tout moment par la suite. 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 niveaux 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 un message à l'utilisateur

Même si BigQuery fournit généralement une réponse en quelques secondes, il est parfois utile de montrer à l'utilisateur que quelque chose se passe pendant l'exécution de la requête.

Ajoutez à votre page Web une interface utilisateur qui affiche la réponse de la fonction checkJobStatus(), ainsi qu'un graphique animé indiquant que la requête est en cours.

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 qui affichera le nombre de lignes renvoyées par une requête, le temps nécessaire à l'exécution 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 positionner le panneau en haut à gauche de la page, sous les boutons de type de carte et la barre d'outils de 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;
}

Le graphique animé peut être ajouté à la page, mais masqué jusqu'à ce qu'il soit nécessaire. Du code JavaScript et CSS peut être utilisé pour l'afficher lorsqu'un job BigQuery est en cours d'exécution.

Ajoutez du code HTML pour afficher un graphique animé. Un fichier image nommé loader.gif se trouve dans le dossier img du dépôt de code.

<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 un job en cours, vous pouvez utiliser la propriété response.statistics. Une fois le job terminé, vous pouvez accéder aux propriétés response.totalRows et response.totalBytesProcessed. Il est utile pour l'utilisateur de convertir les millisecondes en secondes et les octets en gigaoctets pour l'affichage, comme indiqué 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 lorsqu'il y a une 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 ou désactiver le graphique animé, ajoutez une fonction permettant de contrôler sa visibilité. Cette fonction permet d'activer ou de désactiver l'opacité de n'importe quel élément HTML DOM qui lui est 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 une fois que le résultat de la requête a été renvoyé par 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 le graphique animé.

// 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 devrait ressembler à ceci.

Screen Shot 2017-05-10 at 2.32.19 PM.png

Consultez l'exemple complet dans step8/map.html.

14. Éléments à prendre en compte

Trop de repères

Si vous utilisez de très grandes tables, il est possible que votre requête renvoie trop de lignes pour permettre un affichage efficace 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. Envisagez d'utiliser un HeatmapLayer pour afficher la densité ou de regrouper les repères pour indiquer où se trouvent de nombreux points de données à l'aide d'un seul symbole par cluster. Pour en savoir plus, consultez notre tutoriel sur le clustering de marqueurs.

Optimiser les requêtes

BigQuery analysera l'ensemble de la table avec chaque requête. Pour optimiser l'utilisation de votre quota BigQuery, sélectionnez uniquement les colonnes dont vous avez besoin dans votre requête.

Les requêtes seront plus rapides si vous stockez la latitude et la longitude sous forme de valeurs flottantes plutôt que de chaînes.

Exporter les résultats intéressants

Les exemples présentés ici nécessitent que l'utilisateur final soit authentifié auprès de la table BigQuery, ce qui ne convient pas à tous les cas d'utilisation. Une fois que vous avez identifié des tendances intéressantes, il peut être plus facile de les partager avec un public plus large en exportant les résultats de BigQuery et en créant un ensemble de données statiques à l'aide de la couche de données Google Maps.

N'oubliez pas de consulter les Conditions d'utilisation de Google Maps Platform. Pour en savoir plus sur la tarification de Google Maps Platform, consultez la documentation en ligne.

Jouez avec plus de données !

BigQuery contient de nombreux ensembles de données publics avec des colonnes de latitude et de longitude, par exemple les ensembles de données sur les taxis new-yorkais de 2009 à 2016, les données sur les trajets Uber et Lyft à New York et l'ensemble de données GDELT.

15. Félicitations !

Nous espérons que cela vous aidera à vous lancer rapidement avec des requêtes géographiques sur des tables BigQuery afin de découvrir des tendances et de les visualiser sur une carte Google. Bon mappage !

Étape suivante

Si vous souhaitez en savoir plus sur Google Maps Platform ou BigQuery, consultez les suggestions suivantes.

Pour en savoir plus sur le service d'entrepôt de données sans serveur à l'échelle du pétaoctet de Google, consultez Qu'est-ce que BigQuery ?.

Consultez le guide pratique pour créer une application simple à l'aide de l'API BigQuery.

Pour en savoir plus sur l'activation de l'interaction utilisateur permettant de dessiner des formes sur une carte Google, consultez le guide du développeur pour la bibliothèque de dessins.

Découvrez d'autres façons de visualiser des données sur une carte Google.

Consultez le guide de démarrage de l'API cliente JavaScript pour comprendre les concepts de base de l'utilisation de l'API cliente pour accéder à d'autres API Google.