Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
132 changes: 132 additions & 0 deletions places_insights/places-insights-demo/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
# Places Insights Demo Application

This is a client-side web application that demonstrates how to query and visualize Google Places Insights data. It allows users to define geographic search areas, apply a variety of filters, and see aggregated results from Google BigQuery displayed on a Google Map.

The application is built entirely with HTML, CSS, and JavaScript, using the Google Maps Platform JavaScript API for mapping, Deck.gl for H3 heatmap visualizations, and Google Identity Services for client-side OAuth 2.0 authentication.

---

## Prerequisites

Before you can run this application, you must have a Google Cloud project with billing enabled and ensure the following are set up:

### 1. Places Insights Subscription

This demo queries the Places Insights datasets in BigQuery. You must subscribe to these datasets in the Google Cloud Marketplace for the countries you wish to query.

* **Action:** Visit the [Places Insights product page](https://developers.google.com/maps/documentation/placesinsights) and follow the instructions to subscribe to the datasets for your project.

### 2. Enabled APIs

Ensure the following APIs are enabled in your Google Cloud project:

* **BigQuery API** (for running the queries)
* **Maps JavaScript API** (for displaying the map)
* **Geocoding API** (for centering the map on countries/regions)
* **Routes API** (for the "Route Search" feature)
* **Places API (New)** (for the Place Autocomplete web components and Place Details)
* **Places UI Kit API** (Required for the `<gmp-place-details-compact>` component used in the H3 Function demo)

### 3. IAM Permissions

The Google account you authorize with must have the appropriate IAM permissions in your Cloud project to run BigQuery jobs. At a minimum, this typically includes:

* `BigQuery Job User`
* `BigQuery Resource Viewer`

---

## Setup

Follow these steps to configure and run the application locally.

### 1. Create an OAuth 2.0 Client ID

This application uses client-side OAuth 2.0 to authorize users to run BigQuery queries on their own behalf.

1. In the Google Cloud Console, navigate to **APIs & Services > Credentials**.
2. Click **+ CREATE CREDENTIALS** and select **OAuth client ID**.
3. For "Application type", select **Web application**.
4. Give it a name (e.g., "Places Insights Demo").
5. Under **Authorized JavaScript origins**, click **+ ADD URI**.
6. Enter the origin for your local development server. For most servers, this is `http://localhost:8000`.
7. Click **CREATE**.
8. Copy the **Your Client ID** value. You will need this in the next step.

### 2. Create a Google Maps Platform API Key

1. On the same **Credentials** page, click **+ CREATE CREDENTIALS** and select **API key**.
2. Copy the generated API key.
3. **Important:** For security in a production environment, you should restrict this key to your website's domain and ensure it only has access to the APIs listed in the Prerequisites section.

### 3. Configure the Application

1. In the project directory, find the file named `config.js.template` (or create a new `config.js` file).
2. **Rename** this file to `config.js`.
3. Open `config.js` and fill in the placeholder values with your project-specific credentials:

```javascript
window.APP_CONFIG = {
// Your Google Cloud Project ID
GCP_PROJECT_ID: 'YOUR_GCP_PROJECT_ID',

// The OAuth 2.0 Client ID you created in Step 1
OAUTH_CLIENT_ID: 'YOUR_OAUTH_CLIENT_ID.apps.googleusercontent.com',

// The Google Maps Platform API Key you created in Step 2
MAPS_API_KEY: 'YOUR_MAPS_API_KEY',

/**
* Defines which dataset to query.
* Options: 'FULL' or 'SAMPLE'
* - 'FULL': Queries the full country datasets (e.g., places_insights___us.places).
* - 'SAMPLE': Queries the city sample datasets (e.g., places_insights___us___sample.places_sample).
*/
DATASET: 'FULL',
};
```

4. **Dataset Selection:** Set the `DATASET` parameter to `'SAMPLE'` if you are using the [sample datasets](https://developers.google.com/maps/documentation/placesinsights/cloud-setup#sample_data), or `'FULL'` if you have subscribed to the [full datasets](https://developers.google.com/maps/documentation/placesinsights/cloud-setup#full_data).
5. **Security Note:** The `config.js` file **must not be committed to version control**.

### 4. Run a Local Server

Because of browser security policies related to OAuth 2.0, you cannot run this application by opening the `index.html` file directly. You must serve it from a local web server.

1. Open a terminal in the project's root directory.
2. If you have Python 3 installed, you can run a simple server with the command:
```sh
python3 -m http.server 8000
```
3. Open your web browser and navigate to `http://localhost:8000`.

---

## How to Use the Application

For a detailed walkthrough, click the **Help** button in the application's sidebar.

### Quick Start

1. **Select a Location:** Choose a country or city you have subscribed to in Places Insights.
2. **Authorize:** Sign in with your Google account to enable querying.
3. **Choose a Demo Type:**
* **Circle Search:** Click on the map to define a search radius.
* **Polygon Search:** Draw a custom shape on the map or paste a WKT string.
* **Region Search:** Search by administrative names like "London" or "California". You can add multiple regions to search at once.
* **Route Search:** Select an origin and destination to search along a calculated driving route.
* **Places Count Per H3 (Function):** Uses server-side BigQuery functions for high-performance density mapping. This mode supports **low counts (0-4)** and **sample place markers**.
4. **Apply Filters:** Narrow your search using the collapsible filter sections:
* **Place Types:** Select types and optionally check **"Match Primary Type Only"** for stricter filtering.
* **Attributes:** Filter by Rating, Business Status (Operational/Closed), Price, etc.
* **Opening Hours:** Filter by day and time (Standard modes only).
* **Brands:** Filter by brand name or category (US Standard mode only).
5. **Visualize:**
* Leave **Show H3 Density Map** unchecked for simple aggregate counts (Standard modes only).
* Check the box to visualize the results as a color-coded heatmap of hexagonal cells.
6. **Run Search:** Click the "Run Search" button to execute the query and see the results on the map.

### Interactive Features (H3 Function Mode)
When running the **Places Count Per H3** demo:
1. **Click a Hexagon:** Click on any colored H3 cell on the map. This will load up to 20 sample markers for places within that cell.
2. **View Details:** Click on any of the yellow markers to open a **Place Details Card** containing rich information (photos, reviews, opening hours) powered by the Places UI Kit.
84 changes: 84 additions & 0 deletions places_insights/places-insights-demo/auth.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
// Copyright 2026 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// --- AUTHENTICATION ---

/**
* Initializes the Google Identity Services client. This is called once on page load.
*/
function initializeIdentityServices() {
if (!OAUTH_CLIENT_ID || !GCP_PROJECT_ID) {
updateStatus('Configuration missing. Please set up config.js.', 'error');
return;
}
if (typeof google === 'undefined' || !google.accounts) {
updateStatus('Google Identity Services failed to load.', 'error');
console.error('GSI client library not loaded or initialized.');
return;
}
try {
// Initialize the token client for handling OAuth 2.0 flow.
tokenClient = google.accounts.oauth2.initTokenClient({
client_id: OAUTH_CLIENT_ID,
scope: BQ_SCOPES,
callback: () => {}, // Callback is handled by the promise in ensureAccessToken.
});
} catch (error) {
updateStatus('Could not initialize Google Identity Services.', 'error');
console.error(error);
}
}

/**
* Handles clicks on the main authorization button (sign-in or sign-out).
*/
function handleAuthClick() {
if (userSignedIn) {
// If the user is signed in, revoke the token and sign out.
if (accessToken) {
google.accounts.oauth2.revoke(accessToken, () => {});
}
accessToken = null;
resetSignedInUi();
} else {
// If the user is signed out, start the sign-in process.
ensureAccessToken({ prompt: 'consent' });
}
}

/**
* Gets a valid access token, prompting the user for consent if necessary.
* @param {object} options - Options for the token request (e.g., { prompt: 'consent' }).
* @returns {Promise<string>} A promise that resolves with the access token.
*/
function ensureAccessToken(options = { prompt: '' }) {
if (accessToken) return Promise.resolve(accessToken);

return new Promise((resolve, reject) => {
if (!tokenClient) {
return reject(new Error('Identity client not initialized.'));
}
// Define the callback for the token request.
tokenClient.callback = (tokenResponse) => {
if (tokenResponse.error) {
resetSignedInUi();
return reject(new Error(tokenResponse.error_description || 'Authorization failed.'));
}
accessToken = tokenResponse.access_token;
setSignedInUi();
resolve(accessToken);
};
tokenClient.requestAccessToken(options);
});
}
33 changes: 33 additions & 0 deletions places_insights/places-insights-demo/config.js.template
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// This file contains the client-side configuration for connecting to Google Cloud.
// RENAME this file to "config.js" and fill in your actual credentials.
// IMPORTANT: config.js is ignored by Git to keep your credentials private.

window.APP_CONFIG = {
/**
* Your Google Cloud Project ID.
* This is the project where BigQuery API calls will be billed.
*/
GCP_PROJECT_ID: 'YOUR_GCP_PROJECT_ID',

/**
* Your OAuth 2.0 Client ID for a Web Application.
* This ID is used to authenticate users and get their consent to run queries on their behalf.
* Ensure your app's origin (e.g., http://localhost:8000) is in the "Authorized JavaScript origins".
*/
OAUTH_CLIENT_ID: 'YOUR_OAUTH_CLIENT_ID.apps.googleusercontent.com',

/**
* Your Google Maps Platform API Key.
* This is used to display the map and call the Routes API.
* Ensure it has "Maps JavaScript API", "Geocoding API", and "Routes API" enabled.
*/
MAPS_API_KEY: 'YOUR_MAPS_API_KEY',

/**
* Defines which dataset to query.
* Options: 'FULL' or 'SAMPLE'
* - 'FULL': Queries the full country datasets (e.g., places_insights___us.places).
* - 'SAMPLE': Queries the city sample datasets (e.g., places_insights___us___sample.places_sample).
*/
DATASET: 'FULL',
};
Loading