diff --git a/src/Logic/ImageProviders/ImageUploadManager.ts b/src/Logic/ImageProviders/ImageUploadManager.ts index 2b5de74d61..437015b82d 100644 --- a/src/Logic/ImageProviders/ImageUploadManager.ts +++ b/src/Logic/ImageProviders/ImageUploadManager.ts @@ -13,6 +13,7 @@ import ImageUploadQueue, { ImageUploadArguments } from "./ImageUploadQueue" import { GeoOperations } from "../GeoOperations" import NoteCommentElement from "../../UI/Popup/Notes/NoteCommentElement" import OsmObjectDownloader from "../Osm/OsmObjectDownloader" +import ExifReader from "exifreader" /** * The ImageUploadManager has a @@ -81,7 +82,7 @@ export class ImageUploadManager { this._reportError = reportError } - public canBeUploaded(file: File): true | { error: Translation } { + public async canBeUploaded(file: File): Promise { const sizeInBytes = file.size if (sizeInBytes > this._uploader.maxFileSizeInMegabytes * 1000000) { const error = Translations.t.image.toBig.Subs({ @@ -94,12 +95,19 @@ export class ImageUploadManager { if (ext !== "jpg" && ext !== "jpeg") { return { error: new Translation({ en: "Only JPG-files are allowed" }) } } + + const tags = await ExifReader.load(file) + if (tags.ProjectionType.value === "cylindrical") { + return { error: new Translation({ en: "Cylindrical images (typically created by a Panorama-app) are not supported" }) } + } + return true } /** * Uploads the given image, applies the correct title and license for the known user. * Will then add this image to the OSM-feature or the OSM-note automatically, based on the ID of the feature. + * Does _not_ check 'canBeUploaded' * Note: the image will actually be added to the queue. If the image-upload fails, this will be attempted when visiting MC again * @param file a jpg file to upload * @param tagsStore The tags of the feature @@ -117,10 +125,6 @@ export class ImageUploadManager { ignoreGPS: boolean | false } ): void { - const canBeUploaded = this.canBeUploaded(file) - if (canBeUploaded !== true) { - throw canBeUploaded.error - } const tags: OsmTags = tagsStore.data const featureId = tags.id @@ -286,7 +290,7 @@ export class ImageUploadManager { let absoluteUrl: string try { - ;({ key, value, absoluteUrl } = await this._uploader.uploadImage( + ({ key, value, absoluteUrl } = await this._uploader.uploadImage( blob, location, author, diff --git a/src/Logic/ImageProviders/Panoramax.ts b/src/Logic/ImageProviders/Panoramax.ts index dcba6a1359..15fc7c1819 100644 --- a/src/Logic/ImageProviders/Panoramax.ts +++ b/src/Logic/ImageProviders/Panoramax.ts @@ -51,7 +51,7 @@ export default class PanoramaxImageProvider extends ImageProvider { new SvelteUIElement(Panoramax_bw), p.createViewLink({ imageId: img?.id, - location, + location }), true ) @@ -65,14 +65,14 @@ export default class PanoramaxImageProvider extends ImageProvider { const p = new Panoramax(host) return p.createViewLink({ imageId: img?.id, - location, + location }) } public addKnownMeta(meta: ImageData, url?: string) { PanoramaxImageProvider.knownMeta[meta.id] = { data: Promise.resolve({ data: meta, url }), - time: new Date(), + time: new Date() } } @@ -125,7 +125,7 @@ export default class PanoramaxImageProvider extends ImageProvider { 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), + date: new Date(meta.properties.datetime) } } @@ -156,7 +156,7 @@ export default class PanoramaxImageProvider extends ImageProvider { const promise: Promise<{ data: ImageData; url: string }> = this.getInfoForUncached(id) PanoramaxImageProvider.knownMeta[id] = { time: new Date(), - data: promise, + data: promise } return await promise } @@ -215,7 +215,7 @@ export default class PanoramaxImageProvider extends ImageProvider { return { artist: meta.data.providers.at(-1).name, // We take the last provider, as that one probably contain the username of the uploader date: new Date(meta.data.properties["datetime"]), - licenseShortName: meta.data.properties["geovisio:license"], + licenseShortName: meta.data.properties["geovisio:license"] } } @@ -247,8 +247,8 @@ export default class PanoramaxImageProvider extends ImageProvider { properties: { url, northOffset, - pitchOffset, - }, + pitchOffset + } } } } @@ -263,6 +263,7 @@ export class PanoramaxUploader implements ImageUploader { this.panoramax = new AuthorizedPanoramax(url, token) } + async uploadImage( blob: File, currentGps: [number, number], @@ -282,53 +283,63 @@ export class PanoramaxUploader implements ImageUploader { datetime ??= new Date().toISOString() try { const tags = await ExifReader.load(blob) - const [[latD], [latM], [latS, latSDenom]] = < - [[number, number], [number, number], [number, number]] - >tags?.GPSLatitude?.value - const [[lonD], [lonM], [lonS, lonSDenom]] = < - [[number, number], [number, number], [number, number]] - >tags?.GPSLongitude?.value + if (tags.ProjectionType.value === "cylindrical") { + throw "Unsupported image format: cylindrical images (panorama images) are currently not supported" + } + if (tags?.GPSLatitude?.value && tags?.GPSLongitude?.value) { - const exifLat = latD + latM / 60 + latS / (3600 * latSDenom) - const exifLon = lonD + lonM / 60 + lonS / (3600 * lonSDenom) - if ( - typeof exifLat === "number" && - !isNaN(exifLat) && - typeof exifLon === "number" && - !isNaN(exifLon) && - !(exifLat === 0 && exifLon === 0) - ) { - lat = exifLat - lon = exifLon - if (tags?.GPSLatitudeRef?.value?.[0] === "S") { - lat *= -1 - } - if (tags?.GPSLongitudeRef?.value?.[0] === "W") { - lon *= -1 + const [[latD], [latM], [latS, latSDenom]] = < + [[number, number], [number, number], [number, number]] + >tags?.GPSLatitude?.value + const [[lonD], [lonM], [lonS, lonSDenom]] = < + [[number, number], [number, number], [number, number]] + >tags?.GPSLongitude?.value + + const exifLat = latD + latM / 60 + latS / (3600 * latSDenom) + const exifLon = lonD + lonM / 60 + lonS / (3600 * lonSDenom) + if ( + typeof exifLat === "number" && + !isNaN(exifLat) && + typeof exifLon === "number" && + !isNaN(exifLon) && + !(exifLat === 0 && exifLon === 0) + ) { + lat = exifLat + lon = exifLon + if (tags?.GPSLatitudeRef?.value?.[0] === "S") { + lat *= -1 + } + if (tags?.GPSLongitudeRef?.value?.[0] === "W") { + lon *= -1 + } } } - const [date, time] = ( + const dateTime = ( tags.DateTime.value[0] ?? tags.DateTimeOriginal.value[0] ?? tags.GPSDateStamp ?? tags.CreateDate ?? tags["Date Created"] - ).split(" ") - const exifDatetime = new Date(date.replaceAll(":", "-") + "T" + time) - if (exifDatetime.getFullYear() === 1970) { - // The data probably got reset to the epoch - // we don't use the value - console.log( - "Datetime from picture is probably invalid:", - exifDatetime, - "using 'now' instead" - ) - } else { - datetime = exifDatetime.toISOString() + )?.split(" ") + if (dateTime) { + const [date, time] = dateTime + const exifDatetime = new Date(date.replaceAll(":", "-") + "T" + time) + if (exifDatetime.getFullYear() === 1970) { + // The data probably got reset to the epoch + // we don't use the value + console.log( + "Datetime from picture is probably invalid:", + exifDatetime, + "using 'now' instead" + ) + } else { + datetime = exifDatetime.toISOString() + } + } - console.log("Tags are", tags) + } catch (e) { - console.warn("Could not read EXIF-tags") + console.warn("Could not read EXIF-tags due to", e) } const p = this.panoramax @@ -345,7 +356,7 @@ export class PanoramaxUploader implements ImageUploader { indexInSequence: sequence["stats:items"].count + 1, // stats:items is '1'-indexed, so .count is also the last index exifOverride: { Artist: author, - }, + } } if (progress) { options.onProgress = (e: ProgressEvent) => { @@ -362,7 +373,7 @@ export class PanoramaxUploader implements ImageUploader { return { key: "panoramax", value: img.id, - absoluteUrl: img.assets.hd.href, + absoluteUrl: img.assets.hd.href } } } diff --git a/src/UI/Base/FileSelector.svelte b/src/UI/Base/FileSelector.svelte index 28351fafb9..99f5e6a2cd 100644 --- a/src/UI/Base/FileSelector.svelte +++ b/src/UI/Base/FileSelector.svelte @@ -1,6 +1,5 @@ -

Settings test

-Logged in as {$ud} + accept(fileList.detail)} accept="image/jpg">Select file -Current value of pref is {$pref} - - - +{$txt}