Canvas tiles on a Google Map

Testing how to interactively update animation tiles on top of a Google Map.

by David Iglesias

HTML

<!DOCTYPE html>
<!--
 @license
 Copyright 2019 Google LLC. All Rights Reserved.
 SPDX-License-Identifier: Apache-2.0
-->
<html>
  <head>
    <title>Overlay Map Types</title>
    <script src="https://polyfill.io/v3/polyfill.min.js?features=default"></script>
    <!-- jsFiddle will insert css and js -->
  </head>
  <body>
    <div id="map"></div>

    <!-- 
      The `defer` attribute causes the callback to execute after the full HTML
      document has been parsed. For non-blocking uses, avoiding race conditions,
      and consistent behavior across browsers, consider loading using Promises.
      See https://developers.google.com/maps/documentation/javascript/load-maps-js-api
      for more information.
      -->
    <script
      src="https://maps.googleapis.com/maps/api/js?key=AIzaSyB41DRUbKWJHPxaFjMAwdrzWzbVKartNGg&callback=initMap&v=weekly"
      defer
    ></script>
  </body>
</html>

CSS

/**
 * @license
 * Copyright 2019 Google LLC. All Rights Reserved.
 * SPDX-License-Identifier: Apache-2.0
 */
/* 
 * Always set the map height explicitly to define the size of the div element
 * that contains the map. 
 */
#map {
  height: 100%;
}

/* 
 * Optional: Makes the sample page fill the window. 
 */
html,
body {
  height: 100%;
  margin: 0;
  padding: 0;
}

JavaScript

/**
 * @license
 * Copyright 2019 Google LLC. All Rights Reserved.
 * SPDX-License-Identifier: Apache-2.0
 */
/*
 * This demo illustrates the coordinate system used to display map tiles in the
 * API.
 *
 * Tiles in Google Maps are numbered from the same origin as that for
 * pixels. For Google's implementation of the Mercator projection, the origin
 * tile is always at the northwest corner of the map, with x values increasing
 * from west to east and y values increasing from north to south.
 *
 * Try panning and zooming the map to see how the coordinates change.
 */
class CoordMapType {
  tileSize;
  // Cache for the tiles that need to be re-drawn.
  // This could be a weakmap so we can store
  // tile -> {coord, zoom} for redrawing
  tileSet;
  tileOptions;
  alt = null;
  maxZoom = 17;
  minZoom = 0;
  name = null;
  projection = null;
  radius = 6378137;
  currentTick = 0;
  offscreen;
  constructor(tileSize) {
    this.tileSize = tileSize;
    this.tileSet = new Set();
    this.tileOptions = new WeakMap();
    let ratio = window.devicePixelRatio;
    this.offscreen = new OffscreenCanvas(tileSize.width * ratio, tileSize.height * ratio);
    let ctx = this.offscreen.getContext('2d');
    ctx.setTransform(ratio, 0, 0, ratio, 0, 0);
  }
  tick(t) {
  	this.currentTick = t;
    this.tileSet.forEach(this.drawTile.bind(this));
  }
  drawTile(canvas) {
		const t = this.currentTick / 1000; // milliseconds
    const w = this.tileSize.width;
    const h = this.tileSize.height;
		const ctx = this.offscreen.getContext('2d');
    const outCtx = canvas.getContext('bitmaprenderer');
    const options = this.tileOptions.get(canvas);
		const numCircles = options.zoom;

    ctx.clearRect(0, 0, w, h);

    ctx.font = "10px sans-serif";
    ctx.fillStyle = "#000000";
    ctx.fillText(`x: ${options.coord.x}, y: ${options.coord.y}, z: ${options.zoom}, t: ${t.toFixed(3)}`, 0, 10);

    let r = (Math.sin(t * 10) * 10) + 20;
    for (var i = 0; i < numCircles; i++) {
	   ...