diff --git a/assets/layers/geocoded_image/geocoded_image.json b/assets/layers/geocoded_image/geocoded_image.json index dd6f57efd..e6d24f9f8 100644 --- a/assets/layers/geocoded_image/geocoded_image.json +++ b/assets/layers/geocoded_image/geocoded_image.json @@ -2,6 +2,9 @@ "id": "geocoded_image", "name": null, "source": "special", + "description": { + "*": "Layer showing green dots where a geocoded image was found. See NearbyImages.svelte. Propreties: 'rotation':number,'spherical':'yes'|'no'" + }, "pointRendering": [ { "location": [ @@ -48,7 +51,15 @@ } } ], - "iconSize": "14,14" + "iconSize": { + "render": "14,14", + "mappings": [ + { + "if": "spherical=yes", + "then": "28,28" + } + ] + } }, { "location": [ diff --git a/assets/themes/mapcomplete-changes/mapcomplete-changes.json b/assets/themes/mapcomplete-changes/mapcomplete-changes.json index 335c9ba80..5b843b26b 100644 --- a/assets/themes/mapcomplete-changes/mapcomplete-changes.json +++ b/assets/themes/mapcomplete-changes/mapcomplete-changes.json @@ -858,21 +858,24 @@ { "question": { "en": "All platforms", - "cs": "Všechny platformy" + "cs": "Všechny platformy", + "de": "Alle Plattformen" }, "quesiton": "All platforms" }, { "question": { "en": "Made with Android", - "cs": "Vytvořeno s Androidem" + "cs": "Vytvořeno s Androidem", + "de": "Mit Android erstellt" }, "osmTags": "android=yes" }, { "question": { "en": "Made on the web", - "cs": "Vytvořeno na webu" + "cs": "Vytvořeno na webu", + "de": "Im Internet erstellt" }, "osmTags": "android=" } diff --git a/package-lock.json b/package-lock.json index 4d6a27479..5cf482c45 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,7 +15,6 @@ "@comunica/core": "^3.0.1", "@comunica/query-sparql": "^3.0.1", "@comunica/query-sparql-link-traversal": "^0.3.0", - "@photo-sphere-viewer/equirectangular-tiles-adapter": "^5.12.1", "@rapideditor/location-conflation": "^1.3.0", "@rgossiaux/svelte-headlessui": "^1.0.2", "@rgossiaux/svelte-heroicons": "^0.1.2", @@ -30,6 +29,7 @@ "@types/dompurify": "^3.0.2", "@types/follow-redirects": "^1.14.4", "@types/node": "^22.13.5", + "@types/pannellum": "^2.5.0", "@types/pg": "^8.11.11", "@types/qrcode-generator": "^1.0.6", "@types/showdown": "^2.0.0", @@ -71,11 +71,11 @@ "opening_hours": "^3.6.0", "osm-auth": "^2.6.0", "osmtogeojson": "^3.0.0-beta.5", + "pannellum": "^2.5.6", "panoramax-js": "^0.4.8", "panzoom": "^9.4.3", "papaparse": "^5.5.2", "pg": "^8.11.3", - "photo-sphere-viewer": "^4.8.1", "pic4carto": "^2.1.15", "pluscodes": "^2.6.0", "pmtiles": "^4.2.1", @@ -6275,32 +6275,6 @@ "url": "https://opencollective.com/parcel" } }, - "node_modules/@photo-sphere-viewer/core": { - "version": "5.12.1", - "resolved": "https://registry.npmjs.org/@photo-sphere-viewer/core/-/core-5.12.1.tgz", - "integrity": "sha512-aK+SueXdKOr5FQAMwjxswHaa2OZcpWi4tx5P4fjq1vWEDa8PtdaoSdQaAp3Csmthvd9DlfNDUb6c21fTudzM/w==", - "license": "MIT", - "peer": true, - "dependencies": { - "three": "^0.173.0" - } - }, - "node_modules/@photo-sphere-viewer/core/node_modules/three": { - "version": "0.173.0", - "resolved": "https://registry.npmjs.org/three/-/three-0.173.0.tgz", - "integrity": "sha512-AUwVmViIEUgBwxJJ7stnF0NkPpZxx1aZ6WiAbQ/Qq61h6I9UR4grXtZDmO8mnlaNORhHnIBlXJ1uBxILEKuVyw==", - "license": "MIT", - "peer": true - }, - "node_modules/@photo-sphere-viewer/equirectangular-tiles-adapter": { - "version": "5.12.1", - "resolved": "https://registry.npmjs.org/@photo-sphere-viewer/equirectangular-tiles-adapter/-/equirectangular-tiles-adapter-5.12.1.tgz", - "integrity": "sha512-Z9oiPNQwBdkGD1m+bXe0EuuBgdZFzec+d7MKexYgEqzLLukgp1WJ4il+3omMaRP5HAhRVWR5vapVALag+8BmPg==", - "license": "MIT", - "peerDependencies": { - "@photo-sphere-viewer/core": "5.12.1" - } - }, "node_modules/@pkgjs/parseargs": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", @@ -11480,6 +11454,12 @@ "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.4.tgz", "integrity": "sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==" }, + "node_modules/@types/pannellum": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@types/pannellum/-/pannellum-2.5.0.tgz", + "integrity": "sha512-iFVwMHmsTx91t74gU12bDmB1ty5lRgmfK6X+FxymQe8n0nuw3Pp/vk0nw73YdL9WqZgthrpf1KLPzQjZDUsj0g==", + "license": "MIT" + }, "node_modules/@types/papaparse": { "version": "5.3.15", "resolved": "https://registry.npmjs.org/@types/papaparse/-/papaparse-5.3.15.tgz", @@ -23024,6 +23004,12 @@ "version": "1.0.0", "license": "MIT" }, + "node_modules/pannellum": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/pannellum/-/pannellum-2.5.6.tgz", + "integrity": "sha512-R4kSPpj36wQPlyIi9ZftxPfVYF11DEbNBATUEI+pkMGZDFYBV5Jxi6tYFVDdmxA2xaTeKZQHMIuIIj7njVSTQQ==", + "license": "MIT" + }, "node_modules/panoramax-js": { "version": "0.4.8", "resolved": "https://registry.npmjs.org/panoramax-js/-/panoramax-js-0.4.8.tgz", @@ -23258,17 +23244,6 @@ "split2": "^4.1.0" } }, - "node_modules/photo-sphere-viewer": { - "version": "4.8.1", - "resolved": "https://registry.npmjs.org/photo-sphere-viewer/-/photo-sphere-viewer-4.8.1.tgz", - "integrity": "sha512-Yl1KZq1adtrajCOrf8Y79Qi4A35DfEu8atL779YOdA9XHoH2l2+sYovejnZlGgUM0hEbTyenRDoyXSy/MtioYg==", - "deprecated": "Use @photo-sphere-viewer/core instead, see https://photo-sphere-viewer.js.org/guide/migration.html", - "license": "MIT", - "dependencies": { - "three": "^0.147.0", - "uevent": "^2.1.1" - } - }, "node_modules/pic4carto": { "version": "2.1.15", "license": "SEE LICENSE IN LICENSE.txt", @@ -26747,12 +26722,6 @@ "node": ">=0.8" } }, - "node_modules/three": { - "version": "0.147.0", - "resolved": "https://registry.npmjs.org/three/-/three-0.147.0.tgz", - "integrity": "sha512-LPTOslYQXFkmvceQjFTNnVVli2LaVF6C99Pv34fJypp8NbQLbTlu3KinZ0zURghS5zEehK+VQyvWuPZ/Sm8fzw==", - "license": "MIT" - }, "node_modules/through": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", @@ -27639,12 +27608,6 @@ "node": ">=4.2.0" } }, - "node_modules/uevent": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/uevent/-/uevent-2.2.0.tgz", - "integrity": "sha512-48s5LF/c6R1fUmctGib/dWKhZjZLd4aK/85dwVAbwgHNBSO0k0UNp0ZKZpkSbU6633qYhgykYQPakTSuOxZopA==", - "license": "MIT" - }, "node_modules/uglify-js": { "version": "3.19.3", "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz", @@ -34198,29 +34161,6 @@ "version": "2.8.2", "dev": true }, - "@photo-sphere-viewer/core": { - "version": "5.12.1", - "resolved": "https://registry.npmjs.org/@photo-sphere-viewer/core/-/core-5.12.1.tgz", - "integrity": "sha512-aK+SueXdKOr5FQAMwjxswHaa2OZcpWi4tx5P4fjq1vWEDa8PtdaoSdQaAp3Csmthvd9DlfNDUb6c21fTudzM/w==", - "peer": true, - "requires": { - "three": "^0.173.0" - }, - "dependencies": { - "three": { - "version": "0.173.0", - "resolved": "https://registry.npmjs.org/three/-/three-0.173.0.tgz", - "integrity": "sha512-AUwVmViIEUgBwxJJ7stnF0NkPpZxx1aZ6WiAbQ/Qq61h6I9UR4grXtZDmO8mnlaNORhHnIBlXJ1uBxILEKuVyw==", - "peer": true - } - } - }, - "@photo-sphere-viewer/equirectangular-tiles-adapter": { - "version": "5.12.1", - "resolved": "https://registry.npmjs.org/@photo-sphere-viewer/equirectangular-tiles-adapter/-/equirectangular-tiles-adapter-5.12.1.tgz", - "integrity": "sha512-Z9oiPNQwBdkGD1m+bXe0EuuBgdZFzec+d7MKexYgEqzLLukgp1WJ4il+3omMaRP5HAhRVWR5vapVALag+8BmPg==", - "requires": {} - }, "@pkgjs/parseargs": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", @@ -38418,6 +38358,11 @@ "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.4.tgz", "integrity": "sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==" }, + "@types/pannellum": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@types/pannellum/-/pannellum-2.5.0.tgz", + "integrity": "sha512-iFVwMHmsTx91t74gU12bDmB1ty5lRgmfK6X+FxymQe8n0nuw3Pp/vk0nw73YdL9WqZgthrpf1KLPzQjZDUsj0g==" + }, "@types/papaparse": { "version": "5.3.15", "resolved": "https://registry.npmjs.org/@types/papaparse/-/papaparse-5.3.15.tgz", @@ -46271,6 +46216,11 @@ "packet-reader": { "version": "1.0.0" }, + "pannellum": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/pannellum/-/pannellum-2.5.6.tgz", + "integrity": "sha512-R4kSPpj36wQPlyIi9ZftxPfVYF11DEbNBATUEI+pkMGZDFYBV5Jxi6tYFVDdmxA2xaTeKZQHMIuIIj7njVSTQQ==" + }, "panoramax-js": { "version": "0.4.8", "resolved": "https://registry.npmjs.org/panoramax-js/-/panoramax-js-0.4.8.tgz", @@ -46429,15 +46379,6 @@ "split2": "^4.1.0" } }, - "photo-sphere-viewer": { - "version": "4.8.1", - "resolved": "https://registry.npmjs.org/photo-sphere-viewer/-/photo-sphere-viewer-4.8.1.tgz", - "integrity": "sha512-Yl1KZq1adtrajCOrf8Y79Qi4A35DfEu8atL779YOdA9XHoH2l2+sYovejnZlGgUM0hEbTyenRDoyXSy/MtioYg==", - "requires": { - "three": "^0.147.0", - "uevent": "^2.1.1" - } - }, "pic4carto": { "version": "2.1.15", "requires": { @@ -48832,11 +48773,6 @@ "thenify": ">= 3.1.0 < 4" } }, - "three": { - "version": "0.147.0", - "resolved": "https://registry.npmjs.org/three/-/three-0.147.0.tgz", - "integrity": "sha512-LPTOslYQXFkmvceQjFTNnVVli2LaVF6C99Pv34fJypp8NbQLbTlu3KinZ0zURghS5zEehK+VQyvWuPZ/Sm8fzw==" - }, "through": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", @@ -49524,11 +49460,6 @@ "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==" }, - "uevent": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/uevent/-/uevent-2.2.0.tgz", - "integrity": "sha512-48s5LF/c6R1fUmctGib/dWKhZjZLd4aK/85dwVAbwgHNBSO0k0UNp0ZKZpkSbU6633qYhgykYQPakTSuOxZopA==" - }, "uglify-js": { "version": "3.19.3", "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz", diff --git a/package.json b/package.json index b5e5cbc0c..034a5722c 100644 --- a/package.json +++ b/package.json @@ -156,7 +156,8 @@ "android:prepare": "./scripts/prepareAndroid.sh", "android:build": "./scripts/buildAndroid.sh", "android:upload": "scp ./android/app/build/outputs/apk/release/app-release.apk hetzner:apk/mapcomplete-latest.apk && scp ./android/app/build/outputs/apk/release/app-release.apk hetzner:app/mapcomplete-latest.apk", - "android:uninstall": "adb shell pm uninstall org.mapcomplete" + "android:uninstall": "adb shell pm uninstall org.mapcomplete", + "postinstall": "./scripts/fixPannellum.sh" }, "keywords": [ "OpenStreetMap", @@ -177,7 +178,6 @@ "@comunica/core": "^3.0.1", "@comunica/query-sparql": "^3.0.1", "@comunica/query-sparql-link-traversal": "^0.3.0", - "@photo-sphere-viewer/equirectangular-tiles-adapter": "^5.12.1", "@rapideditor/location-conflation": "^1.3.0", "@rgossiaux/svelte-headlessui": "^1.0.2", "@rgossiaux/svelte-heroicons": "^0.1.2", @@ -192,6 +192,7 @@ "@types/dompurify": "^3.0.2", "@types/follow-redirects": "^1.14.4", "@types/node": "^22.13.5", + "@types/pannellum": "^2.5.0", "@types/pg": "^8.11.11", "@types/qrcode-generator": "^1.0.6", "@types/showdown": "^2.0.0", @@ -233,11 +234,11 @@ "opening_hours": "^3.6.0", "osm-auth": "^2.6.0", "osmtogeojson": "^3.0.0-beta.5", + "pannellum": "^2.5.6", "panoramax-js": "^0.4.8", "panzoom": "^9.4.3", "papaparse": "^5.5.2", "pg": "^8.11.3", - "photo-sphere-viewer": "^4.8.1", "pic4carto": "^2.1.15", "pluscodes": "^2.6.0", "pmtiles": "^4.2.1", diff --git a/public/assets/loader_base.jpg b/public/assets/loader_base.jpg deleted file mode 100644 index 4be0e5003..000000000 Binary files a/public/assets/loader_base.jpg and /dev/null differ diff --git a/scripts/ScriptUtils.ts b/scripts/ScriptUtils.ts index 66ca1e815..c6d8bebeb 100644 --- a/scripts/ScriptUtils.ts +++ b/scripts/ScriptUtils.ts @@ -9,6 +9,7 @@ import xml2js from "xml2js" export default class ScriptUtils { public static fixUtils() { Utils.externalDownloadFunction = ScriptUtils.Download + } /** diff --git a/scripts/fixPannellum.sh b/scripts/fixPannellum.sh new file mode 100755 index 000000000..4cc4f4632 --- /dev/null +++ b/scripts/fixPannellum.sh @@ -0,0 +1,8 @@ +#! /bin/bash + +# Are you ready to feel really dirty? +# Pannellum is a direct import (not a module!) which uses window.pannellum = function... +# This breaks when importing this in nodeJS +# So, we patch it up... +echo "Fixing pannellum..." +sed -i 's/^window./if(typeof window !== "undefined")\n&/' "./node_modules/pannellum/build/pannellum.js" diff --git a/src/Logic/ImageProviders/GenericImageProvider.ts b/src/Logic/ImageProviders/GenericImageProvider.ts index 2d2093acf..34d1417f4 100644 --- a/src/Logic/ImageProviders/GenericImageProvider.ts +++ b/src/Logic/ImageProviders/GenericImageProvider.ts @@ -44,4 +44,8 @@ export default class GenericImageProvider extends ImageProvider { public DownloadAttribution(_) { return undefined } + + getPanoramaInfo(image: { id: string }): undefined { + return undefined + } } diff --git a/src/Logic/ImageProviders/ImageProvider.ts b/src/Logic/ImageProviders/ImageProvider.ts index d40907b57..3bb2a9bf0 100644 --- a/src/Logic/ImageProviders/ImageProvider.ts +++ b/src/Logic/ImageProviders/ImageProvider.ts @@ -1,7 +1,8 @@ -import { Store, Stores, UIEventSource } from "../UIEventSource" +import { Store, Stores } from "../UIEventSource" import BaseUIElement from "../../UI/BaseUIElement" import { LicenseInfo } from "./LicenseInfo" import { Utils } from "../../Utils" +import { Feature, Point } from "geojson" export interface ProvidedImage { url: string @@ -19,6 +20,17 @@ export interface ProvidedImage { lat?: number lon?: number host?: string + isSpherical?: boolean +} + +export interface PanoramaView { + url: string, + /** + * 0 - 359 + * Degrees in which the picture is taken, with north = 0; going clockwise + */ + northOffset?: number, + pitchOffset?: number } export default abstract class ImageProvider { @@ -89,6 +101,8 @@ export default abstract class ImageProvider { public abstract apiUrls(): string[] + public abstract getPanoramaInfo(image: { id: string }): Promise> | undefined; + public static async offerImageAsDownload(image: ProvidedImage) { const response = await fetch(image.url_hd ?? image.url) const blob = await response.blob() @@ -96,4 +110,5 @@ export default abstract class ImageProvider { mimetype: "image/jpg", }) } + } diff --git a/src/Logic/ImageProviders/Imgur.ts b/src/Logic/ImageProviders/Imgur.ts index dfa7f8b08..3f0f25570 100644 --- a/src/Logic/ImageProviders/Imgur.ts +++ b/src/Logic/ImageProviders/Imgur.ts @@ -116,4 +116,8 @@ export class Imgur extends ImageProvider { return license } + + getPanoramaInfo(image: { id: string }): undefined { + return undefined + } } diff --git a/src/Logic/ImageProviders/Mapillary.ts b/src/Logic/ImageProviders/Mapillary.ts index a555aade9..48627f06d 100644 --- a/src/Logic/ImageProviders/Mapillary.ts +++ b/src/Logic/ImageProviders/Mapillary.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 Constants from "../../Models/Constants" import SvelteUIElement from "../../UI/Base/SvelteUIElement" import MapillaryIcon from "./MapillaryIcon.svelte" +import { Feature, Point } from "geojson" export class Mapillary extends ImageProvider { public static readonly singleton = new Mapillary() @@ -16,7 +17,7 @@ export class Mapillary extends ImageProvider { "http://mapillary.com", "https://mapillary.com", "http://www.mapillary.com", - "https://www.mapillary.com", + "https://www.mapillary.com" ] defaultKeyPrefixes = ["mapillary", "image"] @@ -69,7 +70,7 @@ export class Mapillary extends ImageProvider { lat: location?.lat, lng: location?.lon, z: location === undefined ? undefined : Math.max((zoom ?? 2) - 1, 1), - pKey, + pKey } const baselink = `https://www.mapillary.com/app/?` const paramsStr = Utils.NoNull( @@ -137,6 +138,41 @@ export class Mapillary extends ImageProvider { return [img] } + + /** + * Download data necessary for the 360°-viewer + * @param pkey + * @constructor + */ + public async getPanoramaInfo(image: { id: number | string }): Promise> { + const pkey = image.id + const metadataUrl = + "https://graph.mapillary.com/" + + pkey + + "?fields=computed_compass_angle,geometry,is_pano,thumb_2048_url,thumb_original_url&access_token=" + + Constants.mapillary_client_token_v4 + const response = await Utils.downloadJsonCached< + { + computed_compass_angle: number, + geometry: Point, + + is_pano: boolean, + thumb_2048_url: string, + thumb_original_url: string, + id: string, + + }>(metadataUrl, 60 * 60) + return { + type: "Feature", + geometry: response.geometry, + properties: { + url: response.thumb_2048_url, + northOffset: response.computed_compass_angle + } + } + } + + public async DownloadAttribution(providedImage: { id: string }): Promise { const mapillaryId = providedImage.id const metadataUrl = @@ -182,7 +218,7 @@ export class Mapillary extends ImageProvider { key, rotation, lat: geometry.coordinates[1], - lon: geometry.coordinates[0], + lon: geometry.coordinates[0] } } } diff --git a/src/Logic/ImageProviders/Panoramax.ts b/src/Logic/ImageProviders/Panoramax.ts index 4f0204a1b..a6ba23c3c 100644 --- a/src/Logic/ImageProviders/Panoramax.ts +++ b/src/Logic/ImageProviders/Panoramax.ts @@ -1,7 +1,7 @@ import { ImageUploader } from "./ImageUploader" import { AuthorizedPanoramax, ImageData, Panoramax, PanoramaxXYZ } from "panoramax-js/dist" import ExifReader from "exifreader" -import ImageProvider, { ProvidedImage } from "./ImageProvider" +import ImageProvider, { PanoramaView, ProvidedImage } from "./ImageProvider" import BaseUIElement from "../../UI/BaseUIElement" import { LicenseInfo } from "./LicenseInfo" import { GeoOperations } from "../GeoOperations" @@ -10,6 +10,7 @@ import { Store, Stores, UIEventSource } from "../UIEventSource" import SvelteUIElement from "../../UI/Base/SvelteUIElement" import Panoramax_bw from "../../assets/svg/Panoramax_bw.svelte" import Link from "../../UI/Base/Link" +import { Feature, Point } from "geojson" export default class PanoramaxImageProvider extends ImageProvider { public static readonly singleton: PanoramaxImageProvider = new PanoramaxImageProvider() @@ -187,6 +188,20 @@ export default class PanoramaxImageProvider extends ImageProvider { } return new Panoramax(host) } + + public async getPanoramaInfo(image: { id: string }): Promise> | undefined { + const imageInfo = await PanoramaxImageProvider.xyz.imageInfo(image.id) + const url = (imageInfo.assets.sd ?? imageInfo.assets.thumb ?? imageInfo.assets.hd).href + const northOffset = imageInfo.properties["view:azimuth"] + const pitchOffset = Number(imageInfo.properties.exif["Xmp.GPano.PosePitchDegrees"]) + return >{ + type: "Feature", + geometry: imageInfo.geometry, + properties: { + url, northOffset, pitchOffset + } + } + } } export class PanoramaxUploader implements ImageUploader { diff --git a/src/Logic/State/UserSettingsMetaTagging.ts b/src/Logic/State/UserSettingsMetaTagging.ts index 6e568c5c3..33a5ae85b 100644 --- a/src/Logic/State/UserSettingsMetaTagging.ts +++ b/src/Logic/State/UserSettingsMetaTagging.ts @@ -1,42 +1,14 @@ import { Utils } from "../../Utils" /** This code is autogenerated - do not edit. Edit ./assets/layers/usersettings/usersettings.json instead */ export class ThemeMetaTagging { - public static readonly themeName = "usersettings" + public static readonly themeName = "usersettings" - public metaTaggging_for_usersettings(feat: { properties: Record }) { - Utils.AddLazyProperty(feat.properties, "_mastodon_candidate_md", () => - feat.properties._description - .match(/\[[^\]]*\]\((.*(mastodon|en.osm.town).*)\).*/) - ?.at(1) - ) - Utils.AddLazyProperty( - feat.properties, - "_d", - () => feat.properties._description?.replace(/</g, "<")?.replace(/>/g, ">") ?? "" - ) - Utils.AddLazyProperty(feat.properties, "_mastodon_candidate_a", () => - ((feat) => { - const e = document.createElement("div") - e.innerHTML = feat.properties._d - return Array.from(e.getElementsByTagName("a")).filter( - (a) => a.href.match(/mastodon|en.osm.town/) !== null - )[0]?.href - })(feat) - ) - Utils.AddLazyProperty(feat.properties, "_mastodon_link", () => - ((feat) => { - const e = document.createElement("div") - e.innerHTML = feat.properties._d - return Array.from(e.getElementsByTagName("a")).filter( - (a) => a.getAttribute("rel")?.indexOf("me") >= 0 - )[0]?.href - })(feat) - ) - Utils.AddLazyProperty( - feat.properties, - "_mastodon_candidate", - () => feat.properties._mastodon_candidate_md ?? feat.properties._mastodon_candidate_a - ) - feat.properties["__current_backgroun"] = "initial_value" - } -} + public metaTaggging_for_usersettings(feat: {properties: Record}) { + Utils.AddLazyProperty(feat.properties, '_mastodon_candidate_md', () => feat.properties._description.match(/\[[^\]]*\]\((.*(mastodon|en.osm.town).*)\).*/)?.at(1) ) + Utils.AddLazyProperty(feat.properties, '_d', () => feat.properties._description?.replace(/</g,'<')?.replace(/>/g,'>') ?? '' ) + Utils.AddLazyProperty(feat.properties, '_mastodon_candidate_a', () => (feat => {const e = document.createElement('div');e.innerHTML = feat.properties._d;return Array.from(e.getElementsByTagName("a")).filter(a => a.href.match(/mastodon|en.osm.town/) !== null)[0]?.href }) (feat) ) + Utils.AddLazyProperty(feat.properties, '_mastodon_link', () => (feat => {const e = document.createElement('div');e.innerHTML = feat.properties._d;return Array.from(e.getElementsByTagName("a")).filter(a => a.getAttribute("rel")?.indexOf('me') >= 0)[0]?.href})(feat) ) + Utils.AddLazyProperty(feat.properties, '_mastodon_candidate', () => feat.properties._mastodon_candidate_md ?? feat.properties._mastodon_candidate_a ) + feat.properties['__current_backgroun'] = 'initial_value' + } +} \ No newline at end of file diff --git a/src/Logic/Web/NearbyImagesSearch.ts b/src/Logic/Web/NearbyImagesSearch.ts index 69987f855..8d74af529 100644 --- a/src/Logic/Web/NearbyImagesSearch.ts +++ b/src/Logic/Web/NearbyImagesSearch.ts @@ -7,8 +7,6 @@ import { BBox } from "../BBox" import Constants from "../../Models/Constants" import { Utils } from "../../Utils" import { Point } from "geojson" -import MvtSource from "../FeatureSource/Sources/MvtSource" -import AllImageProviders from "../ImageProviders/AllImageProviders" import { Imgur } from "../ImageProviders/Imgur" import { Panoramax, PanoramaxXYZ } from "panoramax-js/dist" @@ -211,111 +209,6 @@ class ImagesFromPanoramaxFetcher implements ImageFetcher { } } -class ImagesFromCacheServerFetcher implements ImageFetcher { - private readonly _searchRadius: number - public readonly name = "fromCacheServer" - private readonly _serverUrl: string - - constructor(searchRadius: number = 500, serverUrl: string = Constants.VectorTileServer) { - this._searchRadius = searchRadius - this._serverUrl = serverUrl - } - - async fetchImages(lat: number, lon: number): Promise { - return ( - await Promise.all([ - this.fetchImagesForType(lat, lon, "lines"), - this.fetchImagesForType(lat, lon, "pois"), - this.fetchImagesForType(lat, lon, "polygons"), - ]) - ).flatMap((x) => x) - } - - async fetchImagesForType( - targetlat: number, - targetlon: number, - type: "lines" | "pois" | "polygons" - ): Promise { - const { x, y, z } = Tiles.embedded_tile(targetlat, targetlon, 14) - - const url = this._serverUrl - - async function getFeatures(x: number, y: number) { - const src = new MvtSource( - Utils.SubstituteKeys(url, { - type, - x, - y, - z, - layer: "item_with_image", - }), - x, - y, - z - ) - await src.updateAsync() - return src.features.data - } - - const features = ( - await Promise.all([ - getFeatures(x, y), - getFeatures(x, y + 1), - getFeatures(x, y - 1), - - getFeatures(x + 1, y + 1), - getFeatures(x + 1, y), - getFeatures(x + 1, y - 1), - - getFeatures(x - 1, y - 1), - getFeatures(x - 1, y), - getFeatures(x - 1, y + 1), - ]) - ).flatMap((x) => x) - - const pics: P4CPicture[] = [] - for (const f of features) { - const [lng, lat] = GeoOperations.centerpointCoordinates(f) - if ( - GeoOperations.distanceBetween([targetlon, targetlat], [lng, lat]) > - this._searchRadius - ) { - return [] - } - for (let i = -1; i < 50; i++) { - let key = "image" - if (i >= 0) { - key += ":" + i - } - const v = f.properties[key] - console.log(v) - if (!v) { - continue - } - let provider = "unkown" - try { - provider = (await AllImageProviders.selectBestProvider("image", v))?.name - } catch (e) { - console.error("Could not detect provider for", "image", v) - } - pics.push({ - pictureUrl: v, - coordinates: { lat, lng }, - details: { - isSpherical: false, - }, - osmTags: { - image: v, - }, - thumbUrl: v, - provider, - }) - } - } - return pics - } -} - class MapillaryFetcher implements ImageFetcher { public readonly name = "mapillary_new" private readonly _panoramas: "only" | "no" | undefined @@ -390,7 +283,7 @@ class MapillaryFetcher implements ImageFetcher { mapillary: img.id, }, details: { - isSpherical: img.is_pano, + isSpherical: this._panoramas === "only" }, }) } @@ -407,15 +300,20 @@ export class CombinedFetcher { constructor(radius: number, maxage: Date, indexedFeatures: IndexedFeatureSource) { this.sources = [ new ImagesInLoadedDataFetcher(indexedFeatures, radius), - new ImagesFromCacheServerFetcher(radius), new ImagesFromPanoramaxFetcher(), new ImagesFromPanoramaxFetcher(Constants.panoramax.url), + // For mapillary, we need to query both with and without panoramas. See https://www.mapillary.com/developer/api-documentation/ new MapillaryFetcher({ max_images: 25, start_captured_at: maxage, + panoramas: "only" }), - new P4CImageFetcher("mapillary"), - new P4CImageFetcher("wikicommons"), + new MapillaryFetcher({ + max_images: 25, + start_captured_at: maxage, + panoramas: "no" + }), new P4CImageFetcher("mapillary"), + new P4CImageFetcher("wikicommons") ].map((f) => new CachedFetcher(f)) } diff --git a/src/UI/Image/AttributedImage.svelte b/src/UI/Image/AttributedImage.svelte index a60d9d64f..8be73f6d8 100644 --- a/src/UI/Image/AttributedImage.svelte +++ b/src/UI/Image/AttributedImage.svelte @@ -3,6 +3,8 @@ * Shows an image with attribution */ import ImageAttribution from "./ImageAttribution.svelte" + import { Store } from "../../Logic/UIEventSource" + import type { ProvidedImage } from "../../Logic/ImageProviders/ImageProvider" import { Mapillary } from "../../Logic/ImageProviders/Mapillary" import { UIEventSource } from "../../Logic/UIEventSource" @@ -32,6 +34,8 @@ export let attributionFormat: "minimal" | "medium" | "large" = "medium" let previewedImage: UIEventSource> = MenuState.previewedImage export let canZoom = previewedImage !== undefined + export let nearbyFeatures: Feature[] | Store = [] + let loaded = false let showBigPreview = new UIEventSource(false) onDestroy( @@ -74,9 +78,8 @@ -
- + diff --git a/src/UI/Image/ImageOperations.svelte b/src/UI/Image/ImageOperations.svelte index 884bca202..d0142d0d0 100644 --- a/src/UI/Image/ImageOperations.svelte +++ b/src/UI/Image/ImageOperations.svelte @@ -14,9 +14,12 @@ import Tr from "../Base/Tr.svelte" import Translations from "../i18n/Translations" import DotMenu from "../Base/DotMenu.svelte" + import type { Feature } from "geojson" + import { Store } from "../../Logic/UIEventSource" export let image: Partial & { id: string; url: string } export let clss: string = undefined + export let nearbyFeatures: Feature[] | Store = [] let isLoaded = new UIEventSource(false) @@ -28,7 +31,7 @@
{/if} - +
diff --git a/src/UI/Image/ImagePreview.svelte b/src/UI/Image/ImagePreview.svelte index 0dabcebce..f19a76fdb 100644 --- a/src/UI/Image/ImagePreview.svelte +++ b/src/UI/Image/ImagePreview.svelte @@ -6,23 +6,50 @@ import type { ProvidedImage } from "../../Logic/ImageProviders/ImageProvider" import { UIEventSource } from "../../Logic/UIEventSource" import Zoomcontrol from "../Zoomcontrol" - import { onDestroy } from "svelte" + import { getContext, onDestroy } from "svelte" + import type { PanoramaView } from "./photoSphereViewerWrapper" + import { PhotoSphereViewerWrapper } from "./photoSphereViewerWrapper" + import type { Feature, Point } from "geojson" + import { Store } from "../../Logic/UIEventSource" + + + export let nearbyFeatures: Feature[] | Store = [] export let image: Partial let panzoomInstance = undefined let panzoomEl: HTMLElement + let viewerEl: HTMLElement + export let isLoaded: UIEventSource = undefined onDestroy(Zoomcontrol.createLock()) + async function initPhotosphere() { + let f: Feature = await image.provider.getPanoramaInfo(image) + + const viewer = new PhotoSphereViewerWrapper(viewerEl, f) + if (Array.isArray(nearbyFeatures)) { + viewer.setNearbyFeatures(nearbyFeatures) + } else { + nearbyFeatures.addCallbackAndRunD(feats => { + viewer.setNearbyFeatures(feats) + }) + } + isLoaded.set(true) + + } + $: { - if (panzoomEl) { + if (image.isSpherical) { + + initPhotosphere() + } else if (panzoomEl) { panzoomInstance = panzoom(panzoomEl, { bounds: true, boundsPadding: 0.49, minZoom: 0.1, maxZoom: 25, - initialZoom: 1.0, + initialZoom: 1.0 }) } else { panzoomInstance?.dispose() @@ -30,11 +57,18 @@ } - { + + + +{#if image.isSpherical} +
+{:else} + { isLoaded?.setData(true) }} - src={image.url_hd ?? image.url} -/> + src={image.url_hd ?? image.url} + /> +{/if} diff --git a/src/UI/Image/LinkableImage.svelte b/src/UI/Image/LinkableImage.svelte index a5748885b..19a7aa779 100644 --- a/src/UI/Image/LinkableImage.svelte +++ b/src/UI/Image/LinkableImage.svelte @@ -1,5 +1,6 @@ - -
+ + + +
diff --git a/src/UI/ThemeViewGUI.svelte b/src/UI/ThemeViewGUI.svelte index 03ead9eaa..663f155f3 100644 --- a/src/UI/ThemeViewGUI.svelte +++ b/src/UI/ThemeViewGUI.svelte @@ -18,7 +18,7 @@ import LevelSelector from "./BigComponents/LevelSelector.svelte" import type { RasterLayerPolygon } from "../Models/RasterLayers" import { AvailableRasterLayers } from "../Models/RasterLayers" - import { onDestroy } from "svelte" + import { onDestroy, setContext } from "svelte" import OpenBackgroundSelectorButton from "./BigComponents/OpenBackgroundSelectorButton.svelte" import StateIndicator from "./BigComponents/StateIndicator.svelte" import UploadingImageCounter from "./Image/UploadingImageCounter.svelte" diff --git a/src/test.ts b/src/test.ts index 1dc31faa3..1e5950938 100644 --- a/src/test.ts +++ b/src/test.ts @@ -1,13 +1,17 @@ +import { Mapillary } from "./Logic/ImageProviders/Mapillary" import Test from "./UI/Test.svelte" -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 imgId = "8af265ba-3521-4c46-b2a9-c072215c1de3" let panoramax = new PanoramaxXYZ() panoramax.imageInfo(imgId).then((imageInfo: ImageData) => { console.log("IMG INFO: ", imageInfo) new Test({ target, props: { imageInfo } }) -}) +})*/ +let pkey = 1199645818028177 +new Mapillary().DownloadImageInfo(pkey).then(imageInfo => { + new Test({ target, props: { imageInfo } }) +})