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