diff --git a/src/Logic/ImageProviders/GenericImageProvider.ts b/src/Logic/ImageProviders/GenericImageProvider.ts index 178137027..e6a472701 100644 --- a/src/Logic/ImageProviders/GenericImageProvider.ts +++ b/src/Logic/ImageProviders/GenericImageProvider.ts @@ -31,11 +31,12 @@ export default class GenericImageProvider extends ImageProvider { key: key, url: value, provider: this, + id: value }), ] } - SourceIcon(backlinkSource?: string) { + SourceIcon() { return undefined } diff --git a/src/Logic/ImageProviders/ImageProvider.ts b/src/Logic/ImageProviders/ImageProvider.ts index 6d42623ce..1f7fad760 100644 --- a/src/Logic/ImageProviders/ImageProvider.ts +++ b/src/Logic/ImageProviders/ImageProvider.ts @@ -6,13 +6,14 @@ import { Utils } from "../../Utils" export interface ProvidedImage { url: string key: string - provider: ImageProvider + provider: ImageProvider, + id: string } export default abstract class ImageProvider { public abstract readonly defaultKeyPrefixes: string[] - public abstract SourceIcon(backlinkSource?: string): BaseUIElement + public abstract SourceIcon(id?: string, location?: {lon: number, lat: number}): BaseUIElement /** * Given a properies object, maps it onto _all_ the available pictures for this imageProvider @@ -28,7 +29,7 @@ export default abstract class ImageProvider { throw "No `defaultKeyPrefixes` defined by this image provider" } const relevantUrls = new UIEventSource< - { url: string; key: string; provider: ImageProvider }[] + { id: string, url: string; key: string; provider: ImageProvider }[] >([]) const seenValues = new Set() allTags.addCallbackAndRunD((tags) => { @@ -67,4 +68,10 @@ export default abstract class ImageProvider { public abstract DownloadAttribution(url: string): Promise public abstract apiUrls(): string[] + + public backlink(): string | undefined { + return undefined + } + + } diff --git a/src/Logic/ImageProviders/Imgur.ts b/src/Logic/ImageProviders/Imgur.ts index b84fe606f..904221768 100644 --- a/src/Logic/ImageProviders/Imgur.ts +++ b/src/Logic/ImageProviders/Imgur.ts @@ -66,6 +66,7 @@ export class Imgur extends ImageProvider implements ImageUploader { url: value, key: key, provider: this, + id: value }), ] } diff --git a/src/Logic/ImageProviders/Mapillary.ts b/src/Logic/ImageProviders/Mapillary.ts index 96ec29ebc..ed332c4f8 100644 --- a/src/Logic/ImageProviders/Mapillary.ts +++ b/src/Logic/ImageProviders/Mapillary.ts @@ -4,6 +4,7 @@ import Svg from "../../Svg" import { Utils } from "../../Utils" import { LicenseInfo } from "./LicenseInfo" import Constants from "../../Models/Constants" +import Link from "../../UI/Base/Link" export class Mapillary extends ImageProvider { public static readonly singleton = new Mapillary() @@ -17,10 +18,6 @@ export class Mapillary extends ImageProvider { ] defaultKeyPrefixes = ["mapillary", "image"] - apiUrls(): string[] { - return ["https://mapillary.com", "https://www.mapillary.com", "https://graph.mapillary.com"] - } - /** * Indicates that this is the same URL * Ignores 'stp' parameter @@ -57,6 +54,22 @@ export class Mapillary extends ImageProvider { return false } + static createLink(location: { + lon: number, + lat: number + } = undefined, zoom: number = 17, pKey?: string) { + const params = { + focus: pKey === undefined ? "map" : "photo", + lat: location.lat, + lng: location.lon, + z: location === undefined ? undefined : Math.max((zoom ?? 2) - 1, 1), + pKey, + } + const baselink = `https://www.mapillary.com/app/?` + const paramsStr = Utils.NoNull(Object.keys(params).map(k => params[k] === undefined ? undefined : k + "=" + params[k])) + return baselink + paramsStr.join("&") + } + /** * Returns the correct key for API v4.0 */ @@ -80,8 +93,19 @@ export class Mapillary extends ImageProvider { return undefined } - SourceIcon(backlinkSource?: string): BaseUIElement { - return Svg.mapillary_svg() + apiUrls(): string[] { + return ["https://mapillary.com", "https://www.mapillary.com", "https://graph.mapillary.com"] + } + + SourceIcon(id: string, location?: { + lon: number, + lat: number + }): BaseUIElement { + const icon = Svg.mapillary_svg() + if (!id) { + return icon + } + return new Link(icon, Mapillary.createLink(location, 16, "" + id), true) } async ExtractUrls(key: string, value: string): Promise[]> { @@ -111,6 +135,7 @@ export class Mapillary extends ImageProvider { const response = await Utils.downloadJsonCached(metadataUrl, 60 * 60) const url = response["thumb_1024_url"] return { + id: "" + mapillaryId, url: url, provider: this, key: key, diff --git a/src/Logic/ImageProviders/WikidataImageProvider.ts b/src/Logic/ImageProviders/WikidataImageProvider.ts index 1c7cbea8a..19dcbcbf5 100644 --- a/src/Logic/ImageProviders/WikidataImageProvider.ts +++ b/src/Logic/ImageProviders/WikidataImageProvider.ts @@ -15,7 +15,7 @@ export class WikidataImageProvider extends ImageProvider { super() } - public SourceIcon(_?: string): BaseUIElement { + public SourceIcon(): BaseUIElement { return Svg.wikidata_svg() } diff --git a/src/Logic/ImageProviders/WikimediaImageProvider.ts b/src/Logic/ImageProviders/WikimediaImageProvider.ts index a9841b8fd..1ecb95df5 100644 --- a/src/Logic/ImageProviders/WikimediaImageProvider.ts +++ b/src/Logic/ImageProviders/WikimediaImageProvider.ts @@ -1,7 +1,6 @@ import ImageProvider, { ProvidedImage } from "./ImageProvider" import BaseUIElement from "../../UI/BaseUIElement" import Svg from "../../Svg" -import Link from "../../UI/Base/Link" import { Utils } from "../../Utils" import { LicenseInfo } from "./LicenseInfo" import Wikimedia from "../Web/Wikimedia" @@ -70,17 +69,8 @@ export class WikimediaImageProvider extends ImageProvider { return WikimediaImageProvider.apiUrls } - SourceIcon(backlink: string): BaseUIElement { - const img = Svg.wikimedia_commons_white_svg().SetStyle("width:2em;height: 2em") - if (backlink === undefined) { - return img - } - - return new Link( - Svg.wikimedia_commons_white_svg(), - `https://commons.wikimedia.org/wiki/${backlink}`, - true - ) + SourceIcon(): BaseUIElement { + return Svg.wikimedia_commons_white_svg().SetStyle("width:2em;height: 2em") } public PrepUrl(value: string): ProvidedImage { @@ -173,6 +163,6 @@ export class WikimediaImageProvider extends ImageProvider { if (!image.startsWith("File:")) { image = "File:" + image } - return { url: WikimediaImageProvider.PrepareUrl(image), key: undefined, provider: this } + return { url: WikimediaImageProvider.PrepareUrl(image), key: undefined, provider: this , id: image} } } diff --git a/src/UI/BigComponents/MapillaryLink.svelte b/src/UI/BigComponents/MapillaryLink.svelte index e51bda0c3..c2a229400 100644 --- a/src/UI/BigComponents/MapillaryLink.svelte +++ b/src/UI/BigComponents/MapillaryLink.svelte @@ -5,6 +5,7 @@ import Tr from "../Base/Tr.svelte" import ToSvelte from "../Base/ToSvelte.svelte" import Mapillary_black from "../../assets/svg/Mapillary_black.svelte"; + import { Mapillary } from "../../Logic/ImageProviders/Mapillary" /* A subtleButton which opens mapillary in a new tab at the current location @@ -16,9 +17,7 @@ } let location = mapProperties.location let zoom = mapProperties.zoom - let mapillaryLink = `https://www.mapillary.com/app/?focus=map&lat=${$location?.lat ?? 0}&lng=${ - $location?.lon ?? 0 - }&z=${Math.max(($zoom ?? 2) - 1, 1)}` + let mapillaryLink = Mapillary.createLink($location, $zoom) diff --git a/src/UI/Image/AttributedImage.ts b/src/UI/Image/AttributedImage.ts index 2c4820d2e..6a4894c0b 100644 --- a/src/UI/Image/AttributedImage.ts +++ b/src/UI/Image/AttributedImage.ts @@ -5,21 +5,37 @@ import ImageProvider from "../../Logic/ImageProviders/ImageProvider" import BaseUIElement from "../BaseUIElement" import { Mapillary } from "../../Logic/ImageProviders/Mapillary" import { UIEventSource } from "../../Logic/UIEventSource" +import { Feature } from "geojson" +import { GeoOperations } from "../../Logic/GeoOperations" export class AttributedImage extends Combine { - constructor(imageInfo: { url: string; provider?: ImageProvider; date?: Date }) { + constructor(imageInfo: { + id: string, + url: string; + provider?: ImageProvider; + date?: Date + }, feature?: Feature) { let img: BaseUIElement img = new Img(imageInfo.url, false, { fallbackImage: imageInfo.provider === Mapillary.singleton ? "./assets/svg/blocked.svg" : undefined, }) + let location: { + lon: number, + lat: number + } = undefined + if (feature) { + + const [lon, lat] = GeoOperations.centerpointCoordinates(feature) + location = { lon, lat } + } let attr: BaseUIElement = undefined if (imageInfo.provider !== undefined) { attr = new Attribution( UIEventSource.FromPromise(imageInfo.provider?.DownloadAttribution(imageInfo.url)), - imageInfo.provider?.SourceIcon(), - imageInfo.date + imageInfo.provider?.SourceIcon(imageInfo.id, location), + imageInfo.date, ) } diff --git a/src/UI/Image/Attribution.ts b/src/UI/Image/Attribution.ts index a44189a1e..aa5a017d3 100644 --- a/src/UI/Image/Attribution.ts +++ b/src/UI/Image/Attribution.ts @@ -28,6 +28,7 @@ export default class Attribution extends VariableUiElement { title = new Link(title, license.informationLocation.href, true) } } + return new Combine([ icon ?.SetClass("block left") diff --git a/src/UI/Image/ImageCarousel.ts b/src/UI/Image/ImageCarousel.ts index 8818252d7..70ba392bb 100644 --- a/src/UI/Image/ImageCarousel.ts +++ b/src/UI/Image/ImageCarousel.ts @@ -9,19 +9,21 @@ import ImageProvider from "../../Logic/ImageProviders/ImageProvider" import { OsmConnection } from "../../Logic/Osm/OsmConnection" import { Changes } from "../../Logic/Osm/Changes" import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig" +import { Feature } from "geojson" export class ImageCarousel extends Toggle { constructor( - images: Store<{ key: string; url: string; provider: ImageProvider }[]>, + images: Store<{ id:string, key: string; url: string; provider: ImageProvider }[]>, tags: Store, - state: { osmConnection?: OsmConnection; changes?: Changes; layout: LayoutConfig } + state: { osmConnection?: OsmConnection; changes?: Changes; layout: LayoutConfig }, + feature: Feature ) { const uiElements = images.map( - (imageURLS: { key: string; url: string; provider: ImageProvider }[]) => { + (imageURLS: { key: string; url: string; provider: ImageProvider, id: string }[]) => { const uiElements: BaseUIElement[] = [] for (const url of imageURLS) { try { - let image = new AttributedImage(url) + let image = new AttributedImage(url, feature) if (url.key !== undefined) { image = new Combine([ diff --git a/src/UI/Popup/LinkableImage.svelte b/src/UI/Popup/LinkableImage.svelte index fb8710dc8..d9290fbdd 100644 --- a/src/UI/Popup/LinkableImage.svelte +++ b/src/UI/Popup/LinkableImage.svelte @@ -28,11 +28,13 @@ const t = Translations.t.image.nearby const c = [lon, lat] + console.log(">>>", image) let attributedImage = new AttributedImage({ url: image.thumbUrl ?? image.pictureUrl, provider: AllImageProviders.byName(image.provider), date: new Date(image.date), - }) + id: Object.values(image.osmTags)[0] + }, feature) let distance = Math.round( GeoOperations.distanceBetween([image.coordinates.lng, image.coordinates.lat], c) ) diff --git a/src/UI/SpecialVisualizations.ts b/src/UI/SpecialVisualizations.ts index c0720293a..5400f8316 100644 --- a/src/UI/SpecialVisualizations.ts +++ b/src/UI/SpecialVisualizations.ts @@ -654,7 +654,7 @@ export default class SpecialVisualizations { }, ], needsUrls: AllImageProviders.apiUrls, - constr: (state, tags, args) => { + constr: (state, tags, args, feature) => { let imagePrefixes: string[] = undefined if (args.length > 0) { imagePrefixes = [].concat(...args.map((a) => a.split(","))) @@ -662,7 +662,8 @@ export default class SpecialVisualizations { return new ImageCarousel( AllImageProviders.LoadImagesFor(tags, imagePrefixes), tags, - state + state, + feature ) }, },