Performance: cache panoramax requests more aggressively; reuse license information that comes from the search-api; lower picture limit; should relieve #2384 greatly

This commit is contained in:
Pieter Vander Vennet 2025-04-22 02:53:31 +02:00
parent bd287fd8b0
commit 549129671a
7 changed files with 53 additions and 45 deletions

View file

@ -20,7 +20,8 @@ export interface ProvidedImage {
lat?: number
lon?: number
host?: string
isSpherical: boolean
isSpherical: boolean,
license?: LicenseInfo
}
export interface PanoramaView {

View file

@ -24,7 +24,7 @@ export default class PanoramaxImageProvider extends ImageProvider {
public defaultKeyPrefixes: string[] = ["panoramax"]
public readonly name: string = "panoramax"
private static knownMeta: Record<string, { data: ImageData; time: Date }> = {}
private static knownMeta: Record<string, { data: Promise<{ data: ImageData, url: string }>; time: Date }> = {}
public SourceIcon(
img?: { id: string; url: string },
@ -45,8 +45,8 @@ export default class PanoramaxImageProvider extends ImageProvider {
)
}
public addKnownMeta(meta: ImageData) {
PanoramaxImageProvider.knownMeta[meta.id] = { data: meta, time: new Date() }
public addKnownMeta(meta: ImageData, url?: string) {
PanoramaxImageProvider.knownMeta[meta.id] = { data: Promise.resolve({ data: meta, url }), time: new Date() }
}
/**
@ -102,16 +102,7 @@ export default class PanoramaxImageProvider extends ImageProvider {
}
}
private async getInfoFor(id: string): Promise<{ data: ImageData; url: string }> {
if (!id.match(/^[a-zA-Z0-9-]+$/)) {
return undefined
}
const cached = PanoramaxImageProvider.knownMeta[id]
if (cached) {
if (new Date().getTime() - cached.time.getTime() < 1000) {
return { data: cached.data, url: undefined }
}
}
private async getInfoForUncached(id: string) {
try {
return await this.getInfoFromMapComplete(id)
} catch (e) {
@ -125,6 +116,25 @@ export default class PanoramaxImageProvider extends ImageProvider {
return undefined
}
private async getInfoFor(id: string): Promise<{ data: ImageData; url: string }> {
if (!id.match(/^[a-zA-Z0-9-]+$/)) {
return undefined
}
const cached = PanoramaxImageProvider.knownMeta[id]
if (cached) {
if (new Date().getTime() - cached.time.getTime() < 5000) {
return await cached.data
}
}
const promise: Promise<{ data: ImageData; url: string }> = this.getInfoForUncached(id)
PanoramaxImageProvider.knownMeta[id] = {
time: new Date(),
data: promise
}
return await promise
}
public async ExtractUrls(key: string, value: string): Promise<ProvidedImage[]> {
if (!Panoramax.isId(value)) {
return undefined
@ -139,7 +149,7 @@ export default class PanoramaxImageProvider extends ImageProvider {
getRelevantUrls(tags: Record<string, string>, prefixes: string[]): Store<ProvidedImage[]> {
const source = UIEventSource.FromPromise(super.getRelevantUrlsFor(tags, prefixes))
console.trace("Getting relevant URLS for panoramax yielded", source.data)
function hasLoading(data: ProvidedImage[]) {
if (data === undefined) {
return true

View file

@ -172,7 +172,7 @@ class ImagesFromPanoramaxFetcher implements ImageFetcher {
"https://panoramax.mapcomplete.org",
]
constructor(url?: string, radius: number = 100) {
constructor(url?: string, radius: number = 50) {
this._radius = radius
if (url) {
this._panoramax = new Panoramax(url)
@ -182,12 +182,11 @@ class ImagesFromPanoramaxFetcher implements ImageFetcher {
}
public async fetchImages(lat: number, lon: number): Promise<P4CPicture[]> {
const bboxObj = new BBox([
GeoOperations.destination([lon, lat], this._radius * Math.sqrt(2), -45),
GeoOperations.destination([lon, lat], this._radius * Math.sqrt(2), 135),
])
const bbox: [number, number, number, number] = bboxObj.toLngLatFlat()
const images = await this._panoramax.search({ bbox, limit: 1000 })
const images = await this._panoramax.search({
place: [lon, lat],
place_distance: this._radius ?? 50,
limit: 50
})
return images.map((i) => {
const [lng, lat] = i.geometry.coordinates

View file

@ -1,7 +1,7 @@
<script lang="ts">
import { LicenseInfo } from "../../Logic/ImageProviders/LicenseInfo"
import type { ProvidedImage } from "../../Logic/ImageProviders/ImageProvider"
import { Store, UIEventSource } from "../../Logic/UIEventSource"
import { ImmutableStore, Store, UIEventSource } from "../../Logic/UIEventSource"
import ToSvelte from "../Base/ToSvelte.svelte"
import { EyeIcon } from "@rgossiaux/svelte-heroicons/solid"
import Tr from "../Base/Tr.svelte"
@ -13,7 +13,7 @@
export let image: Partial<ProvidedImage> & { id: string; url: string }
export let attributionFormat: "minimal" | "medium" | "large" = "medium"
let license: Store<LicenseInfo> = UIEventSource.FromPromise(
let license: Store<LicenseInfo> = image.license ? new ImmutableStore(image.license) : UIEventSource.FromPromise(
image.provider?.DownloadAttribution(image)
)
let icon = image.provider?.SourceIcon(image)

View file

@ -18,6 +18,7 @@
import { Utils } from "../../Utils"
import ThemeViewState from "../../Models/ThemeViewState"
import { MenuState } from "../../Models/MenuState"
import { LicenseInfo } from "../../Logic/ImageProviders/LicenseInfo"
export let tags: UIEventSource<OsmTags>
export let state: ThemeViewState
@ -36,7 +37,13 @@
}
})
const t = Translations.t.image.nearby
const providedImage: ProvidedImage = {
let license: LicenseInfo = {
artist: image.author,
license: image.license,
date: new Date(image.date),
informationLocation: image.detailsUrl
}
let providedImage: ProvidedImage = {
url: image.thumbUrl ?? image.pictureUrl,
url_hd: image.pictureUrl,
key: undefined,
@ -44,6 +51,7 @@
date: new Date(image.date),
id: Object.values(image.osmTags)[0],
isSpherical: image.details.isSpherical,
license
}
async function applyLink(isLinked: boolean) {