From e81b0d10ea3b6fb5d78d4415e42885189f9372dc Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Wed, 9 Apr 2025 23:30:39 +0200 Subject: [PATCH] Feature(360): actually show spheres when it is already linked --- assets/svg/Panorama360.svg | 18 +++++++++++++ assets/svg/Panorama360.svg.license | 2 ++ assets/svg/license_info.json | 8 ++++++ public/css/index-tailwind-output.css | 9 +++++++ src/Logic/ImageProviders/ImageProvider.ts | 2 +- src/Logic/ImageProviders/Imgur.ts | 1 + src/Logic/ImageProviders/Mapillary.ts | 18 ++++++++----- src/Logic/ImageProviders/Panoramax.ts | 1 + .../ImageProviders/WikidataImageProvider.ts | 11 ++++++-- .../ImageProviders/WikimediaImageProvider.ts | 9 +++++-- src/Logic/Web/NearbyImagesSearch.ts | 5 ++-- src/UI/Image/AttributedImage.svelte | 26 ++++++++++++++----- src/UI/Image/DeletableImage.svelte | 11 +++++--- src/UI/Image/ImageCarousel.svelte | 23 +++++++++++++--- src/UI/Image/ImagePreview.svelte | 2 +- .../ImageVisualisations.ts | 4 +-- src/assets/svg/Panorama360.svelte | 4 +++ 17 files changed, 124 insertions(+), 30 deletions(-) create mode 100644 assets/svg/Panorama360.svg create mode 100644 assets/svg/Panorama360.svg.license create mode 100644 src/assets/svg/Panorama360.svelte diff --git a/assets/svg/Panorama360.svg b/assets/svg/Panorama360.svg new file mode 100644 index 0000000000..4b45c66ae6 --- /dev/null +++ b/assets/svg/Panorama360.svg @@ -0,0 +1,18 @@ + + + + diff --git a/assets/svg/Panorama360.svg.license b/assets/svg/Panorama360.svg.license new file mode 100644 index 0000000000..ed02883002 --- /dev/null +++ b/assets/svg/Panorama360.svg.license @@ -0,0 +1,2 @@ +SPDX-FileCopyrightText: Pieter Vander Vennet +SPDX-License-Identifier: CC0-1.0 \ No newline at end of file diff --git a/assets/svg/license_info.json b/assets/svg/license_info.json index e94c29d518..e493a9b2f5 100644 --- a/assets/svg/license_info.json +++ b/assets/svg/license_info.json @@ -1,4 +1,12 @@ [ + { + "path": "Panorama360.svg", + "license": "CC0-1.0", + "authors": [ + "Pieter Vander Vennet" + ], + "sources": [] + }, { "path": "SocialImageForeground.svg", "license": "CC-BY-SA-4.0", diff --git a/public/css/index-tailwind-output.css b/public/css/index-tailwind-output.css index 62aec36737..2ac8e2f366 100644 --- a/public/css/index-tailwind-output.css +++ b/public/css/index-tailwind-output.css @@ -3354,6 +3354,11 @@ input[type="range"].range-lg::-moz-range-thumb { background-color: rgb(249 250 251 / var(--tw-bg-opacity, 1)) !important; } +.bg-black { + --tw-bg-opacity: 1; + background-color: rgb(0 0 0 / var(--tw-bg-opacity, 1)); +} + .bg-blue-100 { --tw-bg-opacity: 1; background-color: rgb(225 239 254 / var(--tw-bg-opacity, 1)); @@ -4015,6 +4020,10 @@ input[type="range"].range-lg::-moz-range-thumb { padding: 2rem; } +.p-\[3\.25rem\] { + padding: 3.25rem; +} + .\!px-0 { padding-left: 0px !important; padding-right: 0px !important; diff --git a/src/Logic/ImageProviders/ImageProvider.ts b/src/Logic/ImageProviders/ImageProvider.ts index 6cb152772d..16ad6b7d83 100644 --- a/src/Logic/ImageProviders/ImageProvider.ts +++ b/src/Logic/ImageProviders/ImageProvider.ts @@ -20,7 +20,7 @@ export interface ProvidedImage { lat?: number lon?: number host?: string - isSpherical?: boolean + isSpherical: boolean } export interface PanoramaView { diff --git a/src/Logic/ImageProviders/Imgur.ts b/src/Logic/ImageProviders/Imgur.ts index 3f0f255700..4ccc4ca99d 100644 --- a/src/Logic/ImageProviders/Imgur.ts +++ b/src/Logic/ImageProviders/Imgur.ts @@ -32,6 +32,7 @@ export class Imgur extends ImageProvider { key: key, provider: this, id: value, + isSpherical: false }, ] } diff --git a/src/Logic/ImageProviders/Mapillary.ts b/src/Logic/ImageProviders/Mapillary.ts index 21d270525b..3dd169a3e0 100644 --- a/src/Logic/ImageProviders/Mapillary.ts +++ b/src/Logic/ImageProviders/Mapillary.ts @@ -204,19 +204,24 @@ export class Mapillary extends ImageProvider { const metadataUrl = "https://graph.mapillary.com/" + mapillaryId + - "?fields=thumb_1024_url,thumb_original_url,captured_at,compass_angle,geometry,creator,camera_type&access_token=" + + "?fields=thumb_1024_url,thumb_original_url,captured_at,compass_angle,geometry,computed_geometry,creator,camera_type&access_token=" + Constants.mapillary_client_token_v4 const response = await Utils.downloadJsonCached<{ - thumb_1024_url: string, thumb_original_url: string, captured_at, + thumb_1024_url: string, + thumb_original_url: string, + captured_at, compass_angle: number, - creator: string + creator: string, + computed_geometry: Point, + geometry: Point, + camera_type: "equirectangular" | "spherical" | string }>(metadataUrl, 60 * 60) const url = response["thumb_1024_url"] const url_hd = response["thumb_original_url"] const date = new Date() - const rotation = (720 - Number(response["compass_angle"])) % 360 - const geometry = response["geometry"] - date.setTime(response["captured_at"]) + const rotation: number = (720 - Number(response.compass_angle)) % 360 + const geometry: Point = response.computed_geometry ?? response.geometry + date.setTime(response.captured_at) return { id: "" + mapillaryId, url, @@ -225,6 +230,7 @@ export class Mapillary extends ImageProvider { date, key, rotation, + isSpherical: response.camera_type === "spherical" || response.camera_type === "equirectangular", lat: geometry.coordinates[1], lon: geometry.coordinates[0] } diff --git a/src/Logic/ImageProviders/Panoramax.ts b/src/Logic/ImageProviders/Panoramax.ts index 26143f30dc..61802ab98b 100644 --- a/src/Logic/ImageProviders/Panoramax.ts +++ b/src/Logic/ImageProviders/Panoramax.ts @@ -97,6 +97,7 @@ export default class PanoramaxImageProvider extends ImageProvider { provider: this, status: meta.properties["geovisio:status"], rotation: Number(meta.properties["view:azimuth"]), + isSpherical: meta.properties.exif["Xmp.GPano.ProjectionType"] === "equirectangular", date: new Date(meta.properties.datetime), } } diff --git a/src/Logic/ImageProviders/WikidataImageProvider.ts b/src/Logic/ImageProviders/WikidataImageProvider.ts index 9ef77be375..20bd48ec5d 100644 --- a/src/Logic/ImageProviders/WikidataImageProvider.ts +++ b/src/Logic/ImageProviders/WikidataImageProvider.ts @@ -1,18 +1,21 @@ -import ImageProvider, { ProvidedImage } from "./ImageProvider" +import ImageProvider, { PanoramaView, ProvidedImage } from "./ImageProvider" import BaseUIElement from "../../UI/BaseUIElement" import { WikimediaImageProvider } from "./WikimediaImageProvider" import Wikidata from "../Web/Wikidata" import SvelteUIElement from "../../UI/Base/SvelteUIElement" import * as Wikidata_icon from "../../assets/svg/Wikidata.svelte" import { Utils } from "../../Utils" +import { Feature, Point } from "geojson" export class WikidataImageProvider extends ImageProvider { + + public static readonly singleton = new WikidataImageProvider() public readonly defaultKeyPrefixes = ["wikidata"] public readonly name = "Wikidata" private static readonly keyBlacklist: ReadonlySet = new Set([ "mapillary", - ...Utils.Times((i) => "mapillary:" + i, 10), + ...Utils.Times((i) => "mapillary:" + i, 10) ]) private constructor() { @@ -66,4 +69,8 @@ export class WikidataImageProvider extends ImageProvider { public DownloadAttribution(): Promise { throw new Error("Method not implemented; shouldn't be needed!") } + + public getPanoramaInfo(image: { id: string }): Promise> { + return undefined + } } diff --git a/src/Logic/ImageProviders/WikimediaImageProvider.ts b/src/Logic/ImageProviders/WikimediaImageProvider.ts index 3ccc96f150..73d137602d 100644 --- a/src/Logic/ImageProviders/WikimediaImageProvider.ts +++ b/src/Logic/ImageProviders/WikimediaImageProvider.ts @@ -1,10 +1,11 @@ -import ImageProvider, { ProvidedImage } from "./ImageProvider" +import ImageProvider, { PanoramaView, ProvidedImage } from "./ImageProvider" import BaseUIElement from "../../UI/BaseUIElement" import { Utils } from "../../Utils" import { LicenseInfo } from "./LicenseInfo" import Wikimedia from "../Web/Wikimedia" import SvelteUIElement from "../../UI/Base/SvelteUIElement" import Wikimedia_commons_white from "../../assets/svg/Wikimedia_commons_white.svelte" +import { Feature, Point } from "geojson" /** * This module provides endpoints for wikimedia and others @@ -123,7 +124,6 @@ export class WikimediaImageProvider extends ImageProvider { public async DownloadAttribution(img: { url: string }): Promise { const filename = WikimediaImageProvider.ExtractFileName(img.url) - if (filename === "") { return undefined } @@ -189,6 +189,11 @@ export class WikimediaImageProvider extends ImageProvider { key: undefined, provider: this, id: image, + isSpherical: false } } + + getPanoramaInfo(image: { id: string }): Promise> | undefined { + return undefined + } } diff --git a/src/Logic/Web/NearbyImagesSearch.ts b/src/Logic/Web/NearbyImagesSearch.ts index 5d424ebd54..4c92f776bb 100644 --- a/src/Logic/Web/NearbyImagesSearch.ts +++ b/src/Logic/Web/NearbyImagesSearch.ts @@ -234,7 +234,7 @@ class MapillaryFetcher implements ImageFetcher { async fetchImages(lat: number, lon: number): Promise { const boundingBox = new BBox([[lon, lat]]).padAbsolute(0.003) let url = - "https://graph.mapillary.com/images?fields=computed_geometry,creator,id,thumb_256_url,thumb_original_url,compass_angle&bbox=" + + "https://graph.mapillary.com/images?fields=geometry,computed_geometry,creator,id,thumb_256_url,thumb_original_url,compass_angle&bbox=" + [ boundingBox.getWest(), boundingBox.getSouth(), @@ -263,6 +263,7 @@ class MapillaryFetcher implements ImageFetcher { data: { id: string creator: string + geometry: Point computed_geometry: Point is_pano: boolean thumb_256_url: string @@ -272,7 +273,7 @@ class MapillaryFetcher implements ImageFetcher { }>(url) const pics: P4CPicture[] = [] for (const img of response.data) { - const c = img.computed_geometry.coordinates + const c = img.computed_geometry?.coordinates ?? img.geometry.coordinates if (img.thumb_original_url === undefined) { continue } diff --git a/src/UI/Image/AttributedImage.svelte b/src/UI/Image/AttributedImage.svelte index 8be73f6d85..2b16010644 100644 --- a/src/UI/Image/AttributedImage.svelte +++ b/src/UI/Image/AttributedImage.svelte @@ -21,6 +21,7 @@ import LoadingPlaceholder from "../Base/LoadingPlaceholder.svelte" import { MenuState } from "../../Models/MenuState" import ThemeViewState from "../../Models/ThemeViewState" + import Panorama360 from "../../assets/svg/Panorama360.svelte" export let image: Partial & { id: string; url: string } let fallbackImage: string = undefined @@ -48,7 +49,7 @@ previewedImage.addCallbackAndRun((previewedImage) => { showBigPreview.set( previewedImage !== undefined && - (previewedImage?.id ?? previewedImage?.url) === (image.id ?? image.url) + (previewedImage?.id ?? previewedImage?.url) === (image.id ?? image.url) ) }) ) @@ -66,12 +67,12 @@ type: "Feature", properties: { id: image.id, - rotation: image.rotation, + rotation: image.rotation }, geometry: { type: "Point", - coordinates: [image.lon, image.lat], - }, + coordinates: [image.lon, image.lat] + } } state?.geocodedImages.set([f]) } @@ -100,10 +101,10 @@ -{:else} +{:else if image.status !== "hidden"}
highlight()} on:mouseleave={() => highlight(false)} > @@ -132,6 +133,16 @@ src={image.url} /> + {#if image.isSpherical} +
+
+ +
+
+ {/if} + {#if canZoom && loaded}
+ +{:else if image.status === "hidden"} +
This image has been reported
{/if} diff --git a/src/UI/Image/DeletableImage.svelte b/src/UI/Image/DeletableImage.svelte index bd20108b46..ce719de3b9 100644 --- a/src/UI/Image/DeletableImage.svelte +++ b/src/UI/Image/DeletableImage.svelte @@ -5,9 +5,8 @@ import Popup from "../Base/Popup.svelte" import AccordionSingle from "../Flowbite/AccordionSingle.svelte" import NextButton from "../Base/NextButton.svelte" - import { UIEventSource } from "../../Logic/UIEventSource" + import { Store, UIEventSource } from "../../Logic/UIEventSource" import AttributedImage from "./AttributedImage.svelte" - import type { SpecialVisualizationState } from "../SpecialVisualization" import Dropdown from "../Base/Dropdown.svelte" import { REPORT_REASONS, ReportReason } from "panoramax-js" import { onDestroy } from "svelte" @@ -19,10 +18,14 @@ import ChangeTagAction from "../../Logic/Osm/Actions/ChangeTagAction" import { Tag } from "../../Logic/Tags/Tag" import { MenuState } from "../../Models/MenuState" + import type { Feature } from "geojson" + import ThemeViewState from "../../Models/ThemeViewState" export let image: ProvidedImage - export let state: SpecialVisualizationState + export let state: ThemeViewState export let tags: UIEventSource> + export let nearbyFeatures: Feature[] | Store = [] + let showDeleteDialog = new UIEventSource(false) onDestroy( showDeleteDialog.addCallbackAndRunD((shown) => { @@ -160,7 +163,7 @@
- +