forked from MapComplete/MapComplete
Merge master
This commit is contained in:
commit
470e9acc64
66 changed files with 10798 additions and 414 deletions
|
@ -9,7 +9,6 @@ import BaseUIElement from "../UI/BaseUIElement";
|
|||
import {UIEventSource} from "./UIEventSource";
|
||||
import {LocalStorageSource} from "./Web/LocalStorageSource";
|
||||
import LZString from "lz-string";
|
||||
import * as personal from "../assets/themes/personal/personal.json";
|
||||
import {FixLegacyTheme} from "../Models/ThemeConfig/Conversion/LegacyJsonConvert";
|
||||
import {LayerConfigJson} from "../Models/ThemeConfig/Json/LayerConfigJson";
|
||||
import SharedTagRenderings from "../Customizations/SharedTagRenderings";
|
||||
|
@ -52,17 +51,7 @@ export default class DetermineLayout {
|
|||
console.log("Using layout", layoutId);
|
||||
}
|
||||
layoutId = QueryParameters.GetQueryParameter("layout", layoutId, "The layout to load into MapComplete").data;
|
||||
const layoutToUse: LayoutConfig = AllKnownLayouts.allKnownLayouts.get(layoutId?.toLowerCase());
|
||||
|
||||
if (layoutToUse?.id === personal.id) {
|
||||
layoutToUse.layers = AllKnownLayouts.AllPublicLayers()
|
||||
for (const layer of layoutToUse.layers) {
|
||||
layer.minzoomVisible = Math.max(layer.minzoomVisible, layer.minzoom)
|
||||
layer.minzoom = Math.max(16, layer.minzoom)
|
||||
}
|
||||
}
|
||||
|
||||
return layoutToUse
|
||||
return AllKnownLayouts.allKnownLayouts.get(layoutId?.toLowerCase())
|
||||
}
|
||||
|
||||
public static LoadLayoutFromHash(
|
||||
|
@ -161,7 +150,8 @@ export default class DetermineLayout {
|
|||
}
|
||||
const converState = {
|
||||
tagRenderings: SharedTagRenderings.SharedTagRenderingJson,
|
||||
sharedLayers: knownLayersDict
|
||||
sharedLayers: knownLayersDict,
|
||||
publicLayers: new Set<string>()
|
||||
}
|
||||
json = new FixLegacyTheme().convertStrict(json, "While loading a dynamic theme")
|
||||
const raw = json;
|
||||
|
|
|
@ -79,7 +79,7 @@ export default class GeoJsonSource implements FeatureSourceForLayer, Tiled {
|
|||
private LoadJSONFrom(url: string) {
|
||||
const eventSource = this.features;
|
||||
const self = this;
|
||||
Utils.downloadJson(url)
|
||||
Utils.downloadJsonCached(url, 60 * 60)
|
||||
.then(json => {
|
||||
self.state.setData("loaded")
|
||||
if (json.features === undefined || json.features === null) {
|
||||
|
|
|
@ -34,7 +34,7 @@ export default class GenericImageProvider extends ImageProvider {
|
|||
return undefined;
|
||||
}
|
||||
|
||||
protected DownloadAttribution(url: string) {
|
||||
public DownloadAttribution(url: string) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import {Store, Stores, UIEventSource} from "../UIEventSource";
|
||||
import {Store, UIEventSource} from "../UIEventSource";
|
||||
import BaseUIElement from "../../UI/BaseUIElement";
|
||||
import {LicenseInfo} from "./LicenseInfo";
|
||||
import {Utils} from "../../Utils";
|
||||
|
@ -12,19 +12,7 @@ export interface ProvidedImage {
|
|||
export default abstract class ImageProvider {
|
||||
|
||||
public abstract readonly defaultKeyPrefixes: string[]
|
||||
|
||||
private _cache = new Map<string, Store<LicenseInfo>>()
|
||||
|
||||
GetAttributionFor(url: string): Store<LicenseInfo> {
|
||||
const cached = this._cache.get(url);
|
||||
if (cached !== undefined) {
|
||||
return cached;
|
||||
}
|
||||
const src = Stores.FromPromise(this.DownloadAttribution(url))
|
||||
this._cache.set(url, src)
|
||||
return src;
|
||||
}
|
||||
|
||||
|
||||
public abstract SourceIcon(backlinkSource?: string): BaseUIElement;
|
||||
|
||||
/**
|
||||
|
@ -75,6 +63,6 @@ export default abstract class ImageProvider {
|
|||
|
||||
public abstract ExtractUrls(key: string, value: string): Promise<Promise<ProvidedImage>[]>;
|
||||
|
||||
protected abstract DownloadAttribution(url: string): Promise<LicenseInfo>;
|
||||
public abstract DownloadAttribution(url: string): Promise<LicenseInfo>;
|
||||
|
||||
}
|
|
@ -99,11 +99,23 @@ export class Imgur extends ImageProvider {
|
|||
return []
|
||||
}
|
||||
|
||||
protected DownloadAttribution: (url: string) => Promise<LicenseInfo> = async (url: string) => {
|
||||
/**
|
||||
* Download the attribution from attribution
|
||||
*
|
||||
* const data = {"data":{"id":"I9t6B7B","title":"Station Knokke","description":"author:Pieter Vander Vennet\r\nlicense:CC-BY 4.0\r\nosmid:node\/9812712386","datetime":1655052078,"type":"image\/jpeg","animated":false,"width":2400,"height":1795,"size":910872,"views":2,"bandwidth":1821744,"vote":null,"favorite":false,"nsfw":false,"section":null,"account_url":null,"account_id":null,"is_ad":false,"in_most_viral":false,"has_sound":false,"tags":[],"ad_type":0,"ad_url":"","edited":"0","in_gallery":false,"link":"https:\/\/i.imgur.com\/I9t6B7B.jpg","ad_config":{"safeFlags":["not_in_gallery","share"],"highRiskFlags":[],"unsafeFlags":["sixth_mod_unsafe"],"wallUnsafeFlags":[],"showsAds":false,"showAdLevel":1}},"success":true,"status":200}
|
||||
* Utils.injectJsonDownloadForTests("https://api.imgur.com/3/image/E0RuAK3", data)
|
||||
* const licenseInfo = await Imgur.singleton.DownloadAttribution("https://i.imgur.com/E0RuAK3.jpg")
|
||||
* const expected = new LicenseInfo()
|
||||
* expected.licenseShortName = "CC-BY 4.0"
|
||||
* expected.artist = "Pieter Vander Vennet"
|
||||
* licenseInfo // => expected
|
||||
*/
|
||||
public async DownloadAttribution (url: string) : Promise<LicenseInfo> {
|
||||
const hash = url.substr("https://i.imgur.com/".length).split(".jpg")[0];
|
||||
|
||||
const apiUrl = 'https://api.imgur.com/3/image/' + hash;
|
||||
const response = await Utils.downloadJson(apiUrl, {Authorization: 'Client-ID ' + Constants.ImgurApiKey})
|
||||
const response = await Utils.downloadJsonCached(apiUrl, 365*24*60*60,
|
||||
{Authorization: 'Client-ID ' + Constants.ImgurApiKey})
|
||||
|
||||
const descr: string = response.data.description ?? "";
|
||||
const data: any = {};
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
export class LicenseInfo {
|
||||
title: string = ""
|
||||
artist: string = "";
|
||||
license: string = "";
|
||||
license: string = undefined;
|
||||
licenseShortName: string = "";
|
||||
usageTerms: string = "";
|
||||
attributionRequired: boolean = false;
|
||||
|
|
|
@ -4,7 +4,6 @@ import Svg from "../../Svg";
|
|||
import {Utils} from "../../Utils";
|
||||
import {LicenseInfo} from "./LicenseInfo";
|
||||
import Constants from "../../Models/Constants";
|
||||
import * as Console from "console";
|
||||
|
||||
export class Mapillary extends ImageProvider {
|
||||
|
||||
|
@ -82,7 +81,7 @@ export class Mapillary extends ImageProvider {
|
|||
return [this.PrepareUrlAsync(key, value)]
|
||||
}
|
||||
|
||||
protected async DownloadAttribution(url: string): Promise<LicenseInfo> {
|
||||
public async DownloadAttribution(url: string): Promise<LicenseInfo> {
|
||||
const license = new LicenseInfo()
|
||||
license.artist = "Contributor name unavailable";
|
||||
license.license = "CC BY-SA 4.0";
|
||||
|
@ -98,7 +97,7 @@ export class Mapillary extends ImageProvider {
|
|||
}
|
||||
|
||||
const metadataUrl = 'https://graph.mapillary.com/' + mapillaryId + '?fields=thumb_1024_url&&access_token=' + Constants.mapillary_client_token_v4;
|
||||
const response = await Utils.downloadJson(metadataUrl)
|
||||
const response = await Utils.downloadJsonCached(metadataUrl,60*60)
|
||||
const url = <string>response["thumb_1024_url"];
|
||||
return {
|
||||
url: url,
|
||||
|
|
|
@ -46,7 +46,7 @@ export class WikidataImageProvider extends ImageProvider {
|
|||
return allImages
|
||||
}
|
||||
|
||||
protected DownloadAttribution(url: string): Promise<any> {
|
||||
public DownloadAttribution(url: string): Promise<any> {
|
||||
throw new Error("Method not implemented; shouldn't be needed!");
|
||||
}
|
||||
|
||||
|
|
|
@ -112,7 +112,7 @@ export class WikimediaImageProvider extends ImageProvider {
|
|||
return [Promise.resolve(this.UrlForImage("File:" + value))]
|
||||
}
|
||||
|
||||
protected async DownloadAttribution(filename: string): Promise<LicenseInfo> {
|
||||
public async DownloadAttribution(filename: string): Promise<LicenseInfo> {
|
||||
filename = WikimediaImageProvider.ExtractFileName(filename)
|
||||
|
||||
if (filename === "") {
|
||||
|
@ -123,7 +123,7 @@ export class WikimediaImageProvider extends ImageProvider {
|
|||
"api.php?action=query&prop=imageinfo&iiprop=extmetadata&" +
|
||||
"titles=" + filename +
|
||||
"&format=json&origin=*";
|
||||
const data = await Utils.downloadJson(url)
|
||||
const data = await Utils.downloadJsonCached(url,365*24*60*60)
|
||||
const licenseInfo = new LicenseInfo();
|
||||
const pageInfo = data.query.pages[-1]
|
||||
if (pageInfo === undefined) {
|
||||
|
|
|
@ -222,6 +222,11 @@ export class Changes {
|
|||
}
|
||||
|
||||
console.log("Got the fresh objects!", osmObjects, "pending: ", pending)
|
||||
if(pending.length == 0){
|
||||
console.log("No pending changes...")
|
||||
return true;
|
||||
}
|
||||
|
||||
const perType = Array.from(
|
||||
Utils.Hist(pending.filter(descr => descr.meta.changeType !== undefined && descr.meta.changeType !== null)
|
||||
.map(descr => descr.meta.changeType)), ([key, count]) => (
|
||||
|
|
|
@ -216,11 +216,19 @@ export class TagUtils {
|
|||
*
|
||||
* TagUtils.Tag("xyz<5").matchesProperties({xyz: 4}) // => true
|
||||
* TagUtils.Tag("xyz<5").matchesProperties({xyz: 5}) // => false
|
||||
<<<<<<< HEAD
|
||||
*
|
||||
* // RegexTags must match values with newlines
|
||||
* TagUtils.Tag("note~.*aed.*").matchesProperties({note: "Hier bevindt zich wss een defibrillator. \\n\\n De aed bevindt zich op de 5de verdieping"}) // => true
|
||||
* TagUtils.Tag("note~i~.*aed.*").matchesProperties({note: "Hier bevindt zich wss een defibrillator. \\n\\n De AED bevindt zich op de 5de verdieping"}) // => true
|
||||
*
|
||||
=======
|
||||
*
|
||||
* // RegexTags must match values with newlines
|
||||
* TagUtils.Tag("note~.*aed.*").matchesProperties({note: "Hier bevindt zich wss een defibrillator. \\n\\n De aed bevindt zich op de 5de verdieping"}) // => true
|
||||
* TagUtils.Tag("note~i~.*aed.*").matchesProperties({note: "Hier bevindt zich wss een defibrillator. \\n\\n De AED bevindt zich op de 5de verdieping"}) // => true
|
||||
*
|
||||
>>>>>>> master
|
||||
* // Must match case insensitive
|
||||
* TagUtils.Tag("name~i~somename").matchesProperties({name: "SoMeName"}) // => true
|
||||
*/
|
||||
|
@ -286,7 +294,7 @@ export class TagUtils {
|
|||
private static TagUnsafe(json: AndOrTagConfigJson | string, context: string = ""): TagsFilter {
|
||||
|
||||
if (json === undefined) {
|
||||
throw `Error while parsing a tag: 'json' is undefined in ${context}. Make sure all the tags are defined and at least one tag is present in a complex expression`
|
||||
throw new Error(`Error while parsing a tag: 'json' is undefined in ${context}. Make sure all the tags are defined and at least one tag is present in a complex expression`)
|
||||
}
|
||||
if (typeof (json) != "string") {
|
||||
if (json.and !== undefined && json.or !== undefined) {
|
||||
|
@ -335,7 +343,7 @@ export class TagUtils {
|
|||
return new ComparingTag(split[0], f, operator + val)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (tag.indexOf("~~") >= 0) {
|
||||
const split = Utils.SplitFirst(tag, "~~");
|
||||
if (split[1] === "*") {
|
||||
|
@ -346,9 +354,8 @@ export class TagUtils {
|
|||
new RegExp("^" + split[1] + "$", "s")
|
||||
);
|
||||
}
|
||||
|
||||
const withRegex = TagUtils.parseRegexOperator(tag)
|
||||
if(withRegex != null) {
|
||||
if (withRegex != null) {
|
||||
if (withRegex.value === "*" && withRegex.invert) {
|
||||
throw `Don't use 'key!~*' - use 'key=' instead (empty string as value (in the tag ${tag} while parsing ${context})`
|
||||
}
|
||||
|
@ -362,7 +369,7 @@ export class TagUtils {
|
|||
}
|
||||
return new RegexTag(
|
||||
withRegex.key,
|
||||
new RegExp("^"+value+"$", "s"+withRegex.modifier),
|
||||
new RegExp("^" + value + "$", "s" + withRegex.modifier),
|
||||
withRegex.invert
|
||||
);
|
||||
}
|
||||
|
@ -459,35 +466,35 @@ export class TagUtils {
|
|||
/**
|
||||
* Returns 'true' is opposite tags are detected.
|
||||
* Note that this method will never work perfectly
|
||||
*
|
||||
*
|
||||
* // should be false for some simple cases
|
||||
* TagUtils.ContainsOppositeTags([new Tag("key", "value"), new Tag("key0", "value")]) // => false
|
||||
* TagUtils.ContainsOppositeTags([new Tag("key", "value"), new Tag("key", "value0")]) // => false
|
||||
*
|
||||
*
|
||||
* // should detect simple cases
|
||||
* TagUtils.ContainsOppositeTags([new Tag("key", "value"), new RegexTag("key", "value", true)]) // => true
|
||||
* TagUtils.ContainsOppositeTags([new Tag("key", "value"), new RegexTag("key", /value/, true)]) // => true
|
||||
*/
|
||||
public static ContainsOppositeTags(tags: (TagsFilter)[]) : boolean{
|
||||
for (let i = 0; i < tags.length; i++){
|
||||
public static ContainsOppositeTags(tags: (TagsFilter)[]): boolean {
|
||||
for (let i = 0; i < tags.length; i++) {
|
||||
const tag = tags[i];
|
||||
if(!(tag instanceof Tag || tag instanceof RegexTag)){
|
||||
if (!(tag instanceof Tag || tag instanceof RegexTag)) {
|
||||
continue
|
||||
}
|
||||
for (let j = i + 1; j < tags.length; j++){
|
||||
for (let j = i + 1; j < tags.length; j++) {
|
||||
const guard = tags[j];
|
||||
if(!(guard instanceof Tag || guard instanceof RegexTag)){
|
||||
if (!(guard instanceof Tag || guard instanceof RegexTag)) {
|
||||
continue
|
||||
}
|
||||
if(guard.key !== tag.key) {
|
||||
if (guard.key !== tag.key) {
|
||||
// Different keys: they can _never_ be opposites
|
||||
continue
|
||||
}
|
||||
if((guard.value["source"] ?? guard.value) !== (tag.value["source"] ?? tag.value)){
|
||||
if ((guard.value["source"] ?? guard.value) !== (tag.value["source"] ?? tag.value)) {
|
||||
// different values: the can _never_ be opposites
|
||||
continue
|
||||
}
|
||||
if( (guard["invert"] ?? false) !== (tag["invert"] ?? false) ) {
|
||||
if ((guard["invert"] ?? false) !== (tag["invert"] ?? false)) {
|
||||
// The 'invert' flags are opposite, the key and value is the same for both
|
||||
// This means we have found opposite tags!
|
||||
return true
|
||||
|
@ -502,28 +509,28 @@ export class TagUtils {
|
|||
* Returns a filtered version of 'listToFilter'.
|
||||
* For a list [t0, t1, t2], If `blackList` contains an equivalent (or broader) match of any `t`, this respective `t` is dropped from the returned list
|
||||
* Ignores nested ORS and ANDS
|
||||
*
|
||||
*
|
||||
* TagUtils.removeShadowedElementsFrom([new Tag("key","value")], [new Tag("key","value"), new Tag("other_key","value")]) // => [new Tag("other_key","value")]
|
||||
*/
|
||||
public static removeShadowedElementsFrom(blacklist: TagsFilter[], listToFilter: TagsFilter[] ) : TagsFilter[] {
|
||||
public static removeShadowedElementsFrom(blacklist: TagsFilter[], listToFilter: TagsFilter[]): TagsFilter[] {
|
||||
return listToFilter.filter(tf => !blacklist.some(guard => guard.shadows(tf)))
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a filtered version of 'listToFilter', where no duplicates and no equivalents exists.
|
||||
*
|
||||
*
|
||||
* TagUtils.removeEquivalents([new RegexTag("key", /^..*$/), new Tag("key","value")]) // => [new Tag("key", "value")]
|
||||
*/
|
||||
public static removeEquivalents( listToFilter: (Tag | RegexTag)[]) : TagsFilter[] {
|
||||
public static removeEquivalents(listToFilter: (Tag | RegexTag)[]): TagsFilter[] {
|
||||
const result: TagsFilter[] = []
|
||||
outer: for (let i = 0; i < listToFilter.length; i++){
|
||||
outer: for (let i = 0; i < listToFilter.length; i++) {
|
||||
const tag = listToFilter[i];
|
||||
for (let j = 0; j < listToFilter.length; j++){
|
||||
if(i === j){
|
||||
for (let j = 0; j < listToFilter.length; j++) {
|
||||
if (i === j) {
|
||||
continue
|
||||
}
|
||||
const guard = listToFilter[j];
|
||||
if(guard.shadows(tag)) {
|
||||
if (guard.shadows(tag)) {
|
||||
// the guard 'kills' the tag: we continue the outer loop without adding the tag
|
||||
continue outer;
|
||||
}
|
||||
|
@ -532,7 +539,7 @@ export class TagUtils {
|
|||
}
|
||||
return result
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns `true` if at least one element of the 'guards' shadows one element of the 'listToFilter'.
|
||||
*
|
||||
|
@ -540,7 +547,7 @@ export class TagUtils {
|
|||
* TagUtils.containsEquivalents([new Tag("key","value")], [ new Tag("other_key","value")]) // => false
|
||||
* TagUtils.containsEquivalents([new Tag("key","value")], [ new Tag("key","other_value")]) // => false
|
||||
*/
|
||||
public static containsEquivalents( guards: TagsFilter[], listToFilter: TagsFilter[] ) : boolean {
|
||||
public static containsEquivalents(guards: TagsFilter[], listToFilter: TagsFilter[]): boolean {
|
||||
return listToFilter.some(tf => guards.some(guard => guard.shadows(tf)))
|
||||
}
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@ import {Utils} from "../../Utils";
|
|||
export class QueryParameters {
|
||||
|
||||
static defaults = {}
|
||||
static documentation = {}
|
||||
static documentation: Map<string, string> = new Map<string, string>()
|
||||
private static order: string [] = ["layout", "test", "z", "lat", "lon"];
|
||||
private static _wasInitialized: Set<string> = new Set()
|
||||
private static knownSources = {};
|
||||
|
@ -18,7 +18,7 @@ export class QueryParameters {
|
|||
if (!this.initialized) {
|
||||
this.init();
|
||||
}
|
||||
QueryParameters.documentation[key] = documentation;
|
||||
QueryParameters.documentation.set(key, documentation);
|
||||
if (deflt !== undefined) {
|
||||
QueryParameters.defaults[key] = deflt;
|
||||
}
|
||||
|
|
|
@ -121,13 +121,14 @@ export default class Wikipedia {
|
|||
* @param searchTerm
|
||||
*/
|
||||
public async searchViaIndex(searchTerm: string): Promise<{ title: string, snippet: string, url: string } []> {
|
||||
const url = `${this.backend}/w/index.php?search=${encodeURIComponent(searchTerm)}`
|
||||
const url = `${this.backend}/w/index.php?search=${encodeURIComponent(searchTerm)}&ns0=1`
|
||||
const result = await Utils.downloadAdvanced(url);
|
||||
if(result["redirect"] ){
|
||||
const targetUrl = result["redirect"]
|
||||
// This is an exact match
|
||||
return [{
|
||||
title: this.extractPageName(result["redirect"]).trim(),
|
||||
url: result["redirect"],
|
||||
title: this.extractPageName(targetUrl)?.trim(),
|
||||
url: targetUrl,
|
||||
snippet: ""
|
||||
}]
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue