Skip to content

Instantly share code, notes, and snippets.

@darrenwiens
Created May 14, 2025 16:14
Show Gist options
  • Select an option

  • Save darrenwiens/c01d69ffffdf2ca0abf6f7d0484289a3 to your computer and use it in GitHub Desktop.

Select an option

Save darrenwiens/c01d69ffffdf2ca0abf6f7d0484289a3 to your computer and use it in GitHub Desktop.
A Marzipano frontend for creating a map cube
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Marzipano Map Cube</title>
<link rel="stylesheet" href="style.css">
<style>
html,
body {
height: 100%;
margin: 0;
}
#pano {
width: 100%;
height: 100%;
background: #000;
}
</style>
</head>
<body view-control-buttons>
<div id="pano"></div>
<script src="marzipano-0.10.2/marzipano.js"></script>
<script>
if (window.matchMedia) {
var setMode = function () {
if (mql.matches) {
document.body.classList.remove('desktop');
document.body.classList.add('mobile');
} else {
document.body.classList.remove('mobile');
document.body.classList.add('desktop');
}
};
var mql = matchMedia("(max-width: 500px), (max-height: 500px)");
setMode();
mql.addListener(setMode);
} else {
document.body.classList.add('desktop');
}
document.body.classList.add('no-touch');
window.addEventListener('touchstart', function () {
document.body.classList.remove('no-touch');
document.body.classList.add('touch');
});
var viewer = new Marzipano.Viewer(document.getElementById('pano'));
var geometry = new Marzipano.CubeGeometry([
{ tileSize: 512, size: 512 }
]);
let url = new URL(window.location.href);
let params = new URLSearchParams(url.search);
let x = params.get('x');
let y = params.get('y');
let z = params.get('z');
var source = Marzipano.ImageUrlSource.fromString(`http://127.0.0.1:8000/tile/${z}/${x}/${y}/{f}`, { // Adjust for your API
cubeMap: true,
cubeMapSize: 256
});
var limiter = Marzipano.RectilinearView.limit.traditional(16384, 100 * Math.PI / 180);
let defaultView = {
yaw: 0,
pitch: Math.PI / 2,
}
var view = new Marzipano.RectilinearView(defaultView, limiter);
var scene = viewer.createScene({
source: source,
geometry: geometry,
view: view,
pinFirstLevel: true
});
let linkHotspots = [
{
"yaw": Math.PI,
"pitch": 0,
"rotation": 0,
"target": `?x=${x}&y=${parseInt(y) + 1}&z=${z}`,
"direction": "South"
},
{
"yaw": Math.PI * 1.25,
"pitch": 0,
"rotation": 0,
"target": `?x=${parseInt(x) - 1}&y=${parseInt(y) + 1}&z=${z}`,
"direction": "Southwest"
},
{
"yaw": Math.PI * 1.5,
"pitch": 0,
"rotation": 0,
"target": `?x=${parseInt(x) - 1}&y=${y}&z=${z}`,
"direction": "West"
},
{
"yaw": Math.PI * 1.75,
"pitch": 0,
"rotation": 0,
"target": `?x=${parseInt(x) - 1}&y=${parseInt(y) - 1}&z=${z}`,
"direction": "Nortwest"
},
{
"yaw": 0,
"pitch": 0,
"rotation": 0,
"target": `?x=${x}&y=${parseInt(y) - 1}&z=${z}`,
"direction": "North"
},
{
"yaw": Math.PI * 0.25,
"pitch": 0,
"rotation": 0,
"target": `?x=${parseInt(x) + 1}&y=${parseInt(y) - 1}&z=${z}`,
"direction": "Northeast"
},
{
"yaw": Math.PI * 0.5,
"pitch": 0,
"rotation": 0,
"target": `?x=${parseInt(x) + 1}&y=${y}&z=${z}`,
"direction": "East"
},
{
"yaw": Math.PI * 0.75,
"pitch": 0,
"rotation": 0,
"target": `?x=${parseInt(x) + 1}&y=${parseInt(y) + 1}&z=${z}`,
"direction": "Southeast"
},
]
let infoHotspots = [
{
"yaw": 0,
"pitch": Math.PI / 2,
"title": "Current Location",
"text": `You are here. This is map tile ${z}/${x}/${y}. Click one of the arrow hotspots to navigate to a different tile.`,
}
]
linkHotspots.forEach(function (hotspot) {
var element = createLinkHotspotElement(hotspot);
scene.hotspotContainer().createHotspot(element, { yaw: hotspot.yaw, pitch: hotspot.pitch });
});
infoHotspots.forEach(function (hotspot) {
var element = createInfoHotspotElement(hotspot);
scene.hotspotContainer().createHotspot(element, { yaw: hotspot.yaw, pitch: hotspot.pitch });
});
scene.switchTo();
function createLinkHotspotElement(hotspot) {
var wrapper = document.createElement('div');
wrapper.classList.add('hotspot');
wrapper.classList.add('link-hotspot');
var icon = document.createElement('img');
icon.src = 'link.png';
icon.classList.add('link-hotspot-icon');
var transformProperties = ['-ms-transform', '-webkit-transform', 'transform'];
for (var i = 0; i < transformProperties.length; i++) {
var property = transformProperties[i];
icon.style[property] = 'rotate(' + hotspot.rotation + 'rad)';
}
wrapper.addEventListener('click', function () {
window.location.href = hotspot.target;
});
stopTouchAndScrollEventPropagation(wrapper);
var tooltip = document.createElement('div');
tooltip.classList.add('hotspot-tooltip');
tooltip.classList.add('link-hotspot-tooltip');
tooltip.innerHTML = hotspot.direction
wrapper.appendChild(icon);
wrapper.appendChild(tooltip);
return wrapper;
}
function createInfoHotspotElement(hotspot) {
var wrapper = document.createElement('div');
wrapper.classList.add('hotspot');
wrapper.classList.add('info-hotspot');
var header = document.createElement('div');
header.classList.add('info-hotspot-header');
var iconWrapper = document.createElement('div');
iconWrapper.classList.add('info-hotspot-icon-wrapper');
var icon = document.createElement('img');
icon.src = 'info.png';
icon.classList.add('info-hotspot-icon');
iconWrapper.appendChild(icon);
var titleWrapper = document.createElement('div');
titleWrapper.classList.add('info-hotspot-title-wrapper');
var title = document.createElement('div');
title.classList.add('info-hotspot-title');
title.innerHTML = hotspot.title;
titleWrapper.appendChild(title);
var closeWrapper = document.createElement('div');
closeWrapper.classList.add('info-hotspot-close-wrapper');
var closeIcon = document.createElement('img');
closeIcon.src = 'close.png';
closeIcon.classList.add('info-hotspot-close-icon');
closeWrapper.appendChild(closeIcon);
header.appendChild(iconWrapper);
header.appendChild(titleWrapper);
header.appendChild(closeWrapper);
var text = document.createElement('div');
text.classList.add('info-hotspot-text');
text.innerHTML = hotspot.text;
wrapper.appendChild(header);
wrapper.appendChild(text);
var modal = document.createElement('div');
modal.innerHTML = wrapper.innerHTML;
modal.classList.add('info-hotspot-modal');
document.body.appendChild(modal);
var toggle = function () {
wrapper.classList.toggle('visible');
modal.classList.toggle('visible');
};
wrapper.querySelector('.info-hotspot-header').addEventListener('click', toggle);
modal.querySelector('.info-hotspot-close-wrapper').addEventListener('click', toggle);
stopTouchAndScrollEventPropagation(wrapper);
return wrapper;
}
function stopTouchAndScrollEventPropagation(element, eventList) {
var eventList = [
'touchstart', 'touchmove', 'touchend', 'touchcancel',
'pointerdown', 'pointermove', 'pointerup', 'pointercancel',
'wheel'
];
for (var i = 0; i < eventList.length; i++) {
element.addEventListener(eventList[i], function (event) {
event.stopPropagation();
});
}
}
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment