forked from MapComplete/MapComplete
		
	
		
			
				
	
	
		
			116 lines
		
	
	
	
		
			4.2 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			116 lines
		
	
	
	
		
			4.2 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
| import "pannellum"
 | |
| 
 | |
| import { Feature, Geometry, Point } from "geojson"
 | |
| import { GeoOperations } from "../../Logic/GeoOperations"
 | |
| import { HotspotProperties, PanoramaView } from "../../Logic/ImageProviders/ImageProvider"
 | |
| 
 | |
| export class PhotoSphereViewerWrapper {
 | |
|     private imageInfo: Feature<Point, PanoramaView>
 | |
|     private readonly viewer: Pannellum.Viewer
 | |
|     private nearbyFeatures: Feature<Geometry, HotspotProperties>[] = []
 | |
| 
 | |
|     constructor(
 | |
|         container: HTMLElement,
 | |
|         imageInfo: Feature<Point, PanoramaView>,
 | |
|         nearbyFeatures?: Feature<Geometry, HotspotProperties>[]
 | |
|     ) {
 | |
|         this.imageInfo = imageInfo
 | |
|         this.viewer = pannellum.viewer(container, <any>{
 | |
|             default: {
 | |
|                 firstScene: imageInfo.properties.url,
 | |
|                 sceneFadeDuration: 250,
 | |
|             },
 | |
|             scenes: {
 | |
|                 [imageInfo.properties.url]: {
 | |
|                     type: "equirectangular",
 | |
|                     hfov: 110,
 | |
|                     panorama: imageInfo.properties.url,
 | |
|                     autoLoad: true,
 | |
|                     hotSpots: [],
 | |
|                     sceneFadeDuration: 250,
 | |
|                     compass: true,
 | |
|                     showControls: false,
 | |
|                     northOffset: imageInfo.properties.northOffset,
 | |
|                     horizonPitch: imageInfo.properties.pitchOffset,
 | |
|                 },
 | |
|             },
 | |
|         })
 | |
| 
 | |
|         this.setNearbyFeatures(nearbyFeatures)
 | |
|     }
 | |
| 
 | |
|     public calculatePitch(feature: Feature): number {
 | |
|         const coors = this.imageInfo.geometry.coordinates
 | |
|         const distance = GeoOperations.distanceBetween(
 | |
|             <[number, number]>coors,
 | |
|             GeoOperations.centerpointCoordinates(feature)
 | |
|         )
 | |
| 
 | |
|         // In: -pi/2 up to pi/2
 | |
|         const alpha = Math.atan(distance / 4) // in radians
 | |
|         const degrees = (alpha * 360) / (2 * Math.PI)
 | |
|         return -degrees
 | |
|     }
 | |
| 
 | |
|     private setPanorama(imageInfo: Feature<Point, PanoramaView>) {
 | |
|         if (this.viewer?.getScene() === imageInfo?.properties?.url) {
 | |
|             // Already the current scene
 | |
|             return
 | |
|         }
 | |
|         this.clearHotspots()
 | |
|         this.imageInfo = imageInfo
 | |
|         this.viewer.addScene(imageInfo.properties.url, <any>{
 | |
|             panorama: imageInfo.properties.url,
 | |
|             northOffset: imageInfo.properties.northOffset,
 | |
|             type: "equirectangular",
 | |
|         })
 | |
| 
 | |
|         this.viewer.loadScene(imageInfo.properties.url, 0, imageInfo.properties.northOffset)
 | |
|         this.setNearbyFeatures(this.nearbyFeatures)
 | |
|     }
 | |
| 
 | |
|     private clearHotspots() {
 | |
|         const hotspots =
 | |
|             this.viewer.getConfig()["scenes"][this.imageInfo.properties.url].hotSpots ?? []
 | |
|         for (const hotspot of hotspots) {
 | |
|             this.viewer.removeHotSpot(hotspot?.id, this.imageInfo.properties.url)
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     public setNearbyFeatures(nearbyFeatures: Feature<Geometry, HotspotProperties>[]) {
 | |
|         const imageInfo = this.imageInfo
 | |
|         if (!this.imageInfo) {
 | |
|             return
 | |
|         }
 | |
|         const northOffs = imageInfo.properties.northOffset
 | |
|         this.nearbyFeatures = nearbyFeatures
 | |
|         this.clearHotspots()
 | |
|         for (const f of nearbyFeatures ?? []) {
 | |
|             if (f.properties.gotoPanorama?.properties?.url === this.imageInfo.properties.url) {
 | |
|                 continue // This is the current panorama, no need to show it
 | |
|             }
 | |
|             const yaw = GeoOperations.bearing(imageInfo, GeoOperations.centerpoint(f))
 | |
|             let pitch = 0
 | |
|             if (f.properties.pitch === "auto") {
 | |
|                 pitch = this.calculatePitch(f)
 | |
|             } else if (!isNaN(f.properties.pitch)) {
 | |
|                 pitch = f.properties.pitch
 | |
|             }
 | |
|             this.viewer.addHotSpot(
 | |
|                 {
 | |
|                     type: f.properties.gotoPanorama !== undefined ? "scene" : "info",
 | |
|                     yaw: (yaw - northOffs) % 360,
 | |
|                     pitch,
 | |
|                     text: f.properties.name,
 | |
|                     clickHandlerFunc: () => {
 | |
|                         this.setPanorama(f.properties.gotoPanorama)
 | |
|                     },
 | |
|                 },
 | |
|                 this.imageInfo.properties.url
 | |
|             )
 | |
|             if (f.properties.focus) {
 | |
|                 this.viewer.setYaw(yaw - northOffs)
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| }
 |