JSFiddle - React, Tailwind, and code Playground

HTML

<div id="controls">
  <button id="toggleGrid" class="toggle-button">Show Tiles</button>
</div>
<div id="map"></div>

<script src="https://unpkg.com/deck.gl@8.8.0/dist.min.js"></script>

<script
  async
  src="https://maps.googleapis.com/maps/api/js?key=AIzaSyB41DRUbKWJHPxaFjMAwdrzWzbVKartNGg&callback=initMap"
></script>

CSS

body,
html {
  margin: 0;
  padding: 0;
  height: 100%;
  width: 100%;
}
#map {
  height: 100vh;
  width: 100vw;
  position: absolute;
  top: 0;
  left: 0;
}
#controls {
  position: absolute;
  top: 10px;
  left: 10px;
  z-index: 1000;
  background-color: white;
  padding: 10px;
  border-radius: 4px;
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.3);
}
.toggle-button {
  background-color: #fff;
  border: 1px solid #ccc;
  border-radius: 4px;
  padding: 8px 16px;
  cursor: pointer;
  font-family: Arial, sans-serif;
  font-size: 14px;
  transition: all 0.3s ease;
}
.toggle-button:hover {
  background-color: #f0f0f0;
}
.toggle-button.active {
  background-color: #4285f4;
  color: white;
  border-color: #4285f4;
}
.tile-label {
  color: black;
  font-family: Arial, sans-serif;
  font-size: 12px;
  font-weight: bold;
  text-align: center;
  background-color: rgba(255, 255, 255, 0.7);
  padding: 2px 4px;
  border-radius: 2px;
}

JavaScript

const MAPS_API_KEY = "AIzaSyB41DRUbKWJHPxaFjMAwdrzWzbVKartNGg";
const INSIGHTS_API_KEY = "AIzaSyBj5zfC9KuQwBmICZFDoL3hX_MxRvw73ec";
const INSIGHTS_API_URL = "https://areainsights.googleapis.com/v1:computeInsights";
const TARGET_TILE_COUNT = 100;  // How many tiles to divide the map into
const POINT_SPACING_METERS = 100;  // Spacing between heatmap points
const HEATMAP_RADIUS_PIXELS = 300;  // Size of each heatmap point
const INITIAL_VIEW_STATE = {
  latitude: 48.8566,  // Paris
  longitude: 2.3522,
  zoom: 15,
};

// Global state variables
let map;
let overlay;
let currentData = [];
let tilesVisible = false;
let currentTileElements = [];
let currentLabels = [];
let debouncedUpdateHeatmap;

/**
 * Generates a grid of points within a tile for heatmap visualization
 * Each point will have the same weight (count)
 */
function generatePointsInTile(tilePolygon, count) {
  const points = [];
  // Calculate the bounding box of the tile
  const bounds = {
    north: Math.max(...tilePolygon.map((p) => p.lat)),
    south: Math.min(...tilePolygon.map((p) => p.lat)),
    east: Math.max(...tilePolygon.map((p) => p.lng)),
    west: Math.min(...tilePolygon.map((p) => p.lng)),
  };

  const latSize = bounds.north - bounds.south;
  const lngSize = bounds.east - bounds.west;
  const padding = 0.2;  // Padding to avoid points on the edge
  const paddedBounds = {
    north: bounds.north - latSize * padding,
    south: bounds.south + latSize * padding,
    east: bounds.east - lngSize * padding,
    west: bounds.west + lngSize * padding,
  };

  // Convert meters to lat/lng degrees (approximate)
  const latSpacing = POINT_SPACING_METERS / 111111;
  const lngSpacing = POINT_SPACING_METERS / (111111 * Math.cos((bounds.north * Math.PI) / 180));

  // Generate a grid of points
  for (let lat = paddedBounds.south; lat <= paddedBounds.north; lat += latSpacing) {
    for (let lng = paddedBounds.west; lng <= paddedBounds.east; lng +=...