UX: add link to Mapillary, fix #1637
This commit is contained in:
parent
c7089c27a0
commit
804005e402
12 changed files with 82 additions and 37 deletions
|
@ -31,11 +31,12 @@ export default class GenericImageProvider extends ImageProvider {
|
|||
key: key,
|
||||
url: value,
|
||||
provider: this,
|
||||
id: value
|
||||
}),
|
||||
]
|
||||
}
|
||||
|
||||
SourceIcon(backlinkSource?: string) {
|
||||
SourceIcon() {
|
||||
return undefined
|
||||
}
|
||||
|
||||
|
|
|
@ -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<string>()
|
||||
allTags.addCallbackAndRunD((tags) => {
|
||||
|
@ -67,4 +68,10 @@ export default abstract class ImageProvider {
|
|||
public abstract DownloadAttribution(url: string): Promise<LicenseInfo>
|
||||
|
||||
public abstract apiUrls(): string[]
|
||||
|
||||
public backlink(): string | undefined {
|
||||
return undefined
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -66,6 +66,7 @@ export class Imgur extends ImageProvider implements ImageUploader {
|
|||
url: value,
|
||||
key: key,
|
||||
provider: this,
|
||||
id: value
|
||||
}),
|
||||
]
|
||||
}
|
||||
|
|
|
@ -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<Promise<ProvidedImage>[]> {
|
||||
|
@ -111,6 +135,7 @@ export class Mapillary extends ImageProvider {
|
|||
const response = await Utils.downloadJsonCached(metadataUrl, 60 * 60)
|
||||
const url = <string>response["thumb_1024_url"]
|
||||
return {
|
||||
id: "" + mapillaryId,
|
||||
url: url,
|
||||
provider: this,
|
||||
key: key,
|
||||
|
|
|
@ -15,7 +15,7 @@ export class WikidataImageProvider extends ImageProvider {
|
|||
super()
|
||||
}
|
||||
|
||||
public SourceIcon(_?: string): BaseUIElement {
|
||||
public SourceIcon(): BaseUIElement {
|
||||
return Svg.wikidata_svg()
|
||||
}
|
||||
|
||||
|
|
|
@ -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}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
</script>
|
||||
|
||||
<a class="button flex items-center" href={mapillaryLink} target="_blank">
|
||||
|
|
|
@ -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,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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<any>,
|
||||
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([
|
||||
|
|
|
@ -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)
|
||||
)
|
||||
|
|
|
@ -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
|
||||
)
|
||||
},
|
||||
},
|
||||
|
|
Loading…
Reference in a new issue