Feature: 360-view, experimenting with photo-sphere-viewer

This commit is contained in:
Pieter Vander Vennet 2025-03-16 16:31:38 +01:00
parent fb4fbe2be3
commit 4a45c650be
7 changed files with 265 additions and 29 deletions

View file

@ -325,7 +325,7 @@ class MapillaryFetcher implements ImageFetcher {
private readonly end_captured_at?: Date
constructor(options?: {
panoramas: undefined | "only" | "no"
panoramas?: undefined | "only" | "no"
max_images?: 100 | number
start_captured_at?: Date
end_captured_at?: Date
@ -411,7 +411,6 @@ export class CombinedFetcher {
new ImagesFromPanoramaxFetcher(),
new ImagesFromPanoramaxFetcher(Constants.panoramax.url),
new MapillaryFetcher({
panoramas: "no",
max_images: 25,
start_captured_at: maxage,
}),

View file

@ -0,0 +1,54 @@
import {
EquirectangularTilesAdapter,
EquirectangularTilesAdapterConfig
} from "@photo-sphere-viewer/equirectangular-tiles-adapter"
export class PhotoAdapter extends EquirectangularTilesAdapter {
// This code was shamelessly stolen from https://gitlab.com/panoramax/clients/web-viewer/-/blob/develop/src/utils/PhotoAdapter.js
// MIT-license; thank you adrien!
private readonly _shouldGoFast: () => boolean
constructor(viewer, config?: EquirectangularTilesAdapterConfig & { shouldGoFast?: () => boolean }) {
super(viewer, config)
this._shouldGoFast = config?.shouldGoFast ?? (() => true)
}
/**
* Override to skip loading SD images according to shouldGoFast option.
*/
public async loadTexture(panorama, loader) {
if (!panorama.origBaseUrl) {
panorama.origBaseUrl = panorama.baseUrl
} else {
panorama.baseUrl = panorama.origBaseUrl
}
// Fast mode + thumbnail available + no HD image loaded yet + flat picture
if (
this._shouldGoFast()
&& panorama.thumbUrl
&& !panorama.hdLoaded
&& panorama.rows == 1
) {
panorama.baseUrl = panorama.thumbUrl
}
let data = await super.loadTexture(panorama, loader)
if (panorama.baseUrl === panorama.origBaseUrl) {
panorama.hdLoaded = true
}
return data
}
/**
* Override to skip loading tiles according to shouldGoFast option.
* @private
*/
/*
private __loadTiles(tiles) {
if (!this._shouldGoFast()) {
super.__loadTiles(tiles)
}
}*/
}

View file

@ -1,34 +1,113 @@
<script lang="ts">
import { GeoLocationState } from "../Logic/State/GeoLocationState"
let origlog = console.log
import { onMount } from "svelte"
let logs: string[] = []
export let imageInfo
// Tiles of the panorama, not geotiles
let tilemeta = imageInfo?.asset_templates?.tiles_webp || imageInfo?.asset_templates?.tiles
function log(...items) {
origlog(...items)
logs.push(items.join(" "))
import "@photo-sphere-viewer/core/index.css"
// import "@photo-sphere-viewer/virtual-tour-plugin/index.css"
// import "@photo-sphere-viewertileUrl/gallery-plugin/index.css"
// import "@photo-sphere-viewer/markers-plugin/index.css"
import { AbstractAdapter, Viewer as PSViewer } from "photo-sphere-viewer"
import { PhotoAdapter } from "./Image/photoAdapter"
export const PSV_DEFAULT_ZOOM = 30
export const PSV_ANIM_DURATION = 250
export const PIC_MAX_STAY_DURATION = 3000
const BASE_PANORAMA = {
baseUrl: "./assets/loader_base.jpg",
width: 1280,
cols: 2,
rows: 1,
tileUrl: () => null
}
console.log = log
console.error = (...items) => log("ERR: ", ...items)
console.warn = (...items) => log("WARN: ", ...items)
const st = new GeoLocationState()
const av = st.gpsAvailable
const loc = st.currentGPSLocation
const permission = st.permission
let container: HTMLElement
let isSmall = () => container?.offsetWidth < 576
let shouldGoFast = () => true
function constructPanoramaInfo() {
const f = imageInfo
const hdUrl = (Object.values(f.assets).find(a => a?.roles?.includes("data")) || {}).href
const matrix = f?.properties?.["tiles:tile_matrix_sets"]?.geovisio
const baseUrlWebp = Object.values(f.assets).find(a => a.roles?.includes("visual") && a.type === "image/webp")
const baseUrlJpeg = Object.values(f.assets).find(a => a.roles?.includes("visual") && a.type === "image/jpeg")
const baseUrl = (baseUrlWebp || baseUrlJpeg).href
const thumbUrl = (Object.values(f.assets).find(a => a.roles?.includes("thumbnail") && a.type === "image/jpeg"))?.href
let panorama = {
baseUrl,
origBaseUrl: baseUrl,
basePanoData: (img) => ({
fullWidth: img.width,
fullHeight: img.height
}),
hdUrl,
thumbUrl,
cols: matrix && matrix.tileMatrix[0].matrixWidth,
rows: matrix && matrix.tileMatrix[0].matrixHeight,
width: matrix && (matrix.tileMatrix[0].matrixWidth * matrix.tileMatrix[0].tileWidth),
tileUrl: matrix && ((col, row) => tilemeta.href.replace(/\{TileCol}/g, col).replace(/\{TileRow}/g, row))
}
return panorama
}
PSViewer["useNewAnglesOrder"] = true
onMount(() => {
const viewer = new PSViewer({
container,
panorama: BASE_PANORAMA.baseUrl,
adapter: [{ PhotoAdapter, prototype: AbstractAdapter }
, {
showErrorTile: false,
baseBlur: false,
resolution: isSmall() ? 32 : 64
// shouldGoFast
}],
//withCredentials: parent._options?.fetchOptions?.credentials == "include",
//requestHeaders: parent._options?.fetchOptions?.headers,
//lang: parent._t.psv,
minFov: 5,
loadingTxt: "&nbsp;",
navbar: null
})
const panorama = constructPanoramaInfo()
console.log(panorama, container)
viewer.setOptions({
adapter: [PhotoAdapter, {
showErrorTile: false,
baseBlur: false,
resolution: isSmall() ? 32 : 64
}]
})
viewer.setPanorama(panorama.hdUrl, {
zoom: 0, longitude: 45, latitude: -45
})
window.setTimeout(() => {
console.log(viewer.getPosition())
}, 2000)
})
</script>
<button on:click={() => st.requestPermission()}>Get geolocation</button>
Permission:
<code>{$permission}</code>
Available:
<code>{$av}</code>
Location:
<code>{JSON.stringify($loc)}</code>
<ol>
{#each logs as log}
<li>{log}</li>
{/each}
</ol>
<div bind:this={container} class="h-screen w-screen"></div>

View file

@ -1,4 +1,13 @@
import Test from "./UI/Test.svelte"
import { OsmConnection } from "./Logic/Osm/OsmConnection"
import { ImageData, PanoramaxXYZ } from "panoramax-js"
const target = document.getElementById("maindiv")
target.innerHTML = ""
let img = "https://panoramax-storage-public-fast.s3.gra.perf.cloud.ovh.net/main-pictures/d2/8c/ba/cf/c807-4dbf-b8c8-b1c3aa89182d.jpg"
let imgId = "d28cbacf-c807-4dbf-b8c8-b1c3aa89182d"
let panoramax = new PanoramaxXYZ()
panoramax.imageInfo(imgId).then((imageInfo: ImageData) => {
console.log("IMG INFO: ", imageInfo)
new Test({ target, props: { imageInfo } })
})
new OsmConnection().interact("user/details.json").then((r) => console.log(">>>", r))