diff --git a/Logic/ImageProviders/AllImageProviders.ts b/Logic/ImageProviders/AllImageProviders.ts index c1fe3bb45d..18589f635a 100644 --- a/Logic/ImageProviders/AllImageProviders.ts +++ b/Logic/ImageProviders/AllImageProviders.ts @@ -5,7 +5,6 @@ import GenericImageProvider from "./GenericImageProvider"; import {UIEventSource} from "../UIEventSource"; import ImageProvider, {ProvidedImage} from "./ImageProvider"; import {WikidataImageProvider} from "./WikidataImageProvider"; -import {Utils} from "../../Utils"; /** * A generic 'from the interwebz' image picker, without attribution @@ -17,12 +16,12 @@ export default class AllImageProviders { Mapillary.singleton, WikidataImageProvider.singleton, WikimediaImageProvider.singleton, - new GenericImageProvider(Imgur.defaultValuePrefix)] + new GenericImageProvider([].concat(...Imgur.defaultValuePrefix, WikimediaImageProvider.commonsPrefix))] private static _cache: Map> = new Map>() - public static LoadImagesFor(tags: UIEventSource, imagePrefix: string, loadSpecialSource: boolean): UIEventSource { + public static LoadImagesFor(tags: UIEventSource, imagePrefix?: string): UIEventSource { const id = tags.data.id if (id === undefined) { return undefined; @@ -33,11 +32,14 @@ export default class AllImageProviders { return cached } + const source = new UIEventSource([]) this._cache.set(id, source) const allSources = [] for (const imageProvider of AllImageProviders.ImageAttributionSource) { - const singleSource = imageProvider.GetRelevantUrls(tags) + const singleSource = imageProvider.GetRelevantUrls(tags, { + prefixes: imagePrefix !== undefined ? [imagePrefix] : undefined + }) allSources.push(singleSource) singleSource.addCallbackAndRunD(_ => { const all : ProvidedImage[] = [].concat(...allSources.map(source => source.data)) diff --git a/Logic/ImageProviders/WikimediaImageProvider.ts b/Logic/ImageProviders/WikimediaImageProvider.ts index cf4c1837b9..b8940a5209 100644 --- a/Logic/ImageProviders/WikimediaImageProvider.ts +++ b/Logic/ImageProviders/WikimediaImageProvider.ts @@ -11,12 +11,15 @@ import {LicenseInfo} from "./LicenseInfo"; export class WikimediaImageProvider extends ImageProvider { - public readonly defaultKeyPrefixes = ["wikimedia_commons"] + private readonly commons_key = "wikimedia_commons" + public readonly defaultKeyPrefixes = [this.commons_key,"image"] public static readonly singleton = new WikimediaImageProvider(); + public static readonly commonsPrefix = "https://commons.wikimedia.org/wiki/" private constructor() { super(); } + /** * Recursively walks a wikimedia commons category in order to search for (image) files * Returns (a promise of) a list of URLS @@ -124,38 +127,42 @@ export class WikimediaImageProvider extends ImageProvider { } - private async UrlForImage(image: string): Promise{ - if(!image.startsWith("File:")){ - image = "File:"+image + private async UrlForImage(image: string): Promise { + if (!image.startsWith("File:")) { + image = "File:" + image } return {url: this.PrepareUrl(image), key: undefined, provider: this} } - + public async ExtractUrls(key: string, value: string): Promise[]> { - const commonsPrefix = "https://commons.wikimedia.org/wiki/" - if(value.startsWith(commonsPrefix)){ - value = value.substring(commonsPrefix.length) - } else if(value.startsWith("https://upload.wikimedia.org")){ - const result : ProvidedImage = { + + if(key !== this.commons_key && !value.startsWith(WikimediaImageProvider.commonsPrefix)){ + return [] + } + + if (value.startsWith(WikimediaImageProvider.commonsPrefix)) { + value = value.substring(WikimediaImageProvider.commonsPrefix.length) + } else if (value.startsWith("https://upload.wikimedia.org")) { + const result: ProvidedImage = { key: undefined, url: value, provider: this } return [Promise.resolve(result)] } - if(value.startsWith("Category:")){ + if (value.startsWith("Category:")) { const urls = await WikimediaImageProvider.GetImagesInCategory(value) return urls.map(image => this.UrlForImage(image)) } - if(value.startsWith("File:")){ + if (value.startsWith("File:")) { return [this.UrlForImage(value)] } - if(value.startsWith("http")){ + if (value.startsWith("http")) { // PRobably an error return [] } // We do a last effort and assume this is a file - return [this.UrlForImage("File:"+value)] + return [this.UrlForImage("File:" + value)] } diff --git a/Models/ThemeConfig/FilterConfig.ts b/Models/ThemeConfig/FilterConfig.ts index 2dd9f69d69..7a032c6de3 100644 --- a/Models/ThemeConfig/FilterConfig.ts +++ b/Models/ThemeConfig/FilterConfig.ts @@ -42,5 +42,9 @@ export default class FilterConfig { return {question: question, osmTags: osmTags}; }); + + if(this.options.length > 1 && this.options[0].osmTags["and"]?.length !== 0){ + throw "Error in "+context+"."+this.id+": the first option of a multi-filter should always be the 'reset' option and not have any filters" + } } } \ No newline at end of file diff --git a/UI/BigComponents/SimpleAddUI.ts b/UI/BigComponents/SimpleAddUI.ts index 5e8a7b46d7..ad6a73ec52 100644 --- a/UI/BigComponents/SimpleAddUI.ts +++ b/UI/BigComponents/SimpleAddUI.ts @@ -19,7 +19,6 @@ import CreateNewNodeAction from "../../Logic/Osm/Actions/CreateNewNodeAction"; import {OsmObject, OsmWay} from "../../Logic/Osm/OsmObject"; import PresetConfig from "../../Models/ThemeConfig/PresetConfig"; import FilteredLayer from "../../Models/FilteredLayer"; -import {And} from "../../Logic/Tags/And"; import {BBox} from "../../Logic/BBox"; /* @@ -230,7 +229,25 @@ export default class SimpleAddUI extends Toggle { const disableFiltersOrConfirm = new Toggle( openLayerOrConfirm, disableFilter, - preset.layerToAddTo.appliedFilters.map(filters => filters === undefined || filters.length === 0) + preset.layerToAddTo.appliedFilters.map(filters => { + if(filters === undefined || filters.length === 0){ + return true; + } + for (const filter of filters) { + if(filter.selected === 0 && filter.filter.options.length === 1){ + return false; + } + if(filter.selected !== undefined){ + const tags = filter.filter.options[filter.selected].osmTags + if(tags !== undefined && tags["and"]?.length !== 0){ + // This actually doesn't filter anything at all + return false; + } + } + } + return true + + }) ) diff --git a/UI/SpecialVisualizations.ts b/UI/SpecialVisualizations.ts index 4aba67e7d6..5801e1d886 100644 --- a/UI/SpecialVisualizations.ts +++ b/UI/SpecialVisualizations.ts @@ -66,16 +66,10 @@ export default class SpecialVisualizations { name: "image key/prefix", defaultValue: "image", doc: "The keys given to the images, e.g. if image is given, the first picture URL will be added as image, the second as image:0, the third as image:1, etc... " - }, - { - name: "smart search", - defaultValue: "true", - doc: "Also include images given via 'Wikidata', 'wikimedia_commons' and 'mapillary" - }], + }], constr: (state: State, tags, args) => { const imagePrefix = args[0]; - const loadSpecial = args[1].toLowerCase() === "true"; - return new ImageCarousel(AllImageProviders.LoadImagesFor(tags, imagePrefix, loadSpecial), tags); + return new ImageCarousel(AllImageProviders.LoadImagesFor(tags, imagePrefix), tags); } }, { diff --git a/assets/layers/public_bookcase/public_bookcase.json b/assets/layers/public_bookcase/public_bookcase.json index cc21f23f8c..64b6230c16 100644 --- a/assets/layers/public_bookcase/public_bookcase.json +++ b/assets/layers/public_bookcase/public_bookcase.json @@ -465,9 +465,9 @@ "id": "inside", "options": [ { - "question": "Binnen of buiten", - "osmTags": { - "and": [] + "question": { + "nl": "Binnen of buiten", + "en": "Indoor or outdoor" } }, {