| 
									
										
										
										
											2023-12-05 18:35:18 +01:00
										 |  |  | <script lang="ts"> | 
					
						
							| 
									
										
										
										
											2023-12-19 22:08:00 +01:00
										 |  |  |   /** | 
					
						
							|  |  |  |    * Shows an image with attribution | 
					
						
							|  |  |  |    */ | 
					
						
							|  |  |  |   import ImageAttribution from "./ImageAttribution.svelte" | 
					
						
							| 
									
										
										
										
											2025-03-30 03:10:29 +02:00
										 |  |  |   import { Store } from "../../Logic/UIEventSource" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-04-10 04:53:01 +02:00
										 |  |  |   import type { HotspotProperties, ProvidedImage } from "../../Logic/ImageProviders/ImageProvider" | 
					
						
							| 
									
										
										
										
											2023-12-19 22:08:00 +01:00
										 |  |  |   import { Mapillary } from "../../Logic/ImageProviders/Mapillary" | 
					
						
							| 
									
										
										
										
											2023-12-19 23:02:02 +01:00
										 |  |  |   import { UIEventSource } from "../../Logic/UIEventSource" | 
					
						
							| 
									
										
										
										
											2024-08-28 15:07:18 +02:00
										 |  |  |   import { MagnifyingGlassPlusIcon } from "@babeard/svelte-heroicons/outline" | 
					
						
							| 
									
										
										
										
											2024-09-12 01:31:00 +02:00
										 |  |  |   import { CloseButton } from "flowbite-svelte" | 
					
						
							| 
									
										
										
										
											2024-09-04 01:50:34 +02:00
										 |  |  |   import ImageOperations from "./ImageOperations.svelte" | 
					
						
							|  |  |  |   import Popup from "../Base/Popup.svelte" | 
					
						
							|  |  |  |   import { onDestroy } from "svelte" | 
					
						
							| 
									
										
										
										
											2025-04-10 04:53:01 +02:00
										 |  |  |   import type { Feature, Geometry, Point } from "geojson" | 
					
						
							| 
									
										
										
										
											2024-09-28 02:04:14 +02:00
										 |  |  |   import Loading from "../Base/Loading.svelte" | 
					
						
							|  |  |  |   import Translations from "../i18n/Translations" | 
					
						
							|  |  |  |   import Tr from "../Base/Tr.svelte" | 
					
						
							| 
									
										
										
										
											2024-11-05 00:18:16 +01:00
										 |  |  |   import DotMenu from "../Base/DotMenu.svelte" | 
					
						
							| 
									
										
										
										
											2025-01-17 16:01:40 +01:00
										 |  |  |   import LoadingPlaceholder from "../Base/LoadingPlaceholder.svelte" | 
					
						
							| 
									
										
										
										
											2025-01-23 12:29:54 +01:00
										 |  |  |   import { MenuState } from "../../Models/MenuState" | 
					
						
							| 
									
										
										
										
											2025-03-13 16:53:12 +01:00
										 |  |  |   import ThemeViewState from "../../Models/ThemeViewState" | 
					
						
							| 
									
										
										
										
											2025-04-09 23:30:39 +02:00
										 |  |  |   import Panorama360 from "../../assets/svg/Panorama360.svelte" | 
					
						
							| 
									
										
										
										
											2025-04-23 21:35:43 +02:00
										 |  |  |   import { ExternalLinkIcon } from "@rgossiaux/svelte-heroicons/solid" | 
					
						
							| 
									
										
										
										
											2023-12-05 18:35:18 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-03-13 16:53:12 +01:00
										 |  |  |   export let image: Partial<ProvidedImage> & { id: string; url: string } | 
					
						
							| 
									
										
										
										
											2023-12-19 22:08:00 +01:00
										 |  |  |   let fallbackImage: string = undefined | 
					
						
							| 
									
										
										
										
											2025-04-28 01:58:00 +02:00
										 |  |  |   if (image?.provider === Mapillary.singleton) { | 
					
						
							| 
									
										
										
										
											2023-12-19 22:08:00 +01:00
										 |  |  |     fallbackImage = "./assets/svg/blocked.svg" | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2023-12-05 18:35:18 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-12-19 22:08:00 +01:00
										 |  |  |   let imgEl: HTMLImageElement | 
					
						
							|  |  |  |   export let imgClass: string = undefined | 
					
						
							| 
									
										
										
										
											2025-03-13 16:53:12 +01:00
										 |  |  |   export let state: ThemeViewState = undefined | 
					
						
							| 
									
										
										
										
											2024-08-28 15:07:18 +02:00
										 |  |  |   export let attributionFormat: "minimal" | "medium" | "large" = "medium" | 
					
						
							| 
									
										
										
										
											2025-01-23 12:29:54 +01:00
										 |  |  |   let previewedImage: UIEventSource<Partial<ProvidedImage>> = MenuState.previewedImage | 
					
						
							| 
									
										
										
										
											2024-09-04 01:50:34 +02:00
										 |  |  |   export let canZoom = previewedImage !== undefined | 
					
						
							| 
									
										
										
										
											2025-04-15 18:18:44 +02:00
										 |  |  |   export let nearbyFeatures: | 
					
						
							|  |  |  |     | Feature<Geometry, HotspotProperties>[] | 
					
						
							|  |  |  |     | Store<Feature<Geometry, HotspotProperties>[]> = [] | 
					
						
							| 
									
										
										
										
											2025-03-30 03:10:29 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-08-29 23:12:25 +02:00
										 |  |  |   let loaded = false | 
					
						
							| 
									
										
										
										
											2025-04-28 01:58:00 +02:00
										 |  |  |   let visitUrl = image.provider?.visitUrl(image) | 
					
						
							| 
									
										
										
										
											2024-09-12 01:31:00 +02:00
										 |  |  |   let showBigPreview = new UIEventSource(false) | 
					
						
							| 
									
										
										
										
											2024-10-19 14:44:55 +02:00
										 |  |  |   onDestroy( | 
					
						
							|  |  |  |     showBigPreview.addCallbackAndRun((shown) => { | 
					
						
							| 
									
										
										
										
											2025-03-13 16:53:12 +01:00
										 |  |  |       state.guistate.setPreviewedImage(shown ? image : undefined) | 
					
						
							| 
									
										
										
										
											2024-12-18 01:21:07 +01:00
										 |  |  |     }) | 
					
						
							| 
									
										
										
										
											2024-10-19 14:44:55 +02:00
										 |  |  |   ) | 
					
						
							| 
									
										
										
										
											2024-12-11 02:45:44 +01:00
										 |  |  |   if (previewedImage) { | 
					
						
							|  |  |  |     onDestroy( | 
					
						
							|  |  |  |       previewedImage.addCallbackAndRun((previewedImage) => { | 
					
						
							| 
									
										
										
										
											2024-12-18 01:21:07 +01:00
										 |  |  |         showBigPreview.set( | 
					
						
							|  |  |  |           previewedImage !== undefined && | 
					
						
							| 
									
										
										
										
											2025-04-15 18:18:44 +02:00
										 |  |  |             (previewedImage?.id ?? previewedImage?.url) === (image.id ?? image.url) | 
					
						
							| 
									
										
										
										
											2024-12-18 01:21:07 +01:00
										 |  |  |         ) | 
					
						
							|  |  |  |       }) | 
					
						
							| 
									
										
										
										
											2024-12-11 02:45:44 +01:00
										 |  |  |     ) | 
					
						
							| 
									
										
										
										
											2024-12-01 01:39:13 +01:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2024-09-12 01:31:00 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |   function highlight(entered: boolean = true) { | 
					
						
							|  |  |  |     if (!entered) { | 
					
						
							|  |  |  |       state?.geocodedImages.set([]) | 
					
						
							|  |  |  |       return | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (isNaN(image.lon) || isNaN(image.lat)) { | 
					
						
							|  |  |  |       return | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     const f: Feature<Point> = { | 
					
						
							|  |  |  |       type: "Feature", | 
					
						
							|  |  |  |       properties: { | 
					
						
							|  |  |  |         id: image.id, | 
					
						
							| 
									
										
										
										
											2025-04-15 18:18:44 +02:00
										 |  |  |         rotation: image.rotation, | 
					
						
							| 
									
										
										
										
											2024-09-12 01:31:00 +02:00
										 |  |  |       }, | 
					
						
							|  |  |  |       geometry: { | 
					
						
							|  |  |  |         type: "Point", | 
					
						
							| 
									
										
										
										
											2025-04-15 18:18:44 +02:00
										 |  |  |         coordinates: [image.lon, image.lat], | 
					
						
							|  |  |  |       }, | 
					
						
							| 
									
										
										
										
											2024-09-12 01:31:00 +02:00
										 |  |  |     } | 
					
						
							|  |  |  |     state?.geocodedImages.set([f]) | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2023-12-05 18:35:18 +01:00
										 |  |  | </script> | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-01-24 03:20:31 +01:00
										 |  |  | <Popup shown={showBigPreview} bodyPadding="p-0" dismissable={true}> | 
					
						
							| 
									
										
										
										
											2024-09-04 01:50:34 +02:00
										 |  |  |   <div style="height: 80vh"> | 
					
						
							| 
									
										
										
										
											2025-03-30 03:10:29 +02:00
										 |  |  |     <ImageOperations {image} {nearbyFeatures}> | 
					
						
							| 
									
										
										
										
											2024-09-04 01:50:34 +02:00
										 |  |  |       <slot name="preview-action" /> | 
					
						
							| 
									
										
										
										
											2024-11-05 00:18:16 +01:00
										 |  |  |       <slot name="dot-menu-actions" slot="dot-menu-actions" /> | 
					
						
							| 
									
										
										
										
											2024-09-04 01:50:34 +02:00
										 |  |  |     </ImageOperations> | 
					
						
							|  |  |  |   </div> | 
					
						
							| 
									
										
										
										
											2025-02-10 02:04:58 +01:00
										 |  |  |   <div class="absolute right-4 top-4"> | 
					
						
							| 
									
										
										
										
											2024-10-19 14:44:55 +02:00
										 |  |  |     <CloseButton | 
					
						
							|  |  |  |       class="normal-background" | 
					
						
							|  |  |  |       on:click={() => { | 
					
						
							| 
									
										
										
										
											2024-12-01 01:39:13 +01:00
										 |  |  |         previewedImage?.set(undefined) | 
					
						
							| 
									
										
										
										
											2024-10-19 14:44:55 +02:00
										 |  |  |       }} | 
					
						
							|  |  |  |     /> | 
					
						
							| 
									
										
										
										
											2024-09-04 01:50:34 +02:00
										 |  |  |   </div> | 
					
						
							|  |  |  | </Popup> | 
					
						
							| 
									
										
										
										
											2024-12-17 19:03:05 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-11-05 00:18:16 +01:00
										 |  |  | {#if image.status !== undefined && image.status !== "ready" && image.status !== "hidden"} | 
					
						
							| 
									
										
										
										
											2024-10-19 14:44:55 +02:00
										 |  |  |   <div class="flex h-full flex-col justify-center"> | 
					
						
							| 
									
										
										
										
											2024-09-28 02:04:14 +02:00
										 |  |  |     <Loading> | 
					
						
							| 
									
										
										
										
											2024-10-19 14:44:55 +02:00
										 |  |  |       <Tr t={Translations.t.image.processing} /> | 
					
						
							| 
									
										
										
										
											2024-09-28 02:04:14 +02:00
										 |  |  |     </Loading> | 
					
						
							|  |  |  |   </div> | 
					
						
							| 
									
										
										
										
											2025-04-09 23:30:39 +02:00
										 |  |  | {:else if image.status !== "hidden"} | 
					
						
							| 
									
										
										
										
											2024-09-28 02:04:14 +02:00
										 |  |  |   <div class="relative shrink-0"> | 
					
						
							| 
									
										
										
										
											2024-10-19 14:44:55 +02:00
										 |  |  |     <div | 
					
						
							| 
									
										
										
										
											2025-04-09 23:30:39 +02:00
										 |  |  |       class={"relative w-fit"} | 
					
						
							| 
									
										
										
										
											2024-10-19 14:44:55 +02:00
										 |  |  |       on:mouseenter={() => highlight()} | 
					
						
							|  |  |  |       on:mouseleave={() => highlight(false)} | 
					
						
							| 
									
										
										
										
											2024-09-28 02:04:14 +02:00
										 |  |  |     > | 
					
						
							| 
									
										
										
										
											2025-04-23 21:35:43 +02:00
										 |  |  |       {#if $$slots["dot-menu-actions"] || visitUrl !== undefined} | 
					
						
							| 
									
										
										
										
											2024-11-05 00:18:16 +01:00
										 |  |  |         <DotMenu dotsPosition="top-0 left-0 absolute" hideBackground> | 
					
						
							|  |  |  |           <slot name="dot-menu-actions" /> | 
					
						
							| 
									
										
										
										
											2025-04-28 01:58:00 +02:00
										 |  |  |           {#if visitUrl !== undefined} | 
					
						
							| 
									
										
										
										
											2025-04-23 21:35:43 +02:00
										 |  |  |           <a href={visitUrl} target="_blank" rel="noopener"> | 
					
						
							|  |  |  |             <ExternalLinkIcon class="w-6" /> | 
					
						
							|  |  |  |             <Tr t={Translations.t.image.openOnWebsite.Subs(image.provider)} /> | 
					
						
							|  |  |  |           </a> | 
					
						
							| 
									
										
										
										
											2025-04-28 01:58:00 +02:00
										 |  |  |           {/if} | 
					
						
							| 
									
										
										
										
											2024-11-05 00:18:16 +01:00
										 |  |  |         </DotMenu> | 
					
						
							|  |  |  |       {/if} | 
					
						
							| 
									
										
										
										
											2025-01-17 16:01:40 +01:00
										 |  |  |       {#if !loaded} | 
					
						
							|  |  |  |         <LoadingPlaceholder /> | 
					
						
							|  |  |  |       {/if} | 
					
						
							| 
									
										
										
										
											2024-09-28 02:04:14 +02:00
										 |  |  |       <img | 
					
						
							| 
									
										
										
										
											2025-01-17 16:01:40 +01:00
										 |  |  |         class:hidden={!loaded} | 
					
						
							| 
									
										
										
										
											2024-09-28 02:04:14 +02:00
										 |  |  |         bind:this={imgEl} | 
					
						
							|  |  |  |         on:load={() => (loaded = true)} | 
					
						
							|  |  |  |         class={imgClass ?? ""} | 
					
						
							|  |  |  |         class:cursor-zoom-in={canZoom} | 
					
						
							|  |  |  |         on:click={() => { | 
					
						
							| 
									
										
										
										
											2024-10-19 14:44:55 +02:00
										 |  |  |           previewedImage?.set(image) | 
					
						
							|  |  |  |         }} | 
					
						
							| 
									
										
										
										
											2024-09-28 02:04:14 +02:00
										 |  |  |         on:error={() => { | 
					
						
							| 
									
										
										
										
											2024-10-19 14:44:55 +02:00
										 |  |  |           if (fallbackImage) { | 
					
						
							|  |  |  |             imgEl.src = fallbackImage | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  |         }} | 
					
						
							| 
									
										
										
										
											2024-09-28 02:04:14 +02:00
										 |  |  |         src={image.url} | 
					
						
							|  |  |  |       /> | 
					
						
							| 
									
										
										
										
											2023-12-05 18:35:18 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-04-09 23:30:39 +02:00
										 |  |  |       {#if image.isSpherical} | 
					
						
							| 
									
										
										
										
											2025-04-15 18:18:44 +02:00
										 |  |  |         <div | 
					
						
							|  |  |  |           class="pointer-events-none absolute left-0 top-0 flex h-full w-full items-center justify-center" | 
					
						
							|  |  |  |         > | 
					
						
							|  |  |  |           <div class="rounded-full bg-black p-[3.25rem] opacity-50"> | 
					
						
							|  |  |  |             <div class="relative flex h-0 w-0 items-center justify-center"> | 
					
						
							|  |  |  |               <Panorama360 class="absolute h-16 w-16" color="#ffffff" /> | 
					
						
							| 
									
										
										
										
											2025-04-09 23:30:39 +02:00
										 |  |  |             </div> | 
					
						
							|  |  |  |           </div> | 
					
						
							|  |  |  |         </div> | 
					
						
							|  |  |  |       {/if} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-09-28 02:04:14 +02:00
										 |  |  |       {#if canZoom && loaded} | 
					
						
							|  |  |  |         <div | 
					
						
							|  |  |  |           class="bg-black-transparent absolute right-0 top-0 rounded-bl-full" | 
					
						
							| 
									
										
										
										
											2024-12-01 01:39:13 +01:00
										 |  |  |           on:click={() => previewedImage?.set(image)} | 
					
						
							| 
									
										
										
										
											2024-10-19 14:44:55 +02:00
										 |  |  |         > | 
					
						
							| 
									
										
										
										
											2025-02-10 02:04:58 +01:00
										 |  |  |           <MagnifyingGlassPlusIcon class="h-8 w-8 cursor-zoom-in pb-3 pl-3" color="white" /> | 
					
						
							| 
									
										
										
										
											2024-09-28 02:04:14 +02:00
										 |  |  |         </div> | 
					
						
							|  |  |  |       {/if} | 
					
						
							|  |  |  |     </div> | 
					
						
							|  |  |  |     <div class="absolute bottom-0 left-0"> | 
					
						
							|  |  |  |       <ImageAttribution {image} {attributionFormat} /> | 
					
						
							|  |  |  |     </div> | 
					
						
							| 
									
										
										
										
											2023-12-05 18:35:18 +01:00
										 |  |  |   </div> | 
					
						
							| 
									
										
										
										
											2025-04-09 23:30:39 +02:00
										 |  |  | {:else if image.status === "hidden"} | 
					
						
							|  |  |  |   <div class="subtle">This image has been reported</div> | 
					
						
							| 
									
										
										
										
											2024-09-28 02:04:14 +02:00
										 |  |  | {/if} |