forked from MapComplete/MapComplete
UI: don't allow cylindrical images for now, see #2424
This commit is contained in:
parent
10e0262a0d
commit
b9293dc2c9
5 changed files with 80 additions and 75 deletions
|
|
@ -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<true | { error: Translation }> {
|
||||
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 = <OsmId | NoteId>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,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue