2021-09-29 23:56:59 +02:00
|
|
|
import ImageProvider, { ProvidedImage } from "./ImageProvider"
|
2021-06-18 01:25:13 +02:00
|
|
|
import BaseUIElement from "../../UI/BaseUIElement"
|
2021-07-03 22:24:12 +02:00
|
|
|
import { Utils } from "../../Utils"
|
2021-09-29 23:56:59 +02:00
|
|
|
import { LicenseInfo } from "./LicenseInfo"
|
|
|
|
import Constants from "../../Models/Constants"
|
2023-12-14 18:25:35 +01:00
|
|
|
import SvelteUIElement from "../../UI/Base/SvelteUIElement"
|
|
|
|
import MapillaryIcon from "./MapillaryIcon.svelte"
|
2020-10-17 00:37:45 +02:00
|
|
|
|
2021-09-29 23:56:59 +02:00
|
|
|
export class Mapillary extends ImageProvider {
|
2021-06-18 01:25:13 +02:00
|
|
|
public static readonly singleton = new Mapillary()
|
2024-07-27 12:59:38 +02:00
|
|
|
public readonly name = "Mapillary"
|
|
|
|
|
2021-10-01 02:57:41 +02:00
|
|
|
private static readonly valuePrefix = "https://a.mapillary.com"
|
2021-11-07 16:34:51 +01:00
|
|
|
public static readonly valuePrefixes = [
|
|
|
|
Mapillary.valuePrefix,
|
|
|
|
"http://mapillary.com",
|
|
|
|
"https://mapillary.com",
|
|
|
|
"http://www.mapillary.com",
|
|
|
|
"https://www.mapillary.com",
|
|
|
|
]
|
|
|
|
defaultKeyPrefixes = ["mapillary", "image"]
|
2021-09-15 01:33:52 +02:00
|
|
|
|
2022-05-06 12:41:24 +02:00
|
|
|
/**
|
|
|
|
* Indicates that this is the same URL
|
|
|
|
* Ignores 'stp' parameter
|
|
|
|
*
|
|
|
|
* const a = "https://scontent-bru2-1.xx.fbcdn.net/m1/v/t6/An8xm5SGLt20ETziNqzhhBd8b8S5GHLiIu8N6BbyqHFohFAQoaJJPG8i5yQiSwjYmEqXSfVeoCmpiyBJICEkQK98JOB21kkJoBS8VdhYa-Ty93lBnznQyesJBtKcb32foGut2Hgt10hEMWJbE3dDgA?stp=s1024x768&ccb=10-5&oh=00_AT-ZGTXHzihoaQYBILmEiAEKR64z_IWiTlcAYq_D7Ka0-Q&oe=6278C456&_nc_sid=122ab1"
|
|
|
|
* const b = "https://scontent-bru2-1.xx.fbcdn.net/m1/v/t6/An8xm5SGLt20ETziNqzhhBd8b8S5GHLiIu8N6BbyqHFohFAQoaJJPG8i5yQiSwjYmEqXSfVeoCmpiyBJICEkQK98JOB21kkJoBS8VdhYa-Ty93lBnznQyesJBtKcb32foGut2Hgt10hEMWJbE3dDgA?stp=s256x192&ccb=10-5&oh=00_AT9BZ1Rpc9zbY_uNu92A_4gj1joiy1b6VtgtLIu_7wh9Bg&oe=6278C456&_nc_sid=122ab1"
|
|
|
|
* Mapillary.sameUrl(a, b) => true
|
|
|
|
*/
|
|
|
|
static sameUrl(a: string, b: string): boolean {
|
|
|
|
if (a === b) {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
try {
|
|
|
|
const aUrl = new URL(a)
|
|
|
|
const bUrl = new URL(b)
|
|
|
|
if (aUrl.host !== bUrl.host || aUrl.pathname !== bUrl.pathname) {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
let allSame = true
|
|
|
|
aUrl.searchParams.forEach((value, key) => {
|
|
|
|
if (key === "stp") {
|
|
|
|
// This is the key indicating the image size on mapillary; we ignore it
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if (value !== bUrl.searchParams.get(key)) {
|
|
|
|
allSame = false
|
|
|
|
return
|
|
|
|
}
|
|
|
|
})
|
|
|
|
return allSame
|
|
|
|
} catch (e) {
|
2022-06-03 01:33:41 +02:00
|
|
|
console.debug("Could not compare ", a, "and", b, "due to", e)
|
2022-05-06 12:41:24 +02:00
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2023-12-07 01:04:43 +01:00
|
|
|
static createLink(
|
|
|
|
location: {
|
|
|
|
lon: number
|
|
|
|
lat: number
|
|
|
|
} = undefined,
|
|
|
|
zoom: number = 17,
|
|
|
|
pKey?: string
|
|
|
|
) {
|
2023-12-02 03:12:34 +01:00
|
|
|
const params = {
|
|
|
|
focus: pKey === undefined ? "map" : "photo",
|
2023-12-05 18:35:18 +01:00
|
|
|
lat: location?.lat,
|
|
|
|
lng: location?.lon,
|
2023-12-02 03:12:34 +01:00
|
|
|
z: location === undefined ? undefined : Math.max((zoom ?? 2) - 1, 1),
|
|
|
|
pKey,
|
|
|
|
}
|
|
|
|
const baselink = `https://www.mapillary.com/app/?`
|
2023-12-07 01:04:43 +01:00
|
|
|
const paramsStr = Utils.NoNull(
|
|
|
|
Object.keys(params).map((k) =>
|
|
|
|
params[k] === undefined ? undefined : k + "=" + params[k]
|
|
|
|
)
|
|
|
|
)
|
2023-12-02 03:12:34 +01:00
|
|
|
return baselink + paramsStr.join("&")
|
|
|
|
}
|
|
|
|
|
2021-11-11 17:35:24 +01:00
|
|
|
/**
|
|
|
|
* Returns the correct key for API v4.0
|
|
|
|
*/
|
|
|
|
private static ExtractKeyFromURL(value: string): number {
|
|
|
|
let key: string
|
2021-11-07 16:34:51 +01:00
|
|
|
|
2024-01-02 18:06:25 +01:00
|
|
|
if (value.startsWith("http")) {
|
|
|
|
try {
|
|
|
|
const url = new URL(value.toLowerCase())
|
|
|
|
if (url.searchParams.has("pkey")) {
|
|
|
|
const pkey = Number(url.searchParams.get("pkey"))
|
|
|
|
if (!isNaN(pkey)) {
|
|
|
|
return pkey
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} catch (e) {
|
|
|
|
console.log("Could not parse value for mapillary:", value)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (value.startsWith(Mapillary.valuePrefix)) {
|
2021-11-11 17:35:24 +01:00
|
|
|
key = value.substring(0, value.lastIndexOf("?")).substring(value.lastIndexOf("/") + 1)
|
2022-05-06 12:41:24 +02:00
|
|
|
} else if (value.match("[0-9]*")) {
|
|
|
|
key = value
|
2021-06-18 01:25:13 +02:00
|
|
|
}
|
2021-09-26 17:36:39 +02:00
|
|
|
|
2021-11-11 17:35:24 +01:00
|
|
|
const keyAsNumber = Number(key)
|
|
|
|
if (!isNaN(keyAsNumber)) {
|
|
|
|
return keyAsNumber
|
2021-09-15 01:33:52 +02:00
|
|
|
}
|
2021-11-07 16:34:51 +01:00
|
|
|
|
2021-11-11 17:35:24 +01:00
|
|
|
return undefined
|
2021-06-18 01:25:13 +02:00
|
|
|
}
|
|
|
|
|
2023-12-02 03:12:34 +01:00
|
|
|
apiUrls(): string[] {
|
|
|
|
return ["https://mapillary.com", "https://www.mapillary.com", "https://graph.mapillary.com"]
|
|
|
|
}
|
|
|
|
|
2023-12-07 01:04:43 +01:00
|
|
|
SourceIcon(
|
|
|
|
id: string,
|
|
|
|
location?: {
|
|
|
|
lon: number
|
|
|
|
lat: number
|
|
|
|
}
|
|
|
|
): BaseUIElement {
|
2023-12-14 18:25:35 +01:00
|
|
|
let url: string = undefined
|
|
|
|
if (id) {
|
|
|
|
url = Mapillary.createLink(location, 16, "" + id)
|
2023-12-02 03:12:34 +01:00
|
|
|
}
|
2023-12-14 18:25:35 +01:00
|
|
|
return new SvelteUIElement(MapillaryIcon, { url })
|
2021-06-18 01:25:13 +02:00
|
|
|
}
|
|
|
|
|
2021-09-29 23:56:59 +02:00
|
|
|
async ExtractUrls(key: string, value: string): Promise<Promise<ProvidedImage>[]> {
|
2021-10-18 22:17:41 +02:00
|
|
|
return [this.PrepareUrlAsync(key, value)]
|
2021-09-29 23:56:59 +02:00
|
|
|
}
|
|
|
|
|
2024-07-21 10:52:51 +02:00
|
|
|
public async DownloadAttribution(providedImage: { id: string }): Promise<LicenseInfo> {
|
2024-04-01 02:00:48 +02:00
|
|
|
const mapillaryId = providedImage.id
|
|
|
|
const metadataUrl =
|
|
|
|
"https://graph.mapillary.com/" +
|
|
|
|
mapillaryId +
|
|
|
|
"?fields=thumb_1024_url,thumb_original_url,captured_at,creator&access_token=" +
|
|
|
|
Constants.mapillary_client_token_v4
|
|
|
|
const response = await Utils.downloadJsonCached(metadataUrl, 60 * 60)
|
|
|
|
|
2021-11-11 17:35:24 +01:00
|
|
|
const license = new LicenseInfo()
|
2024-04-01 02:00:48 +02:00
|
|
|
license.artist = response["creator"]["username"]
|
2021-11-11 17:35:24 +01:00
|
|
|
license.license = "CC BY-SA 4.0"
|
|
|
|
// license.license = "Creative Commons Attribution-ShareAlike 4.0 International License";
|
2021-09-26 17:36:39 +02:00
|
|
|
license.attributionRequired = true
|
2024-04-01 02:00:48 +02:00
|
|
|
license.date = new Date(response["captured_at"])
|
2021-09-26 17:36:39 +02:00
|
|
|
return license
|
2020-10-17 00:37:45 +02:00
|
|
|
}
|
2021-11-07 16:34:51 +01:00
|
|
|
|
|
|
|
private async PrepareUrlAsync(key: string, value: string): Promise<ProvidedImage> {
|
2021-11-11 17:35:24 +01:00
|
|
|
const mapillaryId = Mapillary.ExtractKeyFromURL(value)
|
|
|
|
if (mapillaryId === undefined) {
|
2021-11-07 16:34:51 +01:00
|
|
|
return undefined
|
|
|
|
}
|
|
|
|
|
2021-11-11 17:35:24 +01:00
|
|
|
const metadataUrl =
|
|
|
|
"https://graph.mapillary.com/" +
|
|
|
|
mapillaryId +
|
2024-09-12 01:31:00 +02:00
|
|
|
"?fields=thumb_1024_url,thumb_original_url,captured_at,compass_angle,geometry,creator&access_token=" +
|
2021-11-11 17:35:24 +01:00
|
|
|
Constants.mapillary_client_token_v4
|
2022-06-13 00:51:53 +02:00
|
|
|
const response = await Utils.downloadJsonCached(metadataUrl, 60 * 60)
|
2021-11-11 17:35:24 +01:00
|
|
|
const url = <string>response["thumb_1024_url"]
|
2023-12-07 01:04:43 +01:00
|
|
|
const url_hd = <string>response["thumb_original_url"]
|
2024-04-01 02:00:48 +02:00
|
|
|
const date = new Date()
|
2024-09-12 01:31:00 +02:00
|
|
|
const rotation = (720 - Number(response["compass_angle"])) % 360
|
|
|
|
const geometry = response["geometry"]
|
2024-04-01 02:00:48 +02:00
|
|
|
date.setTime(response["captured_at"])
|
2024-04-13 02:40:21 +02:00
|
|
|
return <ProvidedImage>{
|
2023-12-02 03:12:34 +01:00
|
|
|
id: "" + mapillaryId,
|
2023-12-07 01:04:43 +01:00
|
|
|
url,
|
|
|
|
url_hd,
|
2021-11-11 17:35:24 +01:00
|
|
|
provider: this,
|
2024-04-01 02:00:48 +02:00
|
|
|
date,
|
2023-12-07 01:04:43 +01:00
|
|
|
key,
|
2024-09-12 01:31:00 +02:00
|
|
|
rotation,
|
|
|
|
lat: geometry.coordinates[1],
|
|
|
|
lon: geometry.coordinates[0]
|
2021-11-07 16:34:51 +01:00
|
|
|
}
|
|
|
|
}
|
2020-10-17 00:37:45 +02:00
|
|
|
}
|