diff --git a/src/Logic/ImageProviders/LicenseInfo.ts b/src/Logic/ImageProviders/LicenseInfo.ts index da82a9b37..74f4f0be4 100644 --- a/src/Logic/ImageProviders/LicenseInfo.ts +++ b/src/Logic/ImageProviders/LicenseInfo.ts @@ -8,7 +8,7 @@ export class LicenseInfo { copyrighted?: boolean = false credit?: string = "" description?: string = "" - informationLocation?: URL = undefined + informationLocation?: URL | string = undefined date?: Date views?: number } diff --git a/src/Logic/ImageProviders/Mapillary.ts b/src/Logic/ImageProviders/Mapillary.ts index 86e9ea629..43cdb7ae7 100644 --- a/src/Logic/ImageProviders/Mapillary.ts +++ b/src/Logic/ImageProviders/Mapillary.ts @@ -192,7 +192,12 @@ export class Mapillary extends ImageProvider { license.license = "CC BY-SA 4.0" // license.license = "Creative Commons Attribution-ShareAlike 4.0 International License"; license.attributionRequired = true - license.date = new Date(response["captured_at"]) + const date = response["captured_at"] + try { + license.date = new Date(date) + } catch (e) { + console.warn("Could not parse captured_at date from mapillary image. The date is:", date) + } return license } diff --git a/src/Logic/Web/NearbyImagesSearch.ts b/src/Logic/Web/NearbyImagesSearch.ts index 4783ee643..066bb10cf 100644 --- a/src/Logic/Web/NearbyImagesSearch.ts +++ b/src/Logic/Web/NearbyImagesSearch.ts @@ -9,6 +9,7 @@ import { Utils } from "../../Utils" import { Point } from "geojson" import { Imgur } from "../ImageProviders/Imgur" import { ImageData, Panoramax, PanoramaxXYZ } from "panoramax-js/dist" +import { Mapillary } from "../ImageProviders/Mapillary" interface ImageFetcher { /** @@ -222,6 +223,11 @@ class ImagesFromPanoramaxFetcher implements ImageFetcher { const promises: Promise[] = [] const maxRadius = this._radius let prevRadius = 0 + + const nearby = this._panoramax.search({ + bbox: new BBox([[lon, lat]]).pad(0.001).toLngLatFlat() + }) + promises.push(nearby) // We do a nearby search with bbox, see https://source.mapcomplete.org/MapComplete/MapComplete/issues/2384 for (const radiusSetting of radiusSettings) { const promise = this._panoramax.search({ place: [lon, lat], @@ -265,7 +271,7 @@ class MapillaryFetcher implements ImageFetcher { async fetchImages(lat: number, lon: number): Promise { const boundingBox = new BBox([[lon, lat]]).padAbsolute(0.003) let url = - "https://graph.mapillary.com/images?fields=geometry,computed_geometry,creator,id,thumb_256_url,thumb_original_url,compass_angle&bbox=" + + "https://graph.mapillary.com/images?fields=geometry,computed_geometry,creator,id,captured_at,thumb_256_url,thumb_original_url,compass_angle&bbox=" + [ boundingBox.getWest(), boundingBox.getSouth(), @@ -293,13 +299,14 @@ class MapillaryFetcher implements ImageFetcher { const response = await Utils.downloadJson<{ data: { id: string - creator: string + creator: { username: string } geometry: Point computed_geometry: Point is_pano: boolean thumb_256_url: string thumb_original_url: string compass_angle: number + captured_at: number }[] }>(url) const pics: P4CPicture[] = [] @@ -308,6 +315,7 @@ class MapillaryFetcher implements ImageFetcher { if (img.thumb_original_url === undefined) { continue } + const [lon, lat] = img.computed_geometry.coordinates pics.push({ pictureUrl: img.thumb_original_url, provider: "Mapillary", @@ -319,6 +327,12 @@ class MapillaryFetcher implements ImageFetcher { details: { isSpherical: this._panoramas === "only", }, + + detailsUrl: Mapillary.singleton.visitUrl(img, { lon, lat }), + date: img.captured_at, + license: "CC-BY-SA", + author: img.creator.username, + direction: img.compass_angle }) } return pics @@ -367,7 +381,6 @@ export class CombinedFetcher { ): Promise { try { const pics = await source.fetchImages(lat, lon) - console.log(source.name, "==>>", pics) state.data[source.name] = "done" state.ping() diff --git a/src/Models/ThemeConfig/Json/TagRenderingConfigJson.ts b/src/Models/ThemeConfig/Json/TagRenderingConfigJson.ts index 31ffba1dd..a6dbc1286 100644 --- a/src/Models/ThemeConfig/Json/TagRenderingConfigJson.ts +++ b/src/Models/ThemeConfig/Json/TagRenderingConfigJson.ts @@ -246,5 +246,5 @@ export interface TagRenderingConfigJson { * Note: if the theme already has a layer with this ID, the value is ignored * group: hidden */ - requiredLayers: { id: string; minzoom?: number }[] + requiredLayers?: { id: string; minzoom?: number }[] } diff --git a/src/UI/Image/LinkableImage.svelte b/src/UI/Image/LinkableImage.svelte index 18aed3269..69f3aff67 100644 --- a/src/UI/Image/LinkableImage.svelte +++ b/src/UI/Image/LinkableImage.svelte @@ -37,18 +37,29 @@ } }) const t = Translations.t.image.nearby + + let date: Date + if (image.date) { + try { + date = new Date(image.date) + } catch (e) { + console.warn("Could not parse image date", image.date, "for", image.detailsUrl) + } + } + let license: LicenseInfo = { artist: image.author, license: image.license, - date: new Date(image.date), - informationLocation: image.detailsUrl, + informationLocation: (image.detailsUrl ?? image.pictureUrl ?? image.thumbUrl), + date } + console.log(">>> trying to create license info based on", image, license) let providedImage: ProvidedImage = { url: image.thumbUrl ?? image.pictureUrl, url_hd: image.pictureUrl, key: undefined, provider: AllImageProviders.byName(image.provider), - date: new Date(image.date), + date, id: Object.values(image.osmTags)[0], isSpherical: image.details.isSpherical, license, diff --git a/src/UI/Image/NearbyImages.svelte b/src/UI/Image/NearbyImages.svelte index 5cfff3742..29bb15c26 100644 --- a/src/UI/Image/NearbyImages.svelte +++ b/src/UI/Image/NearbyImages.svelte @@ -7,7 +7,7 @@ import type { SpecialVisualizationState } from "../SpecialVisualization" import type { P4CPicture } from "../../Logic/Web/NearbyImagesSearch" import LinkableImage from "./LinkableImage.svelte" - import type { Feature, Point } from "geojson" + import type { Feature, Geometry, Point } from "geojson" import LayerConfig from "../../Models/ThemeConfig/LayerConfig" import Loading from "../Base/Loading.svelte" import AllImageProviders from "../../Logic/ImageProviders/AllImageProviders" @@ -25,7 +25,7 @@ import { BBox } from "../../Logic/BBox" import PanoramaxLink from "../BigComponents/PanoramaxLink.svelte" import { GeoOperations } from "../../Logic/GeoOperations" - import type { PanoramaView } from "../../Logic/ImageProviders/ImageProvider" + import type { HotspotProperties, PanoramaView } from "../../Logic/ImageProviders/ImageProvider" export let tags: UIEventSource export let state: SpecialVisualizationState @@ -52,6 +52,7 @@ [loadedImages] ) + // Panorama-views get a geojson feature to browse around let asFeatures = result.map((p4cs) => p4cs.map( (p4c) => @@ -147,25 +148,27 @@ highlighted.set(feature.properties.id) }, }) - let nearbyFeatures: Store = asFeatures.map((nearbyPoints) => { + + let nearbyFeatures: Store[]> = asFeatures.map((nearbyPoints) => { return [ { type: "Feature", geometry: { type: "Point", coordinates: GeoOperations.centerpointCoordinates(feature) }, - properties: { + properties: { name: layer.title?.GetRenderValue(feature.properties).Subs(feature.properties).txt, focus: true, }, }, ...nearbyPoints - .filter((p) => p.properties.spherical === "yes") + .filter((p) => p.properties["spherical"] === "yes") .map((f) => ({ ...f, - properties: { + properties: { name: "Nearby panorama", pitch: "auto", type: "scene", gotoPanorama: f, + focus: false }, })), ]