google maps | extract depth maps | test google depth maps | Search

This code fetches and assembles a panoramic image from Google Street View, allowing for dynamic display and manipulation of street-level imagery.

Run example

npm run import -- "load google panorama"

load google panorama

// FROM: https://github.com/sidequestlegend/GSVPanoDepth.js/blob/master/examples/js/GSVPano.js
var path = require('path')
var fs = require('fs')
var {GoogleAuth} = require('google-auth-library');
var google = require('googleapis')
var importer = require('../Core')
var getRpcFromSpec = importer.import("get rpc from spec")
var authorize = importer.import("google oauth token client")
var mapsClient = getRpcFromSpec(require('../Resources/APIs/google-maps-platform-openapi3.json'));
var {createCanvas, loadImage} = require('canvas')
var PROFILE_PATH = process.env.HOME || process.env.HOMEPATH || process.env.USERPROFILE;
var authKey = fs.readFileSync(path.join(PROFILE_PATH, '.credentials/key.txt')).toString().trim()
//_panoClient = new google.maps.StreetViewService(),
    
var _location,
    _zoom,
    _panoId,
    _count = 0,
    _total = 0,
    _canvas = createCanvas(1, 1),
    _ctx = _canvas.getContext('2d'),
    rotation = 0,
    copyright = '',
    onSizeChange = null,
    onPanoramaLoad = null

function setProgress(p) {

}

function throwError(message) {

    if (onError) {
        onError(message)
    } else {
        console.error(message)
    }

}

function adaptTextureToZoom() {

    var w = 416 * Math.pow(2, _zoom - 0.7),
        h = (416 * Math.pow(2, _zoom - 2))
    _canvas.width = w
    _canvas.height = h
    _ctx.translate( _canvas.width, 0)
    _ctx.scale(-1, 1)
}

function composeFromTile(x, y, texture) {

    _ctx.drawImage(texture, x * 512, y * 512)
    _count++

    var p = Math.round(_count * 100 / _total)
    setProgress(p)

    if (_count === _total) {
        if (onPanoramaLoad) {
            onPanoramaLoad()
        }
    }

}

async function composePanorama() {

    setProgress(0)
    console.log('Loading panorama for zoom ' + _zoom + '...')

    var w = Math.pow(2, _zoom),
        h = Math.pow(2, _zoom - 1),
        url, x, y

    _count = 0
    _total = w * h
    var now = (new Date).getTime()

    for( y = 0; y < h; y++) {
        for( x = 0; x < w; x++) {
            /*
            var imgResult = await mapsClient.cbk.get({
                output: 'tile',
                panoid: _panoId,
                zoom: 0,
                x: x,
                y: y
            })
            fs.writeFileSync('.tmpimg.jpeg', imgResult.body)
            */
            //url = 'http://maps.google.com/cbk?output=tile&panoid=' + _panoId + '&zoom=0&x=' + x + '&y=' + y + '&' + Date.now()
            //var img = await loadImage('.tmpimg.jpeg')
            /*
            var img = new Image
            img.addEventListener('load', function () {
                
                resolve()
            })
            var imgPromise = new Promise(resolve => {
            })
            img.src = imgResult.body
            await imgPromise
            */
            if (onPanoramaLoad) {
                await onPanoramaLoad(_panoId)
            }
            //composeFromTile(x, y, img)
        }
    }

}

async function loadPanorama(location, onLoad) {
    console.log(mapsClient)
    console.log('Load for', location)
    var result = await mapsClient.maps.streetview.metadata.get({
        location: location[0] + ',' + location[1],
        heading: 0,
        key: authKey
    })
    onPanoramaLoad = onLoad
    //if( onPanoramaData ) onPanoramaData( result )
    //var h = google.maps.geometry.spherical.computeHeading(location, result.location.latLng)
    //rotation = (result.tiles.centerHeading - h) * Math.PI / 180.0
    //copyright = result.copyright
    _panoId = result.body.pano_id
    _location = location
    await composePanorama()
}

function setZoom( z ) {
    _zoom = z
    adaptTextureToZoom()
}

module.exports = {
    setZoom,
    loadPanorama,
}

What the code could have been:

const path = require('path');
const fs = require('fs');
const { GoogleAuth } = require('google-auth-library');
const google = require('googleapis');
const importer = require('../Core');
const { getRpcFromSpec } = importer.import('get rpc from spec');
const { authorize } = importer.import('google oauth token client');
const { createCanvas, loadImage } = require('canvas');

const PROFILE_PATH = process.env.HOME || process.env.HOMEPATH || process.env.USERPROFILE;
const authKey = fs.readFileSync(path.join(PROFILE_PATH, '.credentials/key.txt')).toString().trim();

const _panoClient = new google.maps.StreetViewService();
const _location = null;
const _zoom = 0;
const _panoId = null;
const _count = 0;
const _total = 0;
const _canvas = createCanvas(1, 1);
const _ctx = _canvas.getContext('2d');
let rotation = 0;
let copyright = '';
let onError = null;
let onSizeChange = null;
let onPanoramaLoad = null;

/**
 * @description Sets the progress of the panorama composition
 * @param {number} p The progress percentage
 */
function setProgress(p) {
  if (onError) {
    onError(p);
  } else {
    console.error(`Progress: ${p}%`);
  }
}

/**
 * @description Throws an error if an error handler is provided
 * @param {string} message The error message
 */
function throwError(message) {
  if (onError) {
    onError(message);
  } else {
    console.error(message);
  }
}

/**
 * @description Adapts the texture to the current zoom level
 */
function adaptTextureToZoom() {
  const w = 416 * Math.pow(2, _zoom - 0.7);
  const h = 416 * Math.pow(2, _zoom - 2);
  _canvas.width = w;
  _canvas.height = h;
  _ctx.translate(_canvas.width, 0);
  _ctx.scale(-1, 1);
}

/**
 * @description Composes a tile from the given texture
 * @param {number} x The x-coordinate of the tile
 * @param {number} y The y-coordinate of the tile
 * @param {HTMLImageElement} texture The texture to compose
 */
function composeFromTile(x, y, texture) {
  _ctx.drawImage(texture, x * 512, y * 512);
  _count++;
  setProgress(Math.round(_count * 100 / _total));
  if (_count === _total) {
    if (onPanoramaLoad) {
      onPanoramaLoad();
    }
  }
}

/**
 * @description Composes the panorama from the tiles
 * @async
 */
async function composePanorama() {
  setProgress(0);
  console.log(`Loading panorama for zoom ${_zoom}...`);
  const w = Math.pow(2, _zoom);
  const h = Math.pow(2, _zoom - 1);
  const url = `http://maps.google.com/cbk?output=tile&panoid=${_panoId}&zoom=0`;
  _count = 0;
  _total = w * h;
  const now = new Date().getTime();
  for (let y = 0; y < h; y++) {
    for (let x = 0; x < w; x++) {
      const imgPromise = new Promise((resolve) => {
        const img = new Image();
        img.addEventListener('load', () => {
          resolve();
        });
        img.src = `${url}&x=${x}&y=${y}&${now}`;
      });
      await imgPromise;
      composeFromTile(x, y, new Image());
    }
  }
}

/**
 * @description Loads the panorama from the given location
 * @async
 * @param {Array} location The location coordinates
 * @param {function} onLoad The callback function to call when the panorama is loaded
 */
async function loadPanorama(location, onLoad) {
  console.log('Load for', location);
  const result = await mapsClient.maps.streetview.metadata.get({
    location: location[0] + ',' + location[1],
    heading: 0,
    key: authKey,
  });
  onPanoramaLoad = onLoad;
  _panoId = result.body.pano_id;
  _location = location;
  await composePanorama();
}

/**
 * @description Sets the zoom level of the panorama
 * @param {number} z The new zoom level
 */
function setZoom(z) {
  _zoom = z;
  adaptTextureToZoom();
}

module.exports = {
  setZoom,
  loadPanorama,
};

This code snippet is designed to load and compose a panoramic image from Google Street View using the Google Maps Platform API.

Here's a breakdown:

  1. Dependencies:

  2. Initialization:

  3. Progress Handling:

  4. Image Adaptation:

  5. Tile Composition:

  6. Panorama Loading:

  7. Callbacks:

Purpose:

This code snippet demonstrates how to fetch and assemble a panoramic image from Google Street View programmatically, allowing for dynamic display and manipulation of street-level imagery.