|
76 | 76 | "source": [ |
77 | 77 | "### Overall Goal\n", |
78 | 78 | "\n", |
79 | | - "This notebook serves as a technical introduction for developers and data analysts who have subscribed to the [Places Insights Sample Datasets](https://developers.google.com/maps/documentation/placesinsights/cloud-setup#sample_data).\n", |
| 79 | + "This notebook serves as a technical introduction for developers and data analysts who have subscribed to the **[Places Insights Datasets](https://developers.google.com/maps/documentation/placesinsights)**.\n", |
80 | 80 | "\n", |
81 | | - "Its primary purpose is to demonstrate how to query, aggregate, and visualize Google Maps Platform [Places Insights](https://developers.google.com/maps/documentation/placesinsights) data within a **BigQuery** environment. By running this notebook, you will learn how to transition from raw dataset subscriptions to actionable geospatial insights using Standard SQL and Python.\n", |
| 81 | + "Its primary purpose is to demonstrate how to query, aggregate, and visualize Google Maps Platform Places Insights data within a **BigQuery** environment. Whether you are evaluating the product using **Sample Data** or building production workflows with **Full Data**, this notebook will help you transition from raw dataset subscriptions to actionable geospatial insights using Standard SQL and Python.\n", |
82 | 82 | "\n", |
83 | 83 | "### Key Technologies Used\n", |
84 | 84 | "\n", |
|
94 | 94 | "\n", |
95 | 95 | "### The Step-by-Step Workflow\n", |
96 | 96 | "\n", |
97 | | - "1. **Configuration:** The notebook initializes the environment based on your selection of a Sample City (e.g., \"New York City\", \"Tokyo\", \"London\"). It automatically maps your selection to the correct **Analytics Hub Dataset ID** and geographic coordinates.\n", |
| 97 | + "1. **Configuration:** The notebook initializes the environment based on your selection of a City (e.g., \"New York City\", \"Tokyo\") and your **Data Source** (Sample or Full). It automatically maps your selection to the correct Analytics Hub Dataset ID and geographic coordinates.\n", |
98 | 98 | "\n", |
99 | 99 | "2. **Direct Query Analysis (Radius Search):** We demonstrate how to count places that match specific criteria, such as `primary_type`, `business_status`, and boolean attributes like `allows_dogs`, within a set radius. We utilize the `WITH AGGREGATION_THRESHOLD` clause to compare amenity density across multiple neighborhoods programmatically.\n", |
100 | 100 | "\n", |
|
108 | 108 | "\n", |
109 | 109 | "1. **Prerequisites:**\n", |
110 | 110 | " * A Google Cloud Project with the **BigQuery API** and **Map Tiles API** enabled.\n", |
111 | | - " * A subscription to at least one of the [Places Insights Sample Datasets](https://developers.google.com/maps/documentation/placesinsights/cloud-setup#sample_data) via Analytics Hub.\n", |
| 111 | + " * A subscription to either the [Sample Datasets](https://developers.google.com/maps/documentation/placesinsights/cloud-setup#sample_data) or the [Full Datasets](https://developers.google.com/maps/documentation/placesinsights/cloud-setup#full_data) via Analytics Hub.\n", |
112 | 112 | "\n", |
113 | 113 | "2. **Set Up Secrets:** Configure the following keys in the Colab \"Secrets\" tab (the **key icon** on the left menu):\n", |
114 | 114 | " * `GCP_PROJECT_ID`: Your Google Cloud Project ID.\n", |
|
162 | 162 | "cell_type": "code", |
163 | 163 | "source": [ |
164 | 164 | "# @title 2. Configuration\n", |
165 | | - "# @markdown Select a Sample City to demo. This configures the dataset target and map center.\n", |
166 | | - "# @markdown\n", |
167 | | - "# @markdown **Important:** You must be subscribed to the [Sample Dataset](https://developers.google.com/maps/documentation/placesinsights/cloud-setup#sample_data) in your GCP project.\n", |
168 | | - "# @markdown\n", |
169 | | - "# @markdown If you renamed the dataset during the Analytics Hub subscription process, enter the full table name (e.g., `my_project.my_dataset.places_sample`) in the **Custom Table Name** field below. Leave it blank to use the default.\n", |
| 165 | + "# @markdown Select a Sample City to demo. This configures the map center and the target BigQuery dataset.\n", |
170 | 166 | "\n", |
| 167 | + "# @markdown ### 1. Select Location\n", |
171 | 168 | "SAMPLE_LOCATION = \"New York City, USA\" # @param [\"Sydney, Australia\", \"Sao Paulo, Brazil\", \"Toronto, Canada\", \"Paris, France\", \"Berlin, Germany\", \"Mumbai, India\", \"Jakarta, Indonesia\", \"Rome, Italy\", \"Tokyo, Japan\", \"Mexico City, Mexico\", \"Madrid, Spain\", \"Zurich, Switzerland\", \"London, UK\", \"New York City, USA\"]\n", |
| 169 | + "\n", |
| 170 | + "# @markdown ### 2. Select Data Source\n", |
| 171 | + "# @markdown Choose **Sample Data** for evaluation (requires [\"Sample\" subscription](https://developers.google.com/maps/documentation/placesinsights/cloud-setup#sample_data)) or **Full Data** for production (requires [\"Full\" subscription](https://developers.google.com/maps/documentation/placesinsights/cloud-setup#full_data)).\n", |
| 172 | + "DATASET_MODE = \"Sample Data\" # @param [\"Sample Data\", \"Full Data\"]\n", |
| 173 | + "\n", |
| 174 | + "# @markdown ### 3. Custom Override (Optional)\n", |
| 175 | + "# @markdown Enter a specific table name (e.g., `my_project.my_dataset.my_table`) to override the default.\n", |
| 176 | + "# @markdown\n", |
| 177 | + "# @markdown Only use this field if you renamed the dataset or table during the Analytics Hub subscription process. Leave blank to use the default naming convention.\n", |
172 | 178 | "CUSTOM_TABLE_NAME = \"\" # @param {type:\"string\"}\n", |
173 | 179 | "\n", |
174 | | - "# Configuration Dictionary mapping selection to Dataset and Center Coordinates\n", |
175 | | - "# Note: These table names assume the default naming convention from Analytics Hub.\n", |
| 180 | + "# Configuration Dictionary mapping selection to Country Code and Center Coordinates\n", |
176 | 181 | "config_map = {\n", |
177 | 182 | " \"Sydney, Australia\": {\"code\": \"au\", \"center\": (-33.8688, 151.2093)},\n", |
178 | 183 | " \"Sao Paulo, Brazil\": {\"code\": \"br\", \"center\": (-23.5505, -46.6333)},\n", |
|
190 | 195 | " \"New York City, USA\": {\"code\": \"us\", \"center\": (40.7580, -73.9855)},\n", |
191 | 196 | "}\n", |
192 | 197 | "\n", |
| 198 | + "# 1. Retrieve Config\n", |
193 | 199 | "selected_config = config_map[SAMPLE_LOCATION]\n", |
| 200 | + "code = selected_config['code']\n", |
194 | 201 | "CITY_CENTER_LAT, CITY_CENTER_LNG = selected_config['center']\n", |
195 | 202 | "\n", |
196 | | - "# Determine Table Name\n", |
| 203 | + "# 2. Determine Table Name\n", |
197 | 204 | "if CUSTOM_TABLE_NAME and CUSTOM_TABLE_NAME.strip():\n", |
| 205 | + " # Priority 1: User Custom Override\n", |
198 | 206 | " DATASET_TABLE = CUSTOM_TABLE_NAME.strip()\n", |
199 | 207 | " print(f\"⚠️ Using Custom Table Name: {DATASET_TABLE}\")\n", |
200 | 208 | "else:\n", |
201 | | - " # Default construction\n", |
202 | | - " DATASET_TABLE = f\"places_insights___{selected_config['code']}___sample.places_sample\"\n", |
203 | | - " print(f\"🎯 Target Table (Default): {DATASET_TABLE}\")\n", |
| 209 | + " # Priority 2: Auto-Construct based on Mode\n", |
| 210 | + " if DATASET_MODE == \"Sample Data\":\n", |
| 211 | + " # Pattern: places_insights___us___sample.places_sample\n", |
| 212 | + " DATASET_TABLE = f\"places_insights___{code}___sample.places_sample\"\n", |
| 213 | + " else:\n", |
| 214 | + " # Pattern: places_insights___us.places\n", |
| 215 | + " DATASET_TABLE = f\"places_insights___{code}.places\"\n", |
| 216 | + "\n", |
| 217 | + " print(f\"🎯 Target Table ({DATASET_MODE}): {DATASET_TABLE}\")\n", |
204 | 218 | "\n", |
205 | 219 | "print(f\"🌍 Selected Location: {SAMPLE_LOCATION}\")\n", |
206 | 220 | "print(f\"📍 Center Point: {CITY_CENTER_LAT}, {CITY_CENTER_LNG}\")" |
|
292 | 306 | { |
293 | 307 | "cell_type": "code", |
294 | 308 | "source": [ |
295 | | - "# @title 4. Direct Query: Analyzing \"Al Fresco & Dog-Friendly\" Hubs\n", |
296 | | - "# @markdown The following cell runs the BigQuery analysis to find hotspots.\n", |
| 309 | + "# @title 4. Direct Query: Identifying \"Al Fresco\" Dining Hubs\n", |
| 310 | + "# @markdown This cell demonstrates how to perform a radius search to find specific business types with distinct attributes.\n", |
| 311 | + "# @markdown\n", |
| 312 | + "# @markdown **The Analysis Workflow:**\n", |
| 313 | + "# @markdown 1. **Generate Probe Locations:** Python calculates coordinates for 5 \"neighborhoods\" around the city center.\n", |
| 314 | + "# @markdown 2. **Spatial Join (`ST_DWITHIN`):** We query BigQuery to find places within **1,000 meters** of these points.\n", |
| 315 | + "# @markdown 3. **Attribute Filtering:** We filter the results for **Operational Restaurants** that specifically feature `outdoor_seating` **OR** `allows_dogs`.\n", |
| 316 | + "# @markdown 4. **Privacy Compliance:** The query uses `WITH AGGREGATION_THRESHOLD` to ensure counts are only returned if they meet the privacy threshold (minimum 5).\n", |
297 | 317 | "\n", |
298 | 318 | "# --- 1. Generate Candidate Locations (Python) ---\n", |
299 | 319 | "def get_offset_point(lat, lng, lat_offset, lng_offset):\n", |
|
405 | 425 | "cell_type": "code", |
406 | 426 | "source": [ |
407 | 427 | "# @title 5. Visualization: Marker Map\n", |
408 | | - "# @markdown Renders the query results using the initialized map assets.\n", |
| 428 | + "# @markdown This cell renders the analysis results on an interactive map.\n", |
| 429 | + "# @markdown\n", |
| 430 | + "# @markdown **Visualization Details:**\n", |
| 431 | + "# @markdown * **Map Engine:** We use [Folium](https://python-visualization.github.io/folium/latest/) to generate the map interface.\n", |
| 432 | + "# @markdown * **Base Layer:** The background uses Google Maps Tiles(initialized in Cell 3).\n", |
| 433 | + "# @markdown * **Data Overlay:** Markers represent the 5 distinct neighborhoods analyzed in the previous step. They are **color-coded** to highlight the relative density of dining options (Red = Hotspot, Blue = Low Activity).\n", |
| 434 | + "# @markdown * **Interactivity:** Click on any marker to see the exact count of matching places.\n", |
| 435 | + "\n", |
409 | 436 | "import folium\n", |
410 | 437 | "from folium import plugins, Element\n", |
411 | 438 | "\n", |
|
468 | 495 | "display(m)" |
469 | 496 | ], |
470 | 497 | "metadata": { |
471 | | - "id": "F4Zt7xqqJjWB" |
| 498 | + "id": "F4Zt7xqqJjWB", |
| 499 | + "cellView": "form" |
472 | 500 | }, |
473 | 501 | "execution_count": null, |
474 | 502 | "outputs": [] |
|
477 | 505 | "cell_type": "code", |
478 | 506 | "source": [ |
479 | 507 | "# @title 6. Function Query: Fetch H3 Density Data\n", |
480 | | - "# @markdown We use the [PLACES_COUNT_PER_H3](https://developers.google.com/maps/documentation/placesinsights/place-count-functions/places-count-per-h3) function to retrieve aggregation data.\n", |
| 508 | + "# @markdown This cell shows a Grid Search using the [PLACES_COUNT_PER_H3](https://developers.google.com/maps/documentation/placesinsights/place-count-functions/places-count-per-h3) SQL function.\n", |
| 509 | + "# @markdown\n", |
| 510 | + "# @markdown **The Analysis Workflow:**\n", |
| 511 | + "# @markdown 1. **H3 Grid System:** We use [H3](https://h3geo.org/) (Resolution 8) to normalize the city map into uniform hexagonal cells. This is ideal for comparing density across irregular city layouts.\n", |
| 512 | + "# @markdown 2. **The Query:** We call the pre-defined BigQuery function `PLACES_COUNT_PER_H3`. This function automatically aggregates places into the grid cells.\n", |
| 513 | + "# @markdown 3. **Search Parameters:**\n", |
| 514 | + "# @markdown * **Area:** A **5km radius** around the city center.\n", |
| 515 | + "# @markdown * **Types:** Broad commercial activity (`restaurant`, `store`, `point_of_interest`).\n", |
| 516 | + "# @markdown * **Status:** Only `OPERATIONAL` places.\n", |
| 517 | + "# @markdown 4. **Geometry Processing:** The query returns GeoJSON strings for each hexagon, which we parse into a `GeoDataFrame` for visualization.\n", |
481 | 518 | "\n", |
482 | 519 | "import json\n", |
483 | 520 | "from shapely.geometry import shape\n", |
|
539 | 576 | { |
540 | 577 | "cell_type": "code", |
541 | 578 | "source": [ |
542 | | - "# @title 7. Visualization: H3 Heatmap\n", |
543 | | - "# @markdown We visualize the density data as a Choropleth layer on the Google Maps basemap, reusing the backend assets.\n", |
| 579 | + "# @title 7. Visualization: H3 Density Heatmap\n", |
| 580 | + "# @markdown This cell renders the H3 grid data as a Choropleth map overlay.\n", |
| 581 | + "# @markdown\n", |
| 582 | + "# @markdown **Visualization Details:**\n", |
| 583 | + "# @markdown * **Technique (Choropleth):** We map the `count` of places in each H3 hexagon to a color scale.\n", |
| 584 | + "# @markdown * **Color Scale:** We use a **Yellow-Orange-Red (`YlOrRd`)** gradient.\n", |
| 585 | + "# @markdown * 🟨 **Yellow:** Lower density of places.\n", |
| 586 | + "# @markdown * 🟥 **Red:** High density/commercial saturation.\n", |
| 587 | + "# @markdown * **Context:** By overlaying this on the map, you can correlate high-density hexagons with specific neighborhoods, transit hubs, or shopping districts.\n", |
544 | 588 | "\n", |
545 | 589 | "if 'gdf_h3' not in locals() or gdf_h3 is None or gdf_h3.empty:\n", |
546 | 590 | " raise ValueError(\"No H3 data found! Please run Cell 6 successfully first.\")\n", |
|
574 | 618 | "display(m_hex)" |
575 | 619 | ], |
576 | 620 | "metadata": { |
577 | | - "id": "H_6oq9nrJpev" |
| 621 | + "id": "H_6oq9nrJpev", |
| 622 | + "cellView": "form" |
578 | 623 | }, |
579 | 624 | "execution_count": null, |
580 | 625 | "outputs": [] |
|
0 commit comments