This code fetches and processes depth map data for a given Google Street View panorama, enabling applications to access 3D information about the captured environment.
npm run import -- "extract depth maps"
// FROM: https://github.com/sidequestlegend/GSVPanoDepth.js/blob/master/src/GSVPanoDepth.js
var importer = require('../Core')
var {request} = importer.import("http request")
async function loadDepthMap(panoId, onDepthLoad) {
console.log("Getting depth map for panoid:", panoId)
var url
// This url no longer works
// url = "http://maps.google.com/cbk?output=json&cb_client=maps_sv&v=4&dm=1&pm=1&ph=1&hl=en&panoid=" + panoId;
url = "https://www.google.com/maps/photometa/v1?authuser=0&hl=en&gl=uk&pb=!1m4!1smaps_sv.tactile!11m2!2m1!1b1!2m2!1sen!2suk!3m3!1m2!1e2!2s" +
panoId +
"!4m57!1e1!1e2!1e3!1e4!1e5!1e6!1e8!1e12!2m1!1e1!4m1!1i48!5m1!1e1!5m1!1e2!6m1!1e1!6m1!1e2!9m36!1m3!1e2!2b1!3e2!1m3!1e2!2b0!3e3!1m3!" +
"1e3!2b1!3e2!1m3!1e3!2b0!3e3!1m3!1e8!2b0!3e3!1m3!1e1!2b0!3e3!1m3!1e4!2b0!3e3!1m3!1e10!2b1!3e2!1m3!1e10!2b0!3e3"
console.log(url)
var res = await request({
method: 'GET',
url: url
})
var dm = JSON.parse(res.body.substr(4))[1][0][5][0][5][1][2]
var decoded, depthMap;
try {
decoded = decodeDepthMap(dm)
depthMap = parseDepthMap(decoded)
} catch(e) {
console.error("Error loading depth map for pano " + panoId + "\n" + e.message + "\nAt " + e.filename + "(" + e.lineNumber + ")");
depthMap = createEmptyDepthMap()
}
if(onDepthLoad) {
await onDepthLoad(depthMap)
}
}
function decodeDepthMap(rawDepthMap) {
var i,
compressedDepthMapData,
depthMap,
decompressedDepthMap
// Append '=' in order to make the length of the array a multiple of 4
while(rawDepthMap.length %4 != 0)
rawDepthMap += '='
// Replace '-' by '+' and '_' by '/'
rawDepthMap = rawDepthMap.replace(/-/g,'+')
rawDepthMap = rawDepthMap.replace(/_/g,'/')
// Decode and decompress data
decompressedDepthMap = Buffer.from(rawDepthMap, 'base64').toString('binary')
// Convert output of decompressor to Uint8Array
depthMap = new Uint8Array(decompressedDepthMap.length)
for(i=0; i<decompressedDepthMap.length; ++i)
depthMap[i] = decompressedDepthMap.charCodeAt(i)
return depthMap
}
function parseDepthHeader(depthMap) {
return {
headerSize : depthMap.getUint8(0),
numberOfPlanes : depthMap.getUint16(1, true),
width: depthMap.getUint16(3, true),
height: depthMap.getUint16(5, true),
offset: depthMap.getUint16(7, true)
}
}
function parseDepthPlanes(header, depthMap) {
var planes = [],
indices = [],
i,
n = [0, 0, 0],
d,
byteOffset
for(i=0; i<header.width*header.height; ++i) {
indices.push(depthMap.getUint8(header.offset + i))
}
for(i=0; i<header.numberOfPlanes; ++i) {
byteOffset = header.offset + header.width*header.height + i*4*4
n[0] = depthMap.getFloat32(byteOffset, true)
n[1] = depthMap.getFloat32(byteOffset + 4, true)
n[2] = depthMap.getFloat32(byteOffset + 8, true)
d = depthMap.getFloat32(byteOffset + 12, true)
planes.push({
n: n.slice(0),
d: d
})
}
return { planes: planes, indices: indices }
}
function computeDepthMap(header, indices, planes) {
var depthMap = null,
x, y,
planeIdx,
phi, theta,
v = [0, 0, 0],
w = header.width, h = header.height,
plane, t, p
depthMap = new Float32Array(w*h)
var sin_theta = new Float32Array(h)
var cos_theta = new Float32Array(h)
var sin_phi = new Float32Array(w)
var cos_phi = new Float32Array(w)
for(y=0; y<h; ++y) {
theta = (h - y - 0.5) / h * Math.PI
sin_theta[y] = Math.sin(theta)
cos_theta[y] = Math.cos(theta)
}
for(x=0; x<w; ++x) {
phi = (w - x - 0.5) / w * 2 * Math.PI + Math.PI/2
sin_phi[x] = Math.sin(phi)
cos_phi[x] = Math.cos(phi)
}
for(y=0; y<h; ++y) {
for(x=0; x<w; ++x) {
planeIdx = indices[y*w + x]
v[0] = sin_theta[y] * cos_phi[x]
v[1] = sin_theta[y] * sin_phi[x]
v[2] = cos_theta[y]
if(planeIdx > 0) {
plane = planes[planeIdx]
t = Math.abs( plane.d / (v[0]*plane.n[0] + v[1]*plane.n[1] + v[2]*plane.n[2]) )
depthMap[y*w + (w-x-1)] = t
} else {
depthMap[y*w + (w-x-1)] = 9999999999999999999.
}
}
}
return {
width: w,
height: h,
depthMap: depthMap
}
}
function parseDepthMap(depthMap) {
var depthMapData,
header,
data,
depthMap
depthMapData = new DataView(depthMap.buffer)
header = parseDepthHeader(depthMapData)
data = parseDepthPlanes(header, depthMapData)
depthMap = computeDepthMap(header, data.indices, data.planes)
return depthMap
}
function createEmptyDepthMap() {
var depthMap = {
width: 512,
height: 256,
depthMap: new Float32Array(512*256)
}
for(var i=0; i<512*256; ++i)
depthMap.depthMap[i] = 9999999999999999999.
return depthMap
}
module.exports = {
loadDepthMap
}
const { request } = require('../Core/importer');
const debug = require('debug')('gsvpandepth');
const DEPTH_MAP_URL = 'https://www.google.com/maps/photometa/v1';
async function loadDepthMap(panoId, onDepthLoad) {
debug(`Getting depth map for panoid: ${panoId}`);
const url = `${DEPTH_MAP_URL}?authuser=0&hl=en&gl=uk&panoid=${panoId}`;
const res = await request({ method: 'GET', url });
const dm = JSON.parse(res.body.substr(4))[1][0][5][0][5][1][2];
try {
const decoded = decodeDepthMap(dm);
const depthMap = parseDepthMap(decoded);
if (onDepthLoad) await onDepthLoad(depthMap);
return depthMap;
} catch (e) {
console.error(`Error loading depth map for pano ${panoId}\n${e.message}\nAt ${e.filename}(${e.lineNumber})`);
const depthMap = createEmptyDepthMap();
if (onDepthLoad) await onDepthLoad(depthMap);
return depthMap;
}
}
function decodeDepthMap(rawDepthMap) {
// Append '=' in order to make the length of the array a multiple of 4
while (rawDepthMap.length % 4!== 0) rawDepthMap += '=';
// Replace '-' by '+' and '_' by '/'
rawDepthMap = rawDepthMap.replace(/-/g, '+');
rawDepthMap = rawDepthMap.replace(/_/g, '/');
// Decode and decompress data
const decompressedDepthMap = Buffer.from(rawDepthMap, 'base64').toString('binary');
return decompressedDepthMap;
}
function parseDepthHeader(depthMap) {
const headerSize = depthMap.getUint8(0);
const numberOfPlanes = depthMap.getUint16(1, true);
const width = depthMap.getUint16(3, true);
const height = depthMap.getUint16(5, true);
const offset = depthMap.getUint16(7, true);
return { headerSize, numberOfPlanes, width, height, offset };
}
function parseDepthPlanes(header, depthMap) {
const planes = [];
const indices = [];
const n = [0, 0, 0];
const byteOffset = 0;
for (let i = 0; i < header.width * header.height; ++i) {
indices.push(depthMap.getUint8(header.offset + i));
}
for (let i = 0; i < header.numberOfPlanes; ++i) {
byteOffset += header.width * header.height + i * 4 * 4;
n[0] = depthMap.getFloat32(byteOffset, true);
n[1] = depthMap.getFloat32(byteOffset + 4, true);
n[2] = depthMap.getFloat32(byteOffset + 8, true);
const d = depthMap.getFloat32(byteOffset + 12, true);
planes.push({ n: n.slice(0), d });
}
return { planes, indices };
}
function computeDepthMap(header, indices, planes) {
const depthMap = new Float32Array(header.width * header.height);
const sinTheta = new Float32Array(header.height);
const cosTheta = new Float32Array(header.height);
const sinPhi = new Float32Array(header.width);
const cosPhi = new Float32Array(header.width);
for (let y = 0; y < header.height; ++y) {
const theta = (header.height - y - 0.5) / header.height * Math.PI;
sinTheta[y] = Math.sin(theta);
cosTheta[y] = Math.cos(theta);
}
for (let x = 0; x < header.width; ++x) {
const phi = (header.width - x - 0.5) / header.width * 2 * Math.PI + Math.PI / 2;
sinPhi[x] = Math.sin(phi);
cosPhi[x] = Math.cos(phi);
}
for (let y = 0; y < header.height; ++y) {
for (let x = 0; x < header.width; ++x) {
const planeIdx = indices[y * header.width + x];
const v = [
sinTheta[y] * cosPhi[x],
sinTheta[y] * sinPhi[x],
cosTheta[y],
];
if (planeIdx > 0) {
const plane = planes[planeIdx];
const t = Math.abs(plane.d / (v[0] * plane.n[0] + v[1] * plane.n[1] + v[2] * plane.n[2]));
depthMap[y * header.width + (header.width - x - 1)] = t;
} else {
depthMap[y * header.width + (header.width - x - 1)] = 9999999999999999999;
}
}
}
return { width: header.width, height: header.height, depthMap };
}
function parseDepthMap(depthMap) {
const depthMapData = new DataView(depthMap.buffer);
const header = parseDepthHeader(depthMapData);
const data = parseDepthPlanes(header, depthMapData);
const depthMap = computeDepthMap(header, data.indices, data.planes);
return depthMap;
}
function createEmptyDepthMap() {
const depthMap = {
width: 512,
height: 256,
depthMap: new Float32Array(512 * 256),
};
for (let i = 0; i < 512 * 256; ++i) {
depthMap.depthMap[i] = 9999999999999999999;
}
return depthMap;
}
module.exports = { loadDepthMap };
This code snippet defines an asynchronous function loadDepthMap
that retrieves and processes depth map data for a given Google Street View panorama ID.
Here's a breakdown:
Imports:
loadDepthMap
Function:
panoId
(the ID of the panorama) and an optional onDepthLoad
callback function as arguments.panoId
.decodeDepthMap
and parseDepthMap
.onDepthLoad
callback function with the processed depth map data, if provided.Purpose:
This function retrieves and processes depth map data for a Google Street View panorama, likely used for applications that require 3D information about the environment captured in the panorama.