forked from MapComplete/MapComplete
		
	Also pickup wikimedia categories in the image tags, fix #433
This commit is contained in:
		
							parent
							
								
									8cbb693c98
								
							
						
					
					
						commit
						32cbd6e2c1
					
				
					 6 changed files with 55 additions and 31 deletions
				
			
		|  | @ -5,7 +5,6 @@ import GenericImageProvider from "./GenericImageProvider"; | ||||||
| import {UIEventSource} from "../UIEventSource"; | import {UIEventSource} from "../UIEventSource"; | ||||||
| import ImageProvider, {ProvidedImage} from "./ImageProvider"; | import ImageProvider, {ProvidedImage} from "./ImageProvider"; | ||||||
| import {WikidataImageProvider} from "./WikidataImageProvider"; | import {WikidataImageProvider} from "./WikidataImageProvider"; | ||||||
| import {Utils} from "../../Utils"; |  | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * A generic 'from the interwebz' image picker, without attribution |  * A generic 'from the interwebz' image picker, without attribution | ||||||
|  | @ -17,12 +16,12 @@ export default class AllImageProviders { | ||||||
|         Mapillary.singleton, |         Mapillary.singleton, | ||||||
|         WikidataImageProvider.singleton, |         WikidataImageProvider.singleton, | ||||||
|         WikimediaImageProvider.singleton, |         WikimediaImageProvider.singleton, | ||||||
|         new GenericImageProvider(Imgur.defaultValuePrefix)] |         new GenericImageProvider([].concat(...Imgur.defaultValuePrefix, WikimediaImageProvider.commonsPrefix))] | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|     private static _cache: Map<string, UIEventSource<ProvidedImage[]>> = new Map<string, UIEventSource<ProvidedImage[]>>() |     private static _cache: Map<string, UIEventSource<ProvidedImage[]>> = new Map<string, UIEventSource<ProvidedImage[]>>() | ||||||
| 
 | 
 | ||||||
|     public static LoadImagesFor(tags: UIEventSource<any>, imagePrefix: string, loadSpecialSource: boolean): UIEventSource<ProvidedImage[]> { |     public static LoadImagesFor(tags: UIEventSource<any>, imagePrefix?: string): UIEventSource<ProvidedImage[]> { | ||||||
|         const id = tags.data.id |         const id = tags.data.id | ||||||
|         if (id === undefined) { |         if (id === undefined) { | ||||||
|             return undefined; |             return undefined; | ||||||
|  | @ -33,11 +32,14 @@ export default class AllImageProviders { | ||||||
|             return cached |             return cached | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
|         const source = new UIEventSource([]) |         const source = new UIEventSource([]) | ||||||
|         this._cache.set(id, source) |         this._cache.set(id, source) | ||||||
|         const allSources = [] |         const allSources = [] | ||||||
|         for (const imageProvider of AllImageProviders.ImageAttributionSource) { |         for (const imageProvider of AllImageProviders.ImageAttributionSource) { | ||||||
|             const singleSource = imageProvider.GetRelevantUrls(tags) |             const singleSource = imageProvider.GetRelevantUrls(tags, { | ||||||
|  |                 prefixes: imagePrefix !== undefined ? [imagePrefix] : undefined | ||||||
|  |             }) | ||||||
|             allSources.push(singleSource) |             allSources.push(singleSource) | ||||||
|             singleSource.addCallbackAndRunD(_ => { |             singleSource.addCallbackAndRunD(_ => { | ||||||
|                 const all : ProvidedImage[] = [].concat(...allSources.map(source => source.data)) |                 const all : ProvidedImage[] = [].concat(...allSources.map(source => source.data)) | ||||||
|  |  | ||||||
|  | @ -11,12 +11,15 @@ import {LicenseInfo} from "./LicenseInfo"; | ||||||
| export class WikimediaImageProvider extends ImageProvider { | 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 singleton = new WikimediaImageProvider(); | ||||||
|  |     public static readonly commonsPrefix = "https://commons.wikimedia.org/wiki/" | ||||||
| 
 | 
 | ||||||
|     private constructor() { |     private constructor() { | ||||||
|         super(); |         super(); | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|     /** |     /** | ||||||
|      * Recursively walks a wikimedia commons category in order to search for (image) files |      * Recursively walks a wikimedia commons category in order to search for (image) files | ||||||
|      * Returns (a promise of) a list of URLS |      * Returns (a promise of) a list of URLS | ||||||
|  | @ -124,38 +127,42 @@ export class WikimediaImageProvider extends ImageProvider { | ||||||
| 
 | 
 | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     private async UrlForImage(image: string): Promise<ProvidedImage>{ |     private async UrlForImage(image: string): Promise<ProvidedImage> { | ||||||
|         if(!image.startsWith("File:")){ |         if (!image.startsWith("File:")) { | ||||||
|             image = "File:"+image |             image = "File:" + image | ||||||
|         } |         } | ||||||
|         return {url: this.PrepareUrl(image), key: undefined, provider: this} |         return {url: this.PrepareUrl(image), key: undefined, provider: this} | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     public async ExtractUrls(key: string, value: string): Promise<Promise<ProvidedImage>[]> { |     public async ExtractUrls(key: string, value: string): Promise<Promise<ProvidedImage>[]> { | ||||||
|         const commonsPrefix = "https://commons.wikimedia.org/wiki/" |          | ||||||
|         if(value.startsWith(commonsPrefix)){ |         if(key !== this.commons_key && !value.startsWith(WikimediaImageProvider.commonsPrefix)){ | ||||||
|             value = value.substring(commonsPrefix.length) |             return [] | ||||||
|         } else       if(value.startsWith("https://upload.wikimedia.org")){ |         } | ||||||
|             const result : ProvidedImage = { |          | ||||||
|  |         if (value.startsWith(WikimediaImageProvider.commonsPrefix)) { | ||||||
|  |             value = value.substring(WikimediaImageProvider.commonsPrefix.length) | ||||||
|  |         } else if (value.startsWith("https://upload.wikimedia.org")) { | ||||||
|  |             const result: ProvidedImage = { | ||||||
|                 key: undefined, |                 key: undefined, | ||||||
|                 url: value, |                 url: value, | ||||||
|                 provider: this |                 provider: this | ||||||
|             } |             } | ||||||
|             return [Promise.resolve(result)] |             return [Promise.resolve(result)] | ||||||
|         } |         } | ||||||
|         if(value.startsWith("Category:")){ |         if (value.startsWith("Category:")) { | ||||||
|             const urls = await WikimediaImageProvider.GetImagesInCategory(value) |             const urls = await WikimediaImageProvider.GetImagesInCategory(value) | ||||||
|             return urls.map(image => this.UrlForImage(image)) |             return urls.map(image => this.UrlForImage(image)) | ||||||
|         } |         } | ||||||
|         if(value.startsWith("File:")){ |         if (value.startsWith("File:")) { | ||||||
|             return [this.UrlForImage(value)] |             return [this.UrlForImage(value)] | ||||||
|         } |         } | ||||||
|         if(value.startsWith("http")){ |         if (value.startsWith("http")) { | ||||||
|             // PRobably an error
 |             // PRobably an error
 | ||||||
|             return [] |             return [] | ||||||
|         } |         } | ||||||
|         // We do a last effort and assume this is a file
 |         // We do a last effort and assume this is a file
 | ||||||
|         return [this.UrlForImage("File:"+value)] |         return [this.UrlForImage("File:" + value)] | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -42,5 +42,9 @@ export default class FilterConfig { | ||||||
| 
 | 
 | ||||||
|             return {question: question, osmTags: osmTags}; |             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" | ||||||
|  |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | @ -19,7 +19,6 @@ import CreateNewNodeAction from "../../Logic/Osm/Actions/CreateNewNodeAction"; | ||||||
| import {OsmObject, OsmWay} from "../../Logic/Osm/OsmObject"; | import {OsmObject, OsmWay} from "../../Logic/Osm/OsmObject"; | ||||||
| import PresetConfig from "../../Models/ThemeConfig/PresetConfig"; | import PresetConfig from "../../Models/ThemeConfig/PresetConfig"; | ||||||
| import FilteredLayer from "../../Models/FilteredLayer"; | import FilteredLayer from "../../Models/FilteredLayer"; | ||||||
| import {And} from "../../Logic/Tags/And"; |  | ||||||
| import {BBox} from "../../Logic/BBox"; | import {BBox} from "../../Logic/BBox"; | ||||||
| 
 | 
 | ||||||
| /* | /* | ||||||
|  | @ -230,7 +229,25 @@ export default class SimpleAddUI extends Toggle { | ||||||
|         const disableFiltersOrConfirm = new Toggle( |         const disableFiltersOrConfirm = new Toggle( | ||||||
|             openLayerOrConfirm, |             openLayerOrConfirm, | ||||||
|             disableFilter, |             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 | ||||||
|  |                  | ||||||
|  |             }) | ||||||
|         ) |         ) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -66,16 +66,10 @@ export default class SpecialVisualizations { | ||||||
|                     name: "image key/prefix", |                     name: "image key/prefix", | ||||||
|                     defaultValue: "image", |                     defaultValue: "image", | ||||||
|                     doc: "The keys given to the images, e.g. if <span class='literal-code'>image</span> is given, the first picture URL will be added as <span class='literal-code'>image</span>, the second as <span class='literal-code'>image:0</span>, the third as <span class='literal-code'>image:1</span>, etc... " |                     doc: "The keys given to the images, e.g. if <span class='literal-code'>image</span> is given, the first picture URL will be added as <span class='literal-code'>image</span>, the second as <span class='literal-code'>image:0</span>, the third as <span class='literal-code'>image:1</span>, etc... " | ||||||
|                 }, |  | ||||||
|                     { |  | ||||||
|                         name: "smart search", |  | ||||||
|                         defaultValue: "true", |  | ||||||
|                         doc: "Also include images given via 'Wikidata', 'wikimedia_commons' and 'mapillary" |  | ||||||
|                 }], |                 }], | ||||||
|                 constr: (state: State, tags, args) => { |                 constr: (state: State, tags, args) => { | ||||||
|                     const imagePrefix = args[0]; |                     const imagePrefix = args[0]; | ||||||
|                     const loadSpecial = args[1].toLowerCase() === "true"; |                     return new ImageCarousel(AllImageProviders.LoadImagesFor(tags, imagePrefix), tags); | ||||||
|                     return new ImageCarousel(AllImageProviders.LoadImagesFor(tags, imagePrefix, loadSpecial), tags); |  | ||||||
|                 } |                 } | ||||||
|             }, |             }, | ||||||
|             { |             { | ||||||
|  |  | ||||||
|  | @ -465,9 +465,9 @@ | ||||||
|             "id": "inside", |             "id": "inside", | ||||||
|             "options": [ |             "options": [ | ||||||
|                 { |                 { | ||||||
|                     "question": "Binnen of buiten", |                     "question": { | ||||||
|                     "osmTags": { |                         "nl": "Binnen of buiten", | ||||||
|                         "and": [] |                         "en": "Indoor or outdoor" | ||||||
|                     } |                     } | ||||||
|                 }, |                 }, | ||||||
|                 { |                 { | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue