254 lines
7.6 KiB
JavaScript
254 lines
7.6 KiB
JavaScript
'use strict';
|
|
|
|
//
|
|
// VARIABLES
|
|
//
|
|
|
|
var canvas, canvas2, ctx, ctx2, zoom;
|
|
const maxCanvasHeight = 7000; //px
|
|
var downloadCount = 0;
|
|
|
|
//
|
|
// UTILITY FUNCTIONS
|
|
//
|
|
|
|
function ID(id) {
|
|
return document.getElementById(id);
|
|
}
|
|
|
|
function extractTextBetween(input, before, after) {
|
|
const step1 = input.substring(input.indexOf(before) + before.length, input.length);
|
|
const step2 = step1.substring(0, step1.indexOf(after));
|
|
return step2;
|
|
}
|
|
|
|
//
|
|
// INITIALIZATION
|
|
//
|
|
|
|
window.addEventListener("load", () => {
|
|
const urlBox = ID("downloadURL");
|
|
canvas = ID("canvas");
|
|
canvas2 = ID("canvas2");
|
|
ctx = canvas.getContext("2d");
|
|
ctx2 = canvas2.getContext("2d");
|
|
|
|
ID("loadButton").addEventListener("click", () => {
|
|
zoom = ID("zoom").value;
|
|
console.log("URL : ", urlBox.value);
|
|
parseURL(urlBox.value);
|
|
});
|
|
ID("downloadButton").addEventListener("click", () => {
|
|
downloadCanvas();
|
|
});
|
|
});
|
|
|
|
//
|
|
//
|
|
//
|
|
|
|
function downloadCanvas() {
|
|
ID("infoStatus").textContent = "saving image..";
|
|
if (canvas2.height > 0) {
|
|
const link1 = document.createElement('a');
|
|
link1.download = `top${downloadCount}.png`;
|
|
link1.href = canvas.toDataURL();
|
|
link1.click();
|
|
const link2 = document.createElement('a');
|
|
link2.download = `bottom${downloadCount}.png`;
|
|
link2.href = canvas2.toDataURL();
|
|
link2.click();
|
|
downloadCount++;
|
|
} else {
|
|
const link = document.createElement('a');
|
|
link.download = `image${downloadCount}.png`;
|
|
link.href = canvas.toDataURL();
|
|
link.click();
|
|
downloadCount++;
|
|
}
|
|
}
|
|
|
|
function resetInfoDisplay() {
|
|
const normal = "N/A";
|
|
ID("infoStatus").textContent = normal;
|
|
ID("infoType").textContent = normal;
|
|
ID("infoCopyright").textContent = normal;
|
|
ID("infoZooms").textContent = normal;
|
|
ID("infoID").textContent = normal;
|
|
}
|
|
|
|
function parseURL(url) {
|
|
resetInfoDisplay();
|
|
if (url.includes("panoid")) {
|
|
const id = extractTextBetween(url, "panoid%3D", "%");
|
|
console.log("Extracted PanoID using method 1: ", id);
|
|
|
|
getPath(id);
|
|
} else if (url.includes("m4!1s") && url.includes("data=")) {
|
|
const id = extractTextBetween(url, "m4!1s", "!2e");
|
|
if (id.length > 22) {
|
|
console.log("Extracted Photosphere ID using method 2: ", id);
|
|
getSphere(id);
|
|
} else {
|
|
console.log("Extracted PanoID using method 2: ", id);
|
|
getPath(id);
|
|
}
|
|
} else if (url.includes("googleusercontent.com")) {
|
|
const id = extractTextBetween(url, "googleusercontent.com%2Fp%2F", "%3");
|
|
console.log("Photosphere ID extracted: ", id);
|
|
getSphere(id);
|
|
} else {
|
|
ID("infoStatus").textContent = "Error: Could not understand link.";
|
|
}
|
|
}
|
|
|
|
//
|
|
// PATH OBTAINING LOGIC
|
|
//
|
|
|
|
function getPath(panoid) {
|
|
ID("infoStatus").textContent = "Getting data from server...";
|
|
ID("infoID").textContent = panoid;
|
|
ID("infoType").textContent = `Street View`;
|
|
var request = new XMLHttpRequest();
|
|
request.onload = function () {
|
|
ID("infoStatus").textContent = "Parsing data...";
|
|
// Metadata parsing witchcraft
|
|
const data = JSON.parse(this.responseText.substring(4))[1][0];
|
|
|
|
const tileSize = data[2][3][1][0];
|
|
const bestZoom = (data[2][3][0].length - 1);
|
|
|
|
if (bestZoom < zoom || zoom < 0) {
|
|
zoom = bestZoom;
|
|
document.getElementById("zoom").value = bestZoom;
|
|
}
|
|
|
|
const copyright = data[4][0][0][0][0];
|
|
|
|
ID("infoZooms").textContent = ` 0 to ${bestZoom}`;
|
|
ID("infoCopyright").textContent = copyright;
|
|
|
|
const widthPX = data[2][3][0][zoom][0][1];
|
|
const heightPX = data[2][3][0][zoom][0][0];
|
|
const width = Math.ceil(widthPX / tileSize);
|
|
const height = Math.ceil(heightPX / tileSize);
|
|
|
|
prepCanvas(widthPX, heightPX);
|
|
|
|
ID("infoStatus").textContent = "Downloading tiles";
|
|
const totalTiles = width * height;
|
|
var processedTiles = 0;
|
|
for (let x = 0; x < width; x++) {
|
|
for (let y = 0; y < height; y++) {
|
|
requestImage(`https://streetviewpixels-pa.googleapis.com/v1/tile?cb_client=maps_sv.tactile&panoid=${panoid}&x=${x}&y=${y}&zoom=${zoom}&nbt=1&fover=0`, (image) => {
|
|
paintImage(image, x, y, heightPX, widthPX, tileSize, height);
|
|
processedTiles++;
|
|
if (processedTiles === totalTiles) {
|
|
ID("infoStatus").textContent = "finished";
|
|
}
|
|
});
|
|
}
|
|
}
|
|
};
|
|
request.open("get", `https://www.google.com/maps/photometa/v1?authuser=0&pb=!1m4!1smaps_sv.tactile!11m2!2m1!1b1!2m2!1sen!2sus!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`, true);
|
|
request.send();
|
|
|
|
}
|
|
|
|
//
|
|
// PHOTOSHPERE OBTAINING LOGIC
|
|
//
|
|
|
|
function getSphere(sphereID) {
|
|
ID("infoStatus").textContent = "Getting data from server...";
|
|
ID("infoID").textContent = sphereID;
|
|
ID("infoType").textContent = `Photo Sphere`;
|
|
var request = new XMLHttpRequest();
|
|
request.onload = function () {
|
|
ID("infoStatus").textContent = "Parsing data...";
|
|
// Metadata parsing witchcraft
|
|
const data = JSON.parse(this.responseText.substring(4))[1][0];
|
|
const tileSize = data[2][3][1][0];
|
|
const bestZoom = (data[2][3][0].length - 1);
|
|
|
|
if (bestZoom < zoom || zoom < 0) {
|
|
zoom = bestZoom;
|
|
document.getElementById("zoom").value = bestZoom;
|
|
}
|
|
|
|
const widthPX = data[2][3][0][zoom][0][1];
|
|
const heightPX = data[2][3][0][zoom][0][0];
|
|
const width = Math.ceil(widthPX / tileSize);
|
|
const height = Math.ceil(heightPX / tileSize);
|
|
|
|
prepCanvas(widthPX, heightPX);
|
|
|
|
const copyright = data[4][1][0][0];
|
|
ID("infoZooms").textContent = bestZoom;
|
|
ID("infoCopyright").textContent = copyright;
|
|
|
|
prepCanvas(widthPX, heightPX);
|
|
|
|
ID("infoStatus").textContent = "Downloading tiles";
|
|
const totalTiles = width * height;
|
|
var processedTiles = 0;
|
|
for (let x = 0; x < width; x++) {
|
|
for (let y = 0; y < height; y++) {
|
|
requestImage(`https://lh3.ggpht.com/p/${sphereID}=x${x}-y${y}-z${zoom}`, (image) => {
|
|
paintImage(image, x, y, heightPX, widthPX, tileSize, height);
|
|
processedTiles++;
|
|
if (processedTiles === totalTiles) {
|
|
ID("infoStatus").textContent = "finished";
|
|
}
|
|
});
|
|
}
|
|
}
|
|
};
|
|
request.open("get", `https://www.google.com/maps/photometa/v1?authuser=0&hl=en&gl=us&pb=!1m4!1smaps_sv.tactile!11m2!2m1!1b1!2m2!1sen!2sus!3m3!1m2!1e10!2s${sphereID}!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`, true);
|
|
request.send();
|
|
|
|
}
|
|
|
|
function requestImage(url, callback) {
|
|
var req = new XMLHttpRequest();
|
|
req.responseType = "arraybuffer";
|
|
req.onload = function () {
|
|
const blob = new Blob([this.response], { type: 'application/octet-binary' });
|
|
var url = window.URL.createObjectURL(blob);
|
|
const image = new Image();
|
|
image.src = url;
|
|
image.onload = () => {
|
|
callback(image);
|
|
};
|
|
};
|
|
req.open("get", url, true);
|
|
req.send();
|
|
}
|
|
|
|
function prepCanvas(widthPX, heightPX) {
|
|
canvas.width = widthPX;
|
|
canvas2.width = widthPX;
|
|
|
|
if (heightPX > maxCanvasHeight) {
|
|
canvas2.height = heightPX / 2;
|
|
canvas.height = heightPX / 2;
|
|
} else {
|
|
canvas2.height = 0;
|
|
canvas2.width = 0;
|
|
canvas.height = heightPX;
|
|
}
|
|
}
|
|
|
|
function paintImage(img, x, y, heightPX, widthPX, tileSize, height) {
|
|
if (heightPX > maxCanvasHeight) {
|
|
if ((y > ((height / 2) - 1)) && height !== 1) {
|
|
ctx2.drawImage(img, x * tileSize, y * tileSize - (heightPX / 2));
|
|
} else {
|
|
ctx.drawImage(img, x * tileSize, y * tileSize);
|
|
}
|
|
} else {
|
|
ctx.drawImage(img, x * tileSize, y * tileSize);
|
|
}
|
|
}
|