forked from MapComplete/MapComplete
Refactoring: make needed URLs explicit
This commit is contained in:
parent
7852829f1b
commit
4852888b41
51 changed files with 978 additions and 871 deletions
|
@ -23,27 +23,27 @@ export default class AllImageProviders {
|
|||
)
|
||||
),
|
||||
]
|
||||
|
||||
public static apiUrls: string[] = [].concat(
|
||||
...AllImageProviders.ImageAttributionSource.map((src) => src.apiUrls())
|
||||
)
|
||||
public static defaultKeys = [].concat(
|
||||
AllImageProviders.ImageAttributionSource.map((provider) => provider.defaultKeyPrefixes)
|
||||
)
|
||||
private static providersByName = {
|
||||
imgur: Imgur.singleton,
|
||||
mapillary: Mapillary.singleton,
|
||||
wikidata: WikidataImageProvider.singleton,
|
||||
wikimedia: WikimediaImageProvider.singleton,
|
||||
}
|
||||
|
||||
public static byName(name: string) {
|
||||
return AllImageProviders.providersByName[name.toLowerCase()]
|
||||
}
|
||||
|
||||
public static defaultKeys = [].concat(
|
||||
AllImageProviders.ImageAttributionSource.map((provider) => provider.defaultKeyPrefixes)
|
||||
)
|
||||
|
||||
private static _cache: Map<string, UIEventSource<ProvidedImage[]>> = new Map<
|
||||
string,
|
||||
UIEventSource<ProvidedImage[]>
|
||||
>()
|
||||
|
||||
public static byName(name: string) {
|
||||
return AllImageProviders.providersByName[name.toLowerCase()]
|
||||
}
|
||||
|
||||
public static LoadImagesFor(
|
||||
tags: Store<Record<string, string>>,
|
||||
tagKey?: string[]
|
||||
|
|
|
@ -3,6 +3,10 @@ import ImageProvider, { ProvidedImage } from "./ImageProvider"
|
|||
export default class GenericImageProvider extends ImageProvider {
|
||||
public defaultKeyPrefixes: string[] = ["image"]
|
||||
|
||||
public apiUrls(): string[] {
|
||||
return []
|
||||
}
|
||||
|
||||
private readonly _valuePrefixBlacklist: string[]
|
||||
|
||||
public constructor(valuePrefixBlacklist: string[]) {
|
||||
|
|
|
@ -65,4 +65,6 @@ export default abstract class ImageProvider {
|
|||
public abstract ExtractUrls(key: string, value: string): Promise<Promise<ProvidedImage>[]>
|
||||
|
||||
public abstract DownloadAttribution(url: string): Promise<LicenseInfo>
|
||||
|
||||
public abstract apiUrls(): string[]
|
||||
}
|
||||
|
|
|
@ -4,16 +4,23 @@ import { Utils } from "../../Utils";
|
|||
import Constants from "../../Models/Constants";
|
||||
import { LicenseInfo } from "./LicenseInfo";
|
||||
import { ImageUploader } from "./ImageUploader";
|
||||
import Img from "../../UI/Base/Img";
|
||||
|
||||
export class Imgur extends ImageProvider implements ImageUploader{
|
||||
export class Imgur extends ImageProvider implements ImageUploader {
|
||||
public static readonly defaultValuePrefix = ["https://i.imgur.com"]
|
||||
public static readonly singleton = new Imgur()
|
||||
public readonly defaultKeyPrefixes: string[] = ["image"]
|
||||
public readonly maxFileSizeInMegabytes = 10
|
||||
public readonly maxFileSizeInMegabytes = 10
|
||||
public static readonly apiUrl = "https://api.imgur.com/3/image"
|
||||
|
||||
private constructor() {
|
||||
super()
|
||||
}
|
||||
|
||||
apiUrls(): string[] {
|
||||
return [Imgur.apiUrl]
|
||||
}
|
||||
|
||||
/**
|
||||
* Uploads an image, returns the URL where to find the image
|
||||
* @param title
|
||||
|
@ -24,8 +31,8 @@ export class Imgur extends ImageProvider implements ImageUploader{
|
|||
title: string,
|
||||
description: string,
|
||||
blob: File
|
||||
): Promise<{ key: string, value: string }> {
|
||||
const apiUrl = "https://api.imgur.com/3/image"
|
||||
): Promise<{ key: string; value: string }> {
|
||||
const apiUrl = Imgur.apiUrl
|
||||
const apiKey = Constants.ImgurApiKey
|
||||
|
||||
const formData = new FormData()
|
||||
|
@ -33,7 +40,6 @@ export class Imgur extends ImageProvider implements ImageUploader{
|
|||
formData.append("title", title)
|
||||
formData.append("description", description)
|
||||
|
||||
|
||||
const settings: RequestInit = {
|
||||
method: "POST",
|
||||
body: formData,
|
||||
|
|
|
@ -17,6 +17,10 @@ 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
|
||||
|
|
|
@ -5,6 +5,9 @@ import { WikimediaImageProvider } from "./WikimediaImageProvider"
|
|||
import Wikidata from "../Web/Wikidata"
|
||||
|
||||
export class WikidataImageProvider extends ImageProvider {
|
||||
public apiUrls(): string[] {
|
||||
return Wikidata.neededUrls
|
||||
}
|
||||
public static readonly singleton = new WikidataImageProvider()
|
||||
public readonly defaultKeyPrefixes = ["wikidata"]
|
||||
|
||||
|
|
|
@ -11,11 +11,11 @@ import Wikimedia from "../Web/Wikimedia"
|
|||
*/
|
||||
export class WikimediaImageProvider extends ImageProvider {
|
||||
public static readonly singleton = new WikimediaImageProvider()
|
||||
public static readonly commonsPrefixes = [
|
||||
public static readonly apiUrls = [
|
||||
"https://commons.wikimedia.org/wiki/",
|
||||
"https://upload.wikimedia.org",
|
||||
"File:",
|
||||
]
|
||||
public static readonly commonsPrefixes = [...WikimediaImageProvider.apiUrls, "File:"]
|
||||
private readonly commons_key = "wikimedia_commons"
|
||||
public readonly defaultKeyPrefixes = [this.commons_key, "image"]
|
||||
|
||||
|
@ -66,6 +66,10 @@ export class WikimediaImageProvider extends ImageProvider {
|
|||
return value
|
||||
}
|
||||
|
||||
apiUrls(): string[] {
|
||||
return WikimediaImageProvider.apiUrls
|
||||
}
|
||||
|
||||
SourceIcon(backlink: string): BaseUIElement {
|
||||
const img = Svg.wikimedia_commons_white_svg().SetStyle("width:2em;height: 2em")
|
||||
if (backlink === undefined) {
|
||||
|
|
|
@ -32,11 +32,12 @@ export default class Maproulette {
|
|||
private readonly apiKey: string
|
||||
|
||||
public static singleton = new Maproulette()
|
||||
public static readonly defaultEndpoint = "https://maproulette.org/api/v2"
|
||||
/**
|
||||
* Creates a new Maproulette instance
|
||||
* @param endpoint The API endpoint to use
|
||||
*/
|
||||
constructor(endpoint: string = "https://maproulette.org/api/v2") {
|
||||
constructor(endpoint: string = Maproulette.defaultEndpoint) {
|
||||
this.endpoint = endpoint
|
||||
this.apiKey = Constants.MaprouletteApiKey
|
||||
}
|
||||
|
|
6
src/Logic/Osm/AuthConfig.ts
Normal file
6
src/Logic/Osm/AuthConfig.ts
Normal file
|
@ -0,0 +1,6 @@
|
|||
export interface AuthConfig {
|
||||
"#"?: string // optional comment
|
||||
oauth_client_id: string
|
||||
oauth_secret: string
|
||||
url: string
|
||||
}
|
File diff suppressed because it is too large
Load diff
|
@ -28,14 +28,8 @@ class FeatureSwitchUtils {
|
|||
|
||||
export class OsmConnectionFeatureSwitches {
|
||||
public readonly featureSwitchFakeUser: UIEventSource<boolean>
|
||||
public readonly featureSwitchApiURL: UIEventSource<string>
|
||||
|
||||
constructor() {
|
||||
this.featureSwitchApiURL = QueryParameters.GetQueryParameter(
|
||||
"backend",
|
||||
"osm",
|
||||
"The OSM backend to use - can be used to redirect mapcomplete to the testing backend when using 'osm-test'"
|
||||
)
|
||||
|
||||
this.featureSwitchFakeUser = QueryParameters.GetBooleanQueryParameter(
|
||||
"fake-user",
|
||||
|
@ -143,7 +137,6 @@ export default class FeatureSwitchState extends OsmConnectionFeatureSwitches {
|
|||
|
||||
let testingDefaultValue = false
|
||||
if (
|
||||
this.featureSwitchApiURL.data !== "osm-test" &&
|
||||
!Utils.runningFromConsole &&
|
||||
(location.hostname === "localhost" || location.hostname === "127.0.0.1")
|
||||
) {
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import { IndexedFeatureSource } from "../FeatureSource/FeatureSource"
|
||||
import { GeoOperations } from "../GeoOperations"
|
||||
import { ImmutableStore, Store, Stores, UIEventSource } from "../UIEventSource"
|
||||
import { Mapillary } from "../ImageProviders/Mapillary"
|
||||
import P4C from "pic4carto"
|
||||
import { Utils } from "../../Utils"
|
||||
|
||||
export interface NearbyImageOptions {
|
||||
lon: number
|
||||
lat: number
|
||||
|
@ -35,17 +35,12 @@ export interface P4CPicture {
|
|||
}
|
||||
|
||||
/**
|
||||
* Uses Pic4wCarto to fetch nearby images from various providers
|
||||
* Uses Pic4Carto to fetch nearby images from various providers
|
||||
*/
|
||||
export default class NearbyImagesSearch {
|
||||
private static readonly services = [
|
||||
"mapillary",
|
||||
"flickr",
|
||||
"openstreetcam",
|
||||
"wikicommons",
|
||||
] as const
|
||||
|
||||
private individualStores
|
||||
public static readonly services = ["mapillary", "flickr", "kartaview", "wikicommons"] as const
|
||||
public static readonly apiUrls = ["https://api.flickr.com"]
|
||||
private readonly individualStores: Store<{ images: P4CPicture[]; beforeFilter: number }>[]
|
||||
private readonly _store: UIEventSource<P4CPicture[]> = new UIEventSource<P4CPicture[]>([])
|
||||
public readonly store: Store<P4CPicture[]> = this._store
|
||||
private readonly _options: NearbyImageOptions
|
||||
|
@ -71,16 +66,16 @@ export default class NearbyImagesSearch {
|
|||
this.update()
|
||||
}
|
||||
|
||||
private static buildPictureFetcher(
|
||||
private static async fetchImages(
|
||||
options: NearbyImageOptions,
|
||||
fetcher: "mapillary" | "flickr" | "openstreetcam" | "wikicommons"
|
||||
): Store<{ images: P4CPicture[]; beforeFilter: number }> {
|
||||
fetcher: P4CService
|
||||
): Promise<P4CPicture[]> {
|
||||
const picManager = new P4C.PicturesManager({ usefetchers: [fetcher] })
|
||||
const searchRadius = options.searchRadius ?? 100
|
||||
const maxAgeSeconds = (options.maxDaysOld ?? 3 * 365) * 24 * 60 * 60 * 1000
|
||||
const searchRadius = options.searchRadius ?? 100
|
||||
|
||||
const p4cStore = Stores.FromPromise<P4CPicture[]>(
|
||||
picManager.startPicsRetrievalAround(
|
||||
try {
|
||||
const pics: P4CPicture[] = await picManager.startPicsRetrievalAround(
|
||||
new P4C.LatLng(options.lat, options.lon),
|
||||
searchRadius,
|
||||
{
|
||||
|
@ -88,7 +83,21 @@ export default class NearbyImagesSearch {
|
|||
towardscenter: false,
|
||||
}
|
||||
)
|
||||
return pics
|
||||
} catch (e) {
|
||||
console.error("Could not fetch images from service", fetcher, e)
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
||||
private static buildPictureFetcher(
|
||||
options: NearbyImageOptions,
|
||||
fetcher: P4CService
|
||||
): Store<{ images: P4CPicture[]; beforeFilter: number }> {
|
||||
const p4cStore = Stores.FromPromise<P4CPicture[]>(
|
||||
NearbyImagesSearch.fetchImages(options, fetcher)
|
||||
)
|
||||
const searchRadius = options.searchRadius ?? 100
|
||||
return p4cStore.map(
|
||||
(images) => {
|
||||
if (images === undefined) {
|
||||
|
@ -220,3 +229,5 @@ class ImagesInLoadedDataFetcher {
|
|||
return foundImages
|
||||
}
|
||||
}
|
||||
|
||||
type P4CService = (typeof NearbyImagesSearch.services)[number]
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { Utils } from "../../Utils"
|
||||
|
||||
export default class PlantNet {
|
||||
private static baseUrl =
|
||||
public static baseUrl =
|
||||
"https://my-api.plantnet.org/v2/identify/all?api-key=2b10AAsjzwzJvucA5Ncm5qxe"
|
||||
|
||||
public static query(imageUrls: string[]): Promise<PlantNetResult> {
|
||||
|
|
|
@ -123,6 +123,11 @@ export interface WikidataAdvancedSearchoptions extends WikidataSearchoptions {
|
|||
* Utility functions around wikidata
|
||||
*/
|
||||
export default class Wikidata {
|
||||
public static readonly neededUrls = [
|
||||
"https://www.wikidata.org/",
|
||||
"https://wikidata.org/",
|
||||
"https://query.wikidata.org",
|
||||
]
|
||||
private static readonly _identifierPrefixes = ["Q", "L"].map((str) => str.toLowerCase())
|
||||
private static readonly _prefixesToRemove = [
|
||||
"https://www.wikidata.org/wiki/Lexeme:",
|
||||
|
@ -130,11 +135,11 @@ export default class Wikidata {
|
|||
"http://www.wikidata.org/entity/",
|
||||
"Lexeme:",
|
||||
].map((str) => str.toLowerCase())
|
||||
|
||||
private static readonly _storeCache = new Map<
|
||||
string,
|
||||
Store<{ success: WikidataResponse } | { error: any }>
|
||||
>()
|
||||
|
||||
/**
|
||||
* Same as LoadWikidataEntry, but wrapped into a UIEventSource
|
||||
* @param value
|
||||
|
@ -388,6 +393,7 @@ export default class Wikidata {
|
|||
}
|
||||
|
||||
private static _cache = new Map<string, Promise<WikidataResponse>>()
|
||||
|
||||
public static async LoadWikidataEntryAsync(value: string | number): Promise<WikidataResponse> {
|
||||
const key = "" + value
|
||||
const cached = Wikidata._cache.get(key)
|
||||
|
@ -398,6 +404,7 @@ export default class Wikidata {
|
|||
Wikidata._cache.set(key, uncached)
|
||||
return uncached
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a wikidata page
|
||||
* @returns the entity of the given value
|
||||
|
|
|
@ -34,6 +34,8 @@ export default class Wikipedia {
|
|||
|
||||
private static readonly idsToRemove = ["sjabloon_zie"]
|
||||
|
||||
public static readonly neededUrls = ["*.wikipedia.org"]
|
||||
|
||||
private static readonly _cache = new Map<string, Promise<string>>()
|
||||
private static _fullDetailsCache = new Map<string, Store<FullWikipediaDetails>>()
|
||||
public readonly backend: string
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue