Created
May 14, 2025 16:14
-
-
Save darrenwiens/c01d69ffffdf2ca0abf6f7d0484289a3 to your computer and use it in GitHub Desktop.
A Marzipano frontend for creating a map cube
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <!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