chore: automated housekeeping...

This commit is contained in:
Pieter Vander Vennet 2025-04-15 18:18:44 +02:00
parent 79b6927b56
commit 42ded4c1b1
328 changed files with 4062 additions and 1284 deletions

View file

@ -35,7 +35,9 @@
export let attributionFormat: "minimal" | "medium" | "large" = "medium"
let previewedImage: UIEventSource<Partial<ProvidedImage>> = MenuState.previewedImage
export let canZoom = previewedImage !== undefined
export let nearbyFeatures: Feature<Geometry, HotspotProperties>[] | Store<Feature<Geometry, HotspotProperties>[]> = []
export let nearbyFeatures:
| Feature<Geometry, HotspotProperties>[]
| Store<Feature<Geometry, HotspotProperties>[]> = []
let loaded = false
let showBigPreview = new UIEventSource(false)
@ -49,7 +51,7 @@
previewedImage.addCallbackAndRun((previewedImage) => {
showBigPreview.set(
previewedImage !== undefined &&
(previewedImage?.id ?? previewedImage?.url) === (image.id ?? image.url)
(previewedImage?.id ?? previewedImage?.url) === (image.id ?? image.url)
)
})
)
@ -67,16 +69,15 @@
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])
}
</script>
<Popup shown={showBigPreview} bodyPadding="p-0" dismissable={true}>
@ -135,10 +136,12 @@
/>
{#if image.isSpherical}
<div class="absolute top-0 left-0 w-full h-full flex justify-center items-center pointer-events-none">
<div class="bg-black opacity-50 rounded-full p-[3.25rem]">
<div class="w-0 h-0 relative flex items-center justify-center">
<Panorama360 class="absolute w-16 h-16" color="#ffffff" />
<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" />
</div>
</div>
</div>
@ -157,7 +160,6 @@
<ImageAttribution {image} {attributionFormat} />
</div>
</div>
{:else if image.status === "hidden"}
<div class="subtle">This image has been reported</div>
{/if}

View file

@ -18,12 +18,14 @@
type: "Feature",
geometry: {
type: "Point",
coordinates: GeoOperations.centerpointCoordinates(feature)
coordinates: GeoOperations.centerpointCoordinates(feature),
},
properties: {
name: layer?.title?.GetRenderValue(feature.properties)?.Subs(feature.properties)?.txt ?? feature?.properties?.name,
focus: true
}
name:
layer?.title?.GetRenderValue(feature.properties)?.Subs(feature.properties)?.txt ??
feature?.properties?.name,
focus: true,
},
}
</script>

View file

@ -20,7 +20,9 @@
export let image: Partial<ProvidedImage> & { id: string; url: string }
export let clss: string = undefined
export let nearbyFeatures: Feature<Geometry, HotspotProperties>[] | Store<Feature<Geometry, HotspotProperties>[]> = []
export let nearbyFeatures:
| Feature<Geometry, HotspotProperties>[]
| Store<Feature<Geometry, HotspotProperties>[]> = []
let isLoaded = new UIEventSource(false)
console.log(">>> slots are", $$slots)
@ -37,17 +39,17 @@
</div>
{#if $$slots["dot-menu-actions"]}
<DotMenu dotsPosition="top-0 left-0" dotsSize="w-8 h-8" hideBackground>
<slot name="dot-menu-actions">
<button
class="no-image-background pointer-events-auto flex items-center"
on:click={() => ImageProvider.offerImageAsDownload(image)}
>
<DownloadIcon class="h-6 w-6 px-2 opacity-100" />
<Tr t={Translations.t.general.download.downloadImage} />
</button>
</slot>
</DotMenu>
<DotMenu dotsPosition="top-0 left-0" dotsSize="w-8 h-8" hideBackground>
<slot name="dot-menu-actions">
<button
class="no-image-background pointer-events-auto flex items-center"
on:click={() => ImageProvider.offerImageAsDownload(image)}
>
<DownloadIcon class="h-6 w-6 px-2 opacity-100" />
<Tr t={Translations.t.general.download.downloadImage} />
</button>
</slot>
</DotMenu>
{/if}
<div
class="pointer-events-none absolute bottom-0 left-0 flex w-full flex-wrap items-end justify-between"

View file

@ -13,8 +13,9 @@
import type { Feature, Geometry, Point } from "geojson"
import { Store } from "../../Logic/UIEventSource"
export let nearbyFeatures: Feature<Geometry, HotspotProperties>[] | Store<Feature<Geometry, HotspotProperties>[]> = []
export let nearbyFeatures:
| Feature<Geometry, HotspotProperties>[]
| Store<Feature<Geometry, HotspotProperties>[]> = []
export let image: Partial<ProvidedImage>
let panzoomInstance = undefined
let panzoomEl: HTMLElement
@ -34,17 +35,15 @@
if (Array.isArray(nearbyFeatures)) {
viewer.setNearbyFeatures(nearbyFeatures)
} else {
nearbyFeatures.addCallbackAndRunD(feats => {
nearbyFeatures.addCallbackAndRunD((feats) => {
viewer.setNearbyFeatures(feats)
})
}
isLoaded.set(true)
}
$: {
if (image.isSpherical) {
initPhotosphere()
} else if (panzoomEl) {
panzoomInstance = panzoom(panzoomEl, {
@ -52,7 +51,7 @@
boundsPadding: 0.49,
minZoom: 0.1,
maxZoom: 25,
initialZoom: 1.0
initialZoom: 1.0,
})
} else {
panzoomInstance?.dispose()
@ -61,17 +60,17 @@
</script>
<head>
<link rel="stylesheet" href="./css/pannellum.css">
<link rel="stylesheet" href="./css/pannellum.css" />
</head>
{#if image.isSpherical}
<div bind:this={viewerEl} class="w-full h-full" />
<div bind:this={viewerEl} class="h-full w-full" />
{:else}
<img
bind:this={panzoomEl}
class="panzoom-image h-fit max-w-fit"
on:load={() => {
isLoaded?.setData(true)
}}
isLoaded?.setData(true)
}}
src={image.url_hd ?? image.url}
/>
{/if}

View file

@ -30,7 +30,7 @@
export let linkable = true
let targetValue = Object.values(image.osmTags)[0]
let isLinked = new UIEventSource(Object.values(tags.data).some((v) => targetValue === v))
isLinked.addCallbackAndRun(linked => {
isLinked.addCallbackAndRun((linked) => {
if (linked) {
MenuState.previewedImage.set(undefined)
}
@ -43,7 +43,7 @@
provider: AllImageProviders.byName(image.provider),
date: new Date(image.date),
id: Object.values(image.osmTags)[0],
isSpherical: image.details.isSpherical
isSpherical: image.details.isSpherical,
}
async function applyLink(isLinked: boolean) {
@ -54,7 +54,7 @@
if (isLinked) {
const action = new LinkImageAction(currentTags.id, key, url, tags, {
theme: tags.data._orig_theme ?? state.theme.id,
changeType: "link-image"
changeType: "link-image",
})
await state.changes.applyAction(action)
} else {
@ -63,7 +63,7 @@
if (v === url) {
const action = new ChangeTagAction(currentTags.id, new Tag(k, ""), currentTags, {
theme: tags.data._orig_theme ?? state.theme.id,
changeType: "remove-image"
changeType: "remove-image",
})
state.changes.applyAction(action)
}
@ -99,9 +99,7 @@
attributionFormat="minimal"
>
<svelte:fragment slot="dot-menu-actions">
<LoginToggle {state} silentFail={true} hiddenFail={true}>
{#if linkable}
<label>
<input bind:checked={$isLinked} type="checkbox" />
@ -110,7 +108,6 @@
{/if}
</LoginToggle>
</svelte:fragment>
</AttributedImage>
<LoginToggle {state} silentFail={true}>
{#if linkable}

View file

@ -46,8 +46,7 @@
(pics: P4CPicture[]) =>
pics
.filter(
(p: P4CPicture) =>
!loadedImages.data.has(p.pictureUrl) // We don't show any image which is already linked
(p: P4CPicture) => !loadedImages.data.has(p.pictureUrl) // We don't show any image which is already linked
)
.slice(0, 25),
[loadedImages]
@ -60,15 +59,15 @@
type: "Feature",
geometry: {
type: "Point",
coordinates: [p4c.coordinates.lng, p4c.coordinates.lat]
coordinates: [p4c.coordinates.lng, p4c.coordinates.lat],
},
properties: <PanoramaView>{
id: p4c.pictureUrl,
url: p4c.pictureUrl,
northOffset: p4c.direction,
rotation: p4c.direction,
spherical: p4c.details.isSpherical ? "yes" : "no"
}
spherical: p4c.details.isSpherical ? "yes" : "no",
},
}
)
)
@ -80,14 +79,14 @@
type: "Feature",
geometry: {
type: "Point",
coordinates: [s.coordinates.lng, s.coordinates.lat]
coordinates: [s.coordinates.lng, s.coordinates.lat],
},
properties: {
id: s.pictureUrl,
selected: "yes",
rotation: s.direction
}
}
rotation: s.direction,
},
},
]
})
@ -112,7 +111,7 @@
rotation: state.mapProperties.rotation,
pitch: state.mapProperties.pitch,
zoom: new UIEventSource<number>(16),
location: new UIEventSource({ lon, lat })
location: new UIEventSource({ lon, lat }),
})
const geocodedImageLayer = new LayerConfig(<LayerConfigJson>geocoded_image)
@ -123,7 +122,7 @@
onClick: (feature) => {
console.log("CLicked:", feature.properties)
highlighted.set(feature.properties.id)
}
},
})
ShowDataLayer.showMultipleLayers(map, new StaticFeatureSource([feature]), state.theme.layers)
@ -146,24 +145,29 @@
layer: geocodedImageLayer,
onClick: (feature) => {
highlighted.set(feature.properties.id)
}
},
})
let nearbyFeatures: Store<Feature[]> = asFeatures.map(nearbyPoints => {
return [{
type: "Feature",
geometry: { type: "Point", coordinates: GeoOperations.centerpointCoordinates(feature) },
properties: {
name: layer.title?.GetRenderValue(feature.properties).Subs(feature.properties).txt,
focus: true
}
}, ...nearbyPoints.filter(p => p.properties.spherical === "yes").map(f => ({
...f, properties: {
name: "Nearby panorama",
pitch: "auto",
type: "scene",
gotoPanorama: f
}
}))
let nearbyFeatures: Store<Feature[]> = asFeatures.map((nearbyPoints) => {
return [
{
type: "Feature",
geometry: { type: "Point", coordinates: GeoOperations.centerpointCoordinates(feature) },
properties: {
name: layer.title?.GetRenderValue(feature.properties).Subs(feature.properties).txt,
focus: true,
},
},
...nearbyPoints
.filter((p) => p.properties.spherical === "yes")
.map((f) => ({
...f,
properties: {
name: "Nearby panorama",
pitch: "auto",
type: "scene",
gotoPanorama: f,
},
})),
]
})
@ -204,7 +208,16 @@
selected.set(undefined)
}}
>
<LinkableImage {tags} {image} {state} {feature} {layer} {linkable} {highlighted} {nearbyFeatures} />
<LinkableImage
{tags}
{image}
{state}
{feature}
{layer}
{linkable}
{highlighted}
{nearbyFeatures}
/>
</span>
{/each}
</div>

View file

@ -13,40 +13,45 @@
export let imageArguments: ImageUploadArguments
let confirmDelete = new UIEventSource(false)
function del() {
queue.delete(imageArguments)
}
const t = Translations.t
let src = undefined
try{
try {
src = URL.createObjectURL(imageArguments.blob)
}catch (e) {
} catch (e) {
console.error("Could not create an ObjectURL for blob", imageArguments.blob)
}
</script>
<div class="low-interaction rounded border-interactive w-fit p-2 m-1 flex flex-col">
<img class="max-w-64 w-auto max-h-64 w-auto" {src} />
{imageArguments.featureId} {imageArguments.layoutId}
<button class="as-link self-end" on:click={() => {confirmDelete.set(true)}}>
<div class="low-interaction border-interactive m-1 flex w-fit flex-col rounded p-2">
<img class="max-h-64 w-auto w-auto max-w-64" {src} />
{imageArguments.featureId}
{imageArguments.layoutId}
<button
class="as-link self-end"
on:click={() => {
confirmDelete.set(true)
}}
>
<TrashIcon class="w-4" />
<Tr t={t.imageQueue.delete} />
</button>
<Popup shown={confirmDelete} dismissable={true}>
<Page shown={confirmDelete}>
<svelte:fragment slot="header">
<TrashIcon class="w-8 m-1" />
<TrashIcon class="m-1 w-8" />
<Tr t={t.imageQueue.confirmDeleteTitle} />
</svelte:fragment>
<div class="flex flex-col ">
<div class="flex flex-col">
<div class="flex justify-center">
<img class="max-w-128 w-auto max-h-128 w-auto" src={URL.createObjectURL(imageArguments.blob)} />
<img
class="max-w-128 max-h-128 w-auto w-auto"
src={URL.createObjectURL(imageArguments.blob)}
/>
</div>
<div class="flex w-full">
@ -54,8 +59,7 @@
<Tr t={t.general.back} />
</BackButton>
<button on:click={() => del()} class="primary w-full">
<TrashIcon class="w-8 m-1" />
<TrashIcon class="m-1 w-8" />
<Tr t={t.imageQueue.confirmDelete} />
</button>
</div>

View file

@ -23,13 +23,13 @@
<Tr t={q.intro} />
</div>
<UploadingImageCounter {state}/>
<UploadingImageCounter {state} />
{#if $isUploading}
<Loading />
{:else}
<button class="primary" on:click={() => state.imageUploadManager.uploadQueue()}>
<ArrowPathIcon class="w-8 h-8 m-1" />
<ArrowPathIcon class="m-1 h-8 w-8" />
<Tr t={q.retryAll} />
</button>
{/if}

View file

@ -47,7 +47,14 @@
errs.push(canBeUploaded.error)
continue
}
await state?.imageUploadManager?.uploadImageAndApply(file, tags, targetKey, noBlur, feature, { ignoreGPS })
await state?.imageUploadManager?.uploadImageAndApply(
file,
tags,
targetKey,
noBlur,
feature,
{ ignoreGPS }
)
} catch (e) {
console.error(e)
state.reportError(e, "Could not upload image")

View file

@ -26,9 +26,9 @@
*/
function getCount(input: Store<string[]>): Store<number> {
if (featureId == "*") {
return input.map(inp => inp.length)
return input.map((inp) => inp.length)
}
return input.map(success => success.filter(item => item === featureId).length)
return input.map((success) => success.filter((item) => item === featureId).length)
}
let successfull = getCount(state.imageUploadManager.successfull)
@ -39,7 +39,7 @@
const t = Translations.t.image
const debugging = state.featureSwitches.featureSwitchIsDebugging
let dismissed = 0
failed.addCallbackAndRun(failed => {
failed.addCallbackAndRun((failed) => {
dismissed = Math.min(failed, dismissed)
})
</script>
@ -56,7 +56,7 @@
{#if $pending - $failed === 1}
<Tr t={t.upload.one.uploading} />
{:else if $pending - $failed > 1}
<Tr t={t.upload.multiple.uploading.Subs({count: $pending})} />
<Tr t={t.upload.multiple.uploading.Subs({ count: $pending })} />
{/if}
</Loading>
</div>
@ -70,6 +70,6 @@
{#if $successfull === 1}
<Tr cls="thanks" t={t.upload.one.done} />
{:else if $successfull > 1}
<Tr cls="thanks" t={t.upload.multiple.done.Subs({count: $successfull})} />
<Tr cls="thanks" t={t.upload.multiple.done.Subs({ count: $successfull })} />
{/if}
{/if}

View file

@ -4,38 +4,37 @@ import { Feature, Geometry, Point } from "geojson"
import { GeoOperations } from "../../Logic/GeoOperations"
import { HotspotProperties, PanoramaView } from "../../Logic/ImageProviders/ImageProvider"
export class PhotoSphereViewerWrapper {
private imageInfo: Feature<Point, PanoramaView>
private readonly viewer: Pannellum.Viewer
private nearbyFeatures: Feature<Geometry, HotspotProperties>[] = []
constructor(container: HTMLElement, imageInfo: Feature<Point, PanoramaView>, nearbyFeatures?: Feature<Geometry, HotspotProperties>[]) {
constructor(
container: HTMLElement,
imageInfo: Feature<Point, PanoramaView>,
nearbyFeatures?: Feature<Geometry, HotspotProperties>[]
) {
this.imageInfo = imageInfo
this.viewer = pannellum.viewer(container,
<any>{
default: {
firstScene: imageInfo.properties.url,
sceneFadeDuration: 250
this.viewer = pannellum.viewer(container, <any>{
default: {
firstScene: imageInfo.properties.url,
sceneFadeDuration: 250,
},
scenes: {
[imageInfo.properties.url]: {
type: "equirectangular",
hfov: 110,
panorama: imageInfo.properties.url,
autoLoad: true,
hotSpots: [],
sceneFadeDuration: 250,
compass: true,
showControls: false,
northOffset: imageInfo.properties.northOffset,
horizonPitch: imageInfo.properties.pitchOffset,
},
scenes: {
[imageInfo.properties.url]:
{
type: "equirectangular",
hfov: 110,
panorama: imageInfo.properties.url,
autoLoad: true,
hotSpots: [],
sceneFadeDuration: 250,
compass: true,
showControls: false,
northOffset: imageInfo.properties.northOffset,
horizonPitch: imageInfo.properties.pitchOffset
}
}
}
)
},
})
this.setNearbyFeatures(nearbyFeatures)
}
@ -43,12 +42,13 @@ export class PhotoSphereViewerWrapper {
public calculatePitch(feature: Feature): number {
const coors = this.imageInfo.geometry.coordinates
const distance = GeoOperations.distanceBetween(
<[number, number]>coors, GeoOperations.centerpointCoordinates(feature)
<[number, number]>coors,
GeoOperations.centerpointCoordinates(feature)
)
// In: -pi/2 up to pi/2
const alpha = Math.atan(distance / 4) // in radians
const degrees = alpha * 360 / (2 * Math.PI)
const degrees = (alpha * 360) / (2 * Math.PI)
return -degrees
}
@ -62,7 +62,7 @@ export class PhotoSphereViewerWrapper {
this.viewer.addScene(imageInfo.properties.url, <any>{
panorama: imageInfo.properties.url,
northOffset: imageInfo.properties.northOffset,
type: "equirectangular"
type: "equirectangular",
})
this.viewer.loadScene(imageInfo.properties.url, 0, imageInfo.properties.northOffset)
@ -70,7 +70,8 @@ export class PhotoSphereViewerWrapper {
}
private clearHotspots() {
const hotspots = this.viewer.getConfig()["scenes"][this.imageInfo.properties.url].hotSpots ?? []
const hotspots =
this.viewer.getConfig()["scenes"][this.imageInfo.properties.url].hotSpots ?? []
for (const hotspot of hotspots) {
this.viewer.removeHotSpot(hotspot?.id, this.imageInfo.properties.url)
}
@ -95,20 +96,21 @@ export class PhotoSphereViewerWrapper {
} else if (!isNaN(f.properties.pitch)) {
pitch = f.properties.pitch
}
this.viewer.addHotSpot({
type: f.properties.gotoPanorama !== undefined ? "scene" : "info",
yaw: (yaw - northOffs) % 360,
pitch,
text: f.properties.name,
clickHandlerFunc: () => {
this.setPanorama(f.properties.gotoPanorama)
}
}, this.imageInfo.properties.url)
this.viewer.addHotSpot(
{
type: f.properties.gotoPanorama !== undefined ? "scene" : "info",
yaw: (yaw - northOffs) % 360,
pitch,
text: f.properties.name,
clickHandlerFunc: () => {
this.setPanorama(f.properties.gotoPanorama)
},
},
this.imageInfo.properties.url
)
if (f.properties.focus) {
this.viewer.setYaw(yaw - northOffs)
}
}
}
}