forked from MapComplete/MapComplete
Refactoring: move the units into the layers instead of the themes
This commit is contained in:
parent
3492b5d403
commit
206aff2c9a
16 changed files with 259 additions and 300 deletions
|
@ -9,13 +9,11 @@ export default class AllKnownLayers {
|
||||||
public static sharedLayers: Map<string, LayerConfig> = AllKnownLayers.getSharedLayers();
|
public static sharedLayers: Map<string, LayerConfig> = AllKnownLayers.getSharedLayers();
|
||||||
public static sharedLayersJson: Map<string, any> = AllKnownLayers.getSharedLayersJson();
|
public static sharedLayersJson: Map<string, any> = AllKnownLayers.getSharedLayersJson();
|
||||||
|
|
||||||
public static sharedUnits: any[] = []
|
|
||||||
|
|
||||||
private static getSharedLayers(): Map<string, LayerConfig> {
|
private static getSharedLayers(): Map<string, LayerConfig> {
|
||||||
const sharedLayers = new Map<string, LayerConfig>();
|
const sharedLayers = new Map<string, LayerConfig>();
|
||||||
for (const layer of known_layers.layers) {
|
for (const layer of known_layers.layers) {
|
||||||
try {
|
try {
|
||||||
const parsed = new LayerConfig(layer, AllKnownLayers.sharedUnits, "shared_layers")
|
const parsed = new LayerConfig(layer, "shared_layers")
|
||||||
sharedLayers.set(layer.id, parsed);
|
sharedLayers.set(layer.id, parsed);
|
||||||
sharedLayers[layer.id] = parsed;
|
sharedLayers[layer.id] = parsed;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
@ -35,7 +33,7 @@ export default class AllKnownLayers {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
const parsed = new LayerConfig(layer, AllKnownLayers.sharedUnits, "shared_layer_in_theme")
|
const parsed = new LayerConfig(layer, "shared_layer_in_theme")
|
||||||
sharedLayers.set(layer.id, parsed);
|
sharedLayers.set(layer.id, parsed);
|
||||||
sharedLayers[layer.id] = parsed;
|
sharedLayers[layer.id] = parsed;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import {Translation} from "../UI/i18n/Translation";
|
import {Translation} from "../UI/i18n/Translation";
|
||||||
import UnitConfigJson from "./ThemeConfig/Json/UnitConfigJson";
|
import {ApplicableUnitJson} from "./ThemeConfig/Json/UnitConfigJson";
|
||||||
import Translations from "../UI/i18n/Translations";
|
import Translations from "../UI/i18n/Translations";
|
||||||
|
|
||||||
export class Denomination {
|
export class Denomination {
|
||||||
|
@ -9,7 +9,7 @@ export class Denomination {
|
||||||
public readonly alternativeDenominations: string [];
|
public readonly alternativeDenominations: string [];
|
||||||
private readonly _human: Translation;
|
private readonly _human: Translation;
|
||||||
|
|
||||||
constructor(json: UnitConfigJson, context: string) {
|
constructor(json: ApplicableUnitJson, context: string) {
|
||||||
context = `${context}.unit(${json.canonicalDenomination})`
|
context = `${context}.unit(${json.canonicalDenomination})`
|
||||||
this.canonical = json.canonicalDenomination.trim()
|
this.canonical = json.canonicalDenomination.trim()
|
||||||
if (this.canonical === undefined) {
|
if (this.canonical === undefined) {
|
||||||
|
|
|
@ -2,6 +2,7 @@ import {AndOrTagConfigJson} from "./TagConfigJson";
|
||||||
import {TagRenderingConfigJson} from "./TagRenderingConfigJson";
|
import {TagRenderingConfigJson} from "./TagRenderingConfigJson";
|
||||||
import FilterConfigJson from "./FilterConfigJson";
|
import FilterConfigJson from "./FilterConfigJson";
|
||||||
import {DeleteConfigJson} from "./DeleteConfigJson";
|
import {DeleteConfigJson} from "./DeleteConfigJson";
|
||||||
|
import UnitConfigJson from "./UnitConfigJson";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Configuration for a single layer
|
* Configuration for a single layer
|
||||||
|
@ -317,4 +318,64 @@ export interface LayerConfigJson {
|
||||||
*/
|
*/
|
||||||
allowSplit?: boolean
|
allowSplit?: boolean
|
||||||
|
|
||||||
|
/**
|
||||||
|
* In some cases, a value is represented in a certain unit (such as meters for heigt/distance/..., km/h for speed, ...)
|
||||||
|
*
|
||||||
|
* Sometimes, multiple denominations are possible (e.g. km/h vs mile/h; megawatt vs kilowatt vs gigawatt for power generators, ...)
|
||||||
|
*
|
||||||
|
* This brings in some troubles, as there are multiple ways to write it (no denomitation, 'm' vs 'meter' 'metre', ...)
|
||||||
|
*
|
||||||
|
* Not only do we want to write consistent data to OSM, we also want to present this consistently to the user.
|
||||||
|
* This is handled by defining units.
|
||||||
|
*
|
||||||
|
* # Rendering
|
||||||
|
*
|
||||||
|
* To render a value with long (human) denomination, use {canonical(key)}
|
||||||
|
*
|
||||||
|
* # Usage
|
||||||
|
*
|
||||||
|
* First of all, you define which keys have units applied, for example:
|
||||||
|
*
|
||||||
|
* ```
|
||||||
|
* units: [
|
||||||
|
* appliesTo: ["maxspeed", "maxspeed:hgv", "maxspeed:bus"]
|
||||||
|
* applicableUnits: [
|
||||||
|
* ...
|
||||||
|
* ]
|
||||||
|
* ]
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* ApplicableUnits defines which is the canonical extension, how it is presented to the user, ...:
|
||||||
|
*
|
||||||
|
* ```
|
||||||
|
* applicableUnits: [
|
||||||
|
* {
|
||||||
|
* canonicalDenomination: "km/h",
|
||||||
|
* alternativeDenomination: ["km/u", "kmh", "kph"]
|
||||||
|
* default: true,
|
||||||
|
* human: {
|
||||||
|
* en: "kilometer/hour",
|
||||||
|
* nl: "kilometer/uur"
|
||||||
|
* },
|
||||||
|
* humanShort: {
|
||||||
|
* en: "km/h",
|
||||||
|
* nl: "km/u"
|
||||||
|
* }
|
||||||
|
* },
|
||||||
|
* {
|
||||||
|
* canoncialDenomination: "mph",
|
||||||
|
* ... similar for miles an hour ...
|
||||||
|
* }
|
||||||
|
* ]
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* If this is defined, then every key which the denominations apply to (`maxspeed`, `maxspeed:hgv` and `maxspeed:bus`) will be rewritten at the metatagging stage:
|
||||||
|
* every value will be parsed and the canonical extension will be added add presented to the other parts of the code.
|
||||||
|
*
|
||||||
|
* Also, if a freeform text field is used, an extra dropdown with applicable denominations will be given
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
units?: UnitConfigJson[]
|
||||||
|
|
||||||
}
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
import {TagRenderingConfigJson} from "./TagRenderingConfigJson";
|
import {TagRenderingConfigJson} from "./TagRenderingConfigJson";
|
||||||
import UnitConfigJson from "./UnitConfigJson";
|
|
||||||
import {LayerConfigJson} from "./LayerConfigJson";
|
import {LayerConfigJson} from "./LayerConfigJson";
|
||||||
|
import UnitConfigJson from "./UnitConfigJson";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Defines the entire theme.
|
* Defines the entire theme.
|
||||||
|
@ -217,83 +217,6 @@ export interface LayoutConfigJson {
|
||||||
*/
|
*/
|
||||||
layers: (LayerConfigJson | string | { builtin: string | string[], override: any })[],
|
layers: (LayerConfigJson | string | { builtin: string | string[], override: any })[],
|
||||||
|
|
||||||
/**
|
|
||||||
* In some cases, a value is represented in a certain unit (such as meters for heigt/distance/..., km/h for speed, ...)
|
|
||||||
*
|
|
||||||
* Sometimes, multiple denominations are possible (e.g. km/h vs mile/h; megawatt vs kilowatt vs gigawatt for power generators, ...)
|
|
||||||
*
|
|
||||||
* This brings in some troubles, as there are multiple ways to write it (no denomitation, 'm' vs 'meter' 'metre', ...)
|
|
||||||
*
|
|
||||||
* Not only do we want to write consistent data to OSM, we also want to present this consistently to the user.
|
|
||||||
* This is handled by defining units.
|
|
||||||
*
|
|
||||||
* # Rendering
|
|
||||||
*
|
|
||||||
* To render a value with long (human) denomination, use {canonical(key)}
|
|
||||||
*
|
|
||||||
* # Usage
|
|
||||||
*
|
|
||||||
* First of all, you define which keys have units applied, for example:
|
|
||||||
*
|
|
||||||
* ```
|
|
||||||
* units: [
|
|
||||||
* appliesTo: ["maxspeed", "maxspeed:hgv", "maxspeed:bus"]
|
|
||||||
* applicableUnits: [
|
|
||||||
* ...
|
|
||||||
* ]
|
|
||||||
* ]
|
|
||||||
* ```
|
|
||||||
*
|
|
||||||
* ApplicableUnits defines which is the canonical extension, how it is presented to the user, ...:
|
|
||||||
*
|
|
||||||
* ```
|
|
||||||
* applicableUnits: [
|
|
||||||
* {
|
|
||||||
* canonicalDenomination: "km/h",
|
|
||||||
* alternativeDenomination: ["km/u", "kmh", "kph"]
|
|
||||||
* default: true,
|
|
||||||
* human: {
|
|
||||||
* en: "kilometer/hour",
|
|
||||||
* nl: "kilometer/uur"
|
|
||||||
* },
|
|
||||||
* humanShort: {
|
|
||||||
* en: "km/h",
|
|
||||||
* nl: "km/u"
|
|
||||||
* }
|
|
||||||
* },
|
|
||||||
* {
|
|
||||||
* canoncialDenomination: "mph",
|
|
||||||
* ... similar for miles an hour ...
|
|
||||||
* }
|
|
||||||
* ]
|
|
||||||
* ```
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* If this is defined, then every key which the denominations apply to (`maxspeed`, `maxspeed:hgv` and `maxspeed:bus`) will be rewritten at the metatagging stage:
|
|
||||||
* every value will be parsed and the canonical extension will be added add presented to the other parts of the code.
|
|
||||||
*
|
|
||||||
* Also, if a freeform text field is used, an extra dropdown with applicable denominations will be given
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
units?: {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Every key from this list will be normalized
|
|
||||||
*/
|
|
||||||
appliesToKey: string[],
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The possible denominations
|
|
||||||
*/
|
|
||||||
applicableUnits: UnitConfigJson[]
|
|
||||||
/**
|
|
||||||
* If set, invalid values will be erased in the MC application (but not in OSM of course!)
|
|
||||||
* Be careful with setting this
|
|
||||||
*/
|
|
||||||
eraseInvalidValues?: boolean;
|
|
||||||
}[]
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If defined, data will be clustered.
|
* If defined, data will be clustered.
|
||||||
* Defaults to {maxZoom: 16, minNeeded: 500}
|
* Defaults to {maxZoom: 16, minNeeded: 500}
|
||||||
|
|
|
@ -1,5 +1,23 @@
|
||||||
export default interface UnitConfigJson {
|
export default interface UnitConfigJson {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Every key from this list will be normalized
|
||||||
|
*/
|
||||||
|
appliesToKey: string[],
|
||||||
|
/**
|
||||||
|
* If set, invalid values will be erased in the MC application (but not in OSM of course!)
|
||||||
|
* Be careful with setting this
|
||||||
|
*/
|
||||||
|
eraseInvalidValues?: boolean;
|
||||||
|
/**
|
||||||
|
* The possible denominations
|
||||||
|
*/
|
||||||
|
applicableUnits:ApplicableUnitJson[]
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ApplicableUnitJson
|
||||||
|
{
|
||||||
/**
|
/**
|
||||||
* The canonical value which will be added to the text.
|
* The canonical value which will be added to the text.
|
||||||
* e.g. "m" for meters
|
* e.g. "m" for meters
|
||||||
|
@ -32,5 +50,4 @@ export default interface UnitConfigJson {
|
||||||
* If none is set, the first unit will be considered the default interpretation of a value without a unit
|
* If none is set, the first unit will be considered the default interpretation of a value without a unit
|
||||||
*/
|
*/
|
||||||
default?: boolean
|
default?: boolean
|
||||||
|
|
||||||
}
|
}
|
|
@ -57,16 +57,16 @@ export default class LayerConfig {
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
json: LayerConfigJson,
|
json: LayerConfigJson,
|
||||||
units?: Unit[],
|
|
||||||
context?: string,
|
context?: string,
|
||||||
official: boolean = true
|
official: boolean = true
|
||||||
) {
|
) {
|
||||||
this.units = units ?? [];
|
|
||||||
context = context + "." + json.id;
|
context = context + "." + json.id;
|
||||||
const self = this;
|
const self = this;
|
||||||
this.id = json.id;
|
this.id = json.id;
|
||||||
this.allowSplit = json.allowSplit ?? false;
|
this.allowSplit = json.allowSplit ?? false;
|
||||||
this.name = Translations.T(json.name, context + ".name");
|
this.name = Translations.T(json.name, context + ".name");
|
||||||
|
this.units = (json.units ?? []).map(((unitJson, i) => Unit.fromJson(unitJson, `${context}.unit[${i}]`)))
|
||||||
|
|
||||||
if (json.description !== undefined) {
|
if (json.description !== undefined) {
|
||||||
if (Object.keys(json.description).length === 0) {
|
if (Object.keys(json.description).length === 0) {
|
||||||
|
|
|
@ -6,7 +6,6 @@ import AllKnownLayers from "../../Customizations/AllKnownLayers";
|
||||||
import {Utils} from "../../Utils";
|
import {Utils} from "../../Utils";
|
||||||
import LayerConfig from "./LayerConfig";
|
import LayerConfig from "./LayerConfig";
|
||||||
import {Unit} from "../Unit";
|
import {Unit} from "../Unit";
|
||||||
import {Denomination} from "../Denomination";
|
|
||||||
import {LayerConfigJson} from "./Json/LayerConfigJson";
|
import {LayerConfigJson} from "./Json/LayerConfigJson";
|
||||||
|
|
||||||
export default class LayoutConfig {
|
export default class LayoutConfig {
|
||||||
|
@ -52,7 +51,6 @@ export default class LayoutConfig {
|
||||||
How long is the cache valid, in seconds?
|
How long is the cache valid, in seconds?
|
||||||
*/
|
*/
|
||||||
public readonly cacheTimeout?: number;
|
public readonly cacheTimeout?: number;
|
||||||
public readonly units: Unit[] = []
|
|
||||||
public readonly overpassUrl: string;
|
public readonly overpassUrl: string;
|
||||||
public readonly overpassTimeout: number;
|
public readonly overpassTimeout: number;
|
||||||
private readonly _official: boolean;
|
private readonly _official: boolean;
|
||||||
|
@ -80,7 +78,6 @@ export default class LayoutConfig {
|
||||||
if (json.description === undefined) {
|
if (json.description === undefined) {
|
||||||
throw "Description not defined in " + this.id;
|
throw "Description not defined in " + this.id;
|
||||||
}
|
}
|
||||||
this.units = LayoutConfig.ExtractUnits(json, context) ?? [];
|
|
||||||
this.title = new Translation(json.title, context + ".title");
|
this.title = new Translation(json.title, context + ".title");
|
||||||
this.description = new Translation(json.description, context + ".description");
|
this.description = new Translation(json.description, context + ".description");
|
||||||
this.shortDescription = json.shortDescription === undefined ? this.description.FirstSentence() : new Translation(json.shortDescription, context + ".shortdescription");
|
this.shortDescription = json.shortDescription === undefined ? this.description.FirstSentence() : new Translation(json.shortDescription, context + ".shortdescription");
|
||||||
|
@ -101,7 +98,7 @@ export default class LayoutConfig {
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
this.defaultBackgroundId = json.defaultBackgroundId;
|
this.defaultBackgroundId = json.defaultBackgroundId;
|
||||||
this.layers = LayoutConfig.ExtractLayers(json, this.units, official, context);
|
this.layers = LayoutConfig.ExtractLayers(json, official, context);
|
||||||
|
|
||||||
// ALl the layers are constructed, let them share tagRenderings now!
|
// ALl the layers are constructed, let them share tagRenderings now!
|
||||||
const roaming: { r, source: LayerConfig }[] = []
|
const roaming: { r, source: LayerConfig }[] = []
|
||||||
|
@ -168,7 +165,7 @@ export default class LayoutConfig {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ExtractLayers(json: LayoutConfigJson, units: Unit[], official: boolean, context: string): LayerConfig[] {
|
private static ExtractLayers(json: LayoutConfigJson, official: boolean, context: string): LayerConfig[] {
|
||||||
const result: LayerConfig[] = []
|
const result: LayerConfig[] = []
|
||||||
|
|
||||||
json.layers.forEach((layer, i) => {
|
json.layers.forEach((layer, i) => {
|
||||||
|
@ -176,7 +173,7 @@ export default class LayoutConfig {
|
||||||
if (AllKnownLayers.sharedLayersJson.get(layer) !== undefined) {
|
if (AllKnownLayers.sharedLayersJson.get(layer) !== undefined) {
|
||||||
if (json.overrideAll !== undefined) {
|
if (json.overrideAll !== undefined) {
|
||||||
let lyr = JSON.parse(JSON.stringify(AllKnownLayers.sharedLayersJson[layer]));
|
let lyr = JSON.parse(JSON.stringify(AllKnownLayers.sharedLayersJson[layer]));
|
||||||
const newLayer = new LayerConfig(Utils.Merge(json.overrideAll, lyr), units, `${json.id}+overrideAll.layers[${i}]`, official)
|
const newLayer = new LayerConfig(Utils.Merge(json.overrideAll, lyr), `${json.id}+overrideAll.layers[${i}]`, official)
|
||||||
result.push(newLayer)
|
result.push(newLayer)
|
||||||
return
|
return
|
||||||
} else {
|
} else {
|
||||||
|
@ -194,7 +191,7 @@ export default class LayoutConfig {
|
||||||
layer = Utils.Merge(json.overrideAll, layer);
|
layer = Utils.Merge(json.overrideAll, layer);
|
||||||
}
|
}
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
const newLayer = new LayerConfig(layer, units, `${json.id}.layers[${i}]`, official)
|
const newLayer = new LayerConfig(layer, `${json.id}.layers[${i}]`, official)
|
||||||
result.push(newLayer)
|
result.push(newLayer)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -213,7 +210,7 @@ export default class LayoutConfig {
|
||||||
newLayer = Utils.Merge(json.overrideAll, newLayer);
|
newLayer = Utils.Merge(json.overrideAll, newLayer);
|
||||||
}
|
}
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
const layerConfig = new LayerConfig(newLayer, units, `${json.id}.layers[${i}]`, official)
|
const layerConfig = new LayerConfig(newLayer, `${json.id}.layers[${i}]`, official)
|
||||||
result.push(layerConfig)
|
result.push(layerConfig)
|
||||||
return
|
return
|
||||||
})
|
})
|
||||||
|
@ -223,54 +220,6 @@ export default class LayoutConfig {
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ExtractUnits(json: LayoutConfigJson, context: string): Unit[] {
|
|
||||||
const result: Unit[] = []
|
|
||||||
if ((json.units ?? []).length !== 0) {
|
|
||||||
for (let i1 = 0; i1 < json.units.length; i1++) {
|
|
||||||
let unit = json.units[i1];
|
|
||||||
const appliesTo = unit.appliesToKey
|
|
||||||
|
|
||||||
for (let i = 0; i < appliesTo.length; i++) {
|
|
||||||
let key = appliesTo[i];
|
|
||||||
if (key.trim() !== key) {
|
|
||||||
throw `${context}.unit[${i1}].appliesToKey[${i}] is invalid: it starts or ends with whitespace`
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((unit.applicableUnits ?? []).length === 0) {
|
|
||||||
throw `${context}: define at least one applicable unit`
|
|
||||||
}
|
|
||||||
// Some keys do have unit handling
|
|
||||||
|
|
||||||
const defaultSet = unit.applicableUnits.filter(u => u.default === true)
|
|
||||||
// No default is defined - we pick the first as default
|
|
||||||
if (defaultSet.length === 0) {
|
|
||||||
unit.applicableUnits[0].default = true
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check that there are not multiple defaults
|
|
||||||
if (defaultSet.length > 1) {
|
|
||||||
throw `Multiple units are set as default: they have canonical values of ${defaultSet.map(u => u.canonicalDenomination).join(", ")}`
|
|
||||||
}
|
|
||||||
const applicable = unit.applicableUnits.map((u, i) => new Denomination(u, `${context}.units[${i}]`))
|
|
||||||
result.push(new Unit(appliesTo, applicable, unit.eraseInvalidValues ?? false));
|
|
||||||
}
|
|
||||||
|
|
||||||
const seenKeys = new Set<string>()
|
|
||||||
for (const unit of result) {
|
|
||||||
const alreadySeen = Array.from(unit.appliesToKeys).filter((key: string) => seenKeys.has(key));
|
|
||||||
if (alreadySeen.length > 0) {
|
|
||||||
throw `${context}.units: multiple units define the same keys. The key(s) ${alreadySeen.join(",")} occur multiple times`
|
|
||||||
}
|
|
||||||
unit.appliesToKeys.forEach(key => seenKeys.add(key))
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public CustomCodeSnippets(): string[] {
|
public CustomCodeSnippets(): string[] {
|
||||||
if (this._official) {
|
if (this._official) {
|
||||||
return [];
|
return [];
|
||||||
|
|
|
@ -67,7 +67,7 @@ export default class TagRenderingConfig {
|
||||||
}
|
}
|
||||||
if (json.freeform) {
|
if (json.freeform) {
|
||||||
|
|
||||||
if(json.freeform.addExtraTags !== undefined && json.freeform.addExtraTags.length === undefined){
|
if(json.freeform.addExtraTags !== undefined && json.freeform.addExtraTags.map === undefined){
|
||||||
throw `Freeform.addExtraTags should be a list of strings - not a single string (at ${context})`
|
throw `Freeform.addExtraTags should be a list of strings - not a single string (at ${context})`
|
||||||
}
|
}
|
||||||
this.freeform = {
|
this.freeform = {
|
||||||
|
|
|
@ -2,6 +2,7 @@ import BaseUIElement from "../UI/BaseUIElement";
|
||||||
import {FixedUiElement} from "../UI/Base/FixedUiElement";
|
import {FixedUiElement} from "../UI/Base/FixedUiElement";
|
||||||
import Combine from "../UI/Base/Combine";
|
import Combine from "../UI/Base/Combine";
|
||||||
import {Denomination} from "./Denomination";
|
import {Denomination} from "./Denomination";
|
||||||
|
import UnitConfigJson from "./ThemeConfig/Json/UnitConfigJson";
|
||||||
|
|
||||||
export class Unit {
|
export class Unit {
|
||||||
public readonly appliesToKeys: Set<string>;
|
public readonly appliesToKeys: Set<string>;
|
||||||
|
@ -52,6 +53,35 @@ export class Unit {
|
||||||
this.possiblePostFixes.sort((a, b) => b.length - a.length)
|
this.possiblePostFixes.sort((a, b) => b.length - a.length)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static fromJson(json: UnitConfigJson, ctx: string){
|
||||||
|
const appliesTo = json.appliesToKey
|
||||||
|
for (let i = 0; i < appliesTo.length; i++) {
|
||||||
|
let key = appliesTo[i];
|
||||||
|
if (key.trim() !== key) {
|
||||||
|
throw `${ctx}.appliesToKey[${i}] is invalid: it starts or ends with whitespace`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((json.applicableUnits ?? []).length === 0) {
|
||||||
|
throw `${ctx}: define at least one applicable unit`
|
||||||
|
}
|
||||||
|
// Some keys do have unit handling
|
||||||
|
|
||||||
|
const defaultSet = json.applicableUnits.filter(u => u.default === true)
|
||||||
|
// No default is defined - we pick the first as default
|
||||||
|
if (defaultSet.length === 0) {
|
||||||
|
json.applicableUnits[0].default = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that there are not multiple defaults
|
||||||
|
if (defaultSet.length > 1) {
|
||||||
|
throw `Multiple units are set as default: they have canonical values of ${defaultSet.map(u => u.canonicalDenomination).join(", ")}`
|
||||||
|
}
|
||||||
|
const applicable = json.applicableUnits.map((u, i) => new Denomination(u, `${ctx}.units[${i}]`))
|
||||||
|
return new Unit(appliesTo, applicable, json.eraseInvalidValues ?? false)
|
||||||
|
}
|
||||||
|
|
||||||
isApplicableToKey(key: string | undefined): boolean {
|
isApplicableToKey(key: string | undefined): boolean {
|
||||||
if (key === undefined) {
|
if (key === undefined) {
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -154,5 +154,26 @@
|
||||||
"tower:type=observation"
|
"tower:type=observation"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
"units": [
|
||||||
|
{
|
||||||
|
"appliesToKey": [
|
||||||
|
"height"
|
||||||
|
],
|
||||||
|
"applicableUnits": [
|
||||||
|
{
|
||||||
|
"canonicalDenomination": "m",
|
||||||
|
"alternativeDenomination": [
|
||||||
|
"meter",
|
||||||
|
"mtr"
|
||||||
|
],
|
||||||
|
"human": {
|
||||||
|
"nl": " meter",
|
||||||
|
"en": " meter"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"eraseInvalidValues": true
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
|
@ -913,43 +913,6 @@
|
||||||
"wayHandling": 0
|
"wayHandling": 0
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"units": [
|
|
||||||
{
|
|
||||||
"appliesToKey": [
|
|
||||||
"climbing:length",
|
|
||||||
"climbing:length:min",
|
|
||||||
"climbing:length:max"
|
|
||||||
],
|
|
||||||
"applicableUnits": [
|
|
||||||
{
|
|
||||||
"canonicalDenomination": "",
|
|
||||||
"alternativeDenomination": [
|
|
||||||
"m",
|
|
||||||
"meter",
|
|
||||||
"meters"
|
|
||||||
],
|
|
||||||
"human": {
|
|
||||||
"en": " meter",
|
|
||||||
"nl": " meter",
|
|
||||||
"fr": " mètres"
|
|
||||||
},
|
|
||||||
"default": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"canonicalDenomination": "ft",
|
|
||||||
"alternativeDenomination": [
|
|
||||||
"feet",
|
|
||||||
"voet"
|
|
||||||
],
|
|
||||||
"human": {
|
|
||||||
"en": " feet",
|
|
||||||
"nl": " voet",
|
|
||||||
"fr": " pieds"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"roamingRenderings": [
|
"roamingRenderings": [
|
||||||
{
|
{
|
||||||
"#": "Website",
|
"#": "Website",
|
||||||
|
@ -1466,6 +1429,7 @@
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
||||||
"overrideAll": {
|
"overrideAll": {
|
||||||
"titleIcons": [
|
"titleIcons": [
|
||||||
{
|
{
|
||||||
|
@ -1497,6 +1461,43 @@
|
||||||
"_embedding_feature:access=JSON.parse(feat.properties._embedding_features_with_access ?? '{}').access",
|
"_embedding_feature:access=JSON.parse(feat.properties._embedding_features_with_access ?? '{}').access",
|
||||||
"_embedding_feature:access:description=JSON.parse(feat.properties._embedding_features_with_access ?? '{}')['access:description']",
|
"_embedding_feature:access:description=JSON.parse(feat.properties._embedding_features_with_access ?? '{}')['access:description']",
|
||||||
"_embedding_feature:id=JSON.parse(feat.properties._embedding_features_with_access ?? '{}').id"
|
"_embedding_feature:id=JSON.parse(feat.properties._embedding_features_with_access ?? '{}').id"
|
||||||
|
],
|
||||||
|
"units+": [
|
||||||
|
{
|
||||||
|
"appliesToKey": [
|
||||||
|
"climbing:length",
|
||||||
|
"climbing:length:min",
|
||||||
|
"climbing:length:max"
|
||||||
|
],
|
||||||
|
"applicableUnits": [
|
||||||
|
{
|
||||||
|
"canonicalDenomination": "",
|
||||||
|
"alternativeDenomination": [
|
||||||
|
"m",
|
||||||
|
"meter",
|
||||||
|
"meters"
|
||||||
|
],
|
||||||
|
"human": {
|
||||||
|
"en": " meter",
|
||||||
|
"nl": " meter",
|
||||||
|
"fr": " mètres"
|
||||||
|
},
|
||||||
|
"default": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"canonicalDenomination": "ft",
|
||||||
|
"alternativeDenomination": [
|
||||||
|
"feet",
|
||||||
|
"voet"
|
||||||
|
],
|
||||||
|
"human": {
|
||||||
|
"en": " feet",
|
||||||
|
"nl": " voet",
|
||||||
|
"fr": " pieds"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -26,26 +26,5 @@
|
||||||
"socialImage": "",
|
"socialImage": "",
|
||||||
"layers": [
|
"layers": [
|
||||||
"observation_tower"
|
"observation_tower"
|
||||||
],
|
|
||||||
"units": [
|
|
||||||
{
|
|
||||||
"appliesToKey": [
|
|
||||||
"height"
|
|
||||||
],
|
|
||||||
"applicableUnits": [
|
|
||||||
{
|
|
||||||
"canonicalDenomination": "m",
|
|
||||||
"alternativeDenomination": [
|
|
||||||
"meter",
|
|
||||||
"mtr"
|
|
||||||
],
|
|
||||||
"human": {
|
|
||||||
"nl": " meter",
|
|
||||||
"en": " meter"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"eraseInvalidValues": true
|
|
||||||
}
|
|
||||||
]
|
]
|
||||||
}
|
}
|
|
@ -145,82 +145,82 @@
|
||||||
"fr": "Éolienne"
|
"fr": "Éolienne"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"units": [
|
|
||||||
{
|
|
||||||
"appliesToKey": [
|
|
||||||
"generator:output:electricity"
|
|
||||||
],
|
],
|
||||||
"applicableUnits": [
|
"units": [
|
||||||
{
|
{
|
||||||
"canonicalDenomination": "MW",
|
"appliesToKey": [
|
||||||
"alternativeDenomination": [
|
"generator:output:electricity"
|
||||||
"megawatts",
|
|
||||||
"megawatt"
|
|
||||||
],
|
],
|
||||||
"human": {
|
"applicableUnits": [
|
||||||
"en": " megawatts",
|
{
|
||||||
"nl": " megawatt",
|
"canonicalDenomination": "MW",
|
||||||
"fr": " megawatts"
|
"alternativeDenomination": [
|
||||||
}
|
"megawatts",
|
||||||
|
"megawatt"
|
||||||
|
],
|
||||||
|
"human": {
|
||||||
|
"en": " megawatts",
|
||||||
|
"nl": " megawatt",
|
||||||
|
"fr": " megawatts"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"canonicalDenomination": "kW",
|
||||||
|
"alternativeDenomination": [
|
||||||
|
"kilowatts",
|
||||||
|
"kilowatt"
|
||||||
|
],
|
||||||
|
"human": {
|
||||||
|
"en": " kilowatts",
|
||||||
|
"nl": " kilowatt",
|
||||||
|
"fr": " kilowatts"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"canonicalDenomination": "W",
|
||||||
|
"alternativeDenomination": [
|
||||||
|
"watts",
|
||||||
|
"watt"
|
||||||
|
],
|
||||||
|
"human": {
|
||||||
|
"en": " watts",
|
||||||
|
"nl": " watt",
|
||||||
|
"fr": " watts"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"canonicalDenomination": "GW",
|
||||||
|
"alternativeDenomination": [
|
||||||
|
"gigawatts",
|
||||||
|
"gigawatt"
|
||||||
|
],
|
||||||
|
"human": {
|
||||||
|
"en": " gigawatts",
|
||||||
|
"nl": " gigawatt",
|
||||||
|
"fr": " gigawatts"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"eraseInvalidValues": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"canonicalDenomination": "kW",
|
"appliesToKey": [
|
||||||
"alternativeDenomination": [
|
"height",
|
||||||
"kilowatts",
|
"rotor:diameter"
|
||||||
"kilowatt"
|
|
||||||
],
|
],
|
||||||
"human": {
|
"applicableUnits": [
|
||||||
"en": " kilowatts",
|
{
|
||||||
"nl": " kilowatt",
|
"canonicalDenomination": "m",
|
||||||
"fr": " kilowatts"
|
"alternativeDenomination": [
|
||||||
}
|
"meter"
|
||||||
},
|
],
|
||||||
{
|
"human": {
|
||||||
"canonicalDenomination": "W",
|
"en": " meter",
|
||||||
"alternativeDenomination": [
|
"nl": " meter",
|
||||||
"watts",
|
"fr": " mètres"
|
||||||
"watt"
|
}
|
||||||
],
|
}
|
||||||
"human": {
|
]
|
||||||
"en": " watts",
|
|
||||||
"nl": " watt",
|
|
||||||
"fr": " watts"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"canonicalDenomination": "GW",
|
|
||||||
"alternativeDenomination": [
|
|
||||||
"gigawatts",
|
|
||||||
"gigawatt"
|
|
||||||
],
|
|
||||||
"human": {
|
|
||||||
"en": " gigawatts",
|
|
||||||
"nl": " gigawatt",
|
|
||||||
"fr": " gigawatts"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"eraseInvalidValues": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"appliesToKey": [
|
|
||||||
"height",
|
|
||||||
"rotor:diameter"
|
|
||||||
],
|
|
||||||
"applicableUnits": [
|
|
||||||
{
|
|
||||||
"canonicalDenomination": "m",
|
|
||||||
"alternativeDenomination": [
|
|
||||||
"meter"
|
|
||||||
],
|
|
||||||
"human": {
|
|
||||||
"en": " meter",
|
|
||||||
"nl": " meter",
|
|
||||||
"fr": " mètres"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,26 +39,5 @@
|
||||||
"binocular",
|
"binocular",
|
||||||
"observation_tower"
|
"observation_tower"
|
||||||
],
|
],
|
||||||
"hideFromOverview": true,
|
"hideFromOverview": true
|
||||||
"units": [
|
|
||||||
{
|
|
||||||
"appliesToKey": [
|
|
||||||
"height"
|
|
||||||
],
|
|
||||||
"applicableUnits": [
|
|
||||||
{
|
|
||||||
"canonicalDenomination": "m",
|
|
||||||
"alternativeDenomination": [
|
|
||||||
"meter",
|
|
||||||
"mtr"
|
|
||||||
],
|
|
||||||
"human": {
|
|
||||||
"nl": " meter",
|
|
||||||
"en": " meter"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"eraseInvalidValues": true
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
}
|
|
@ -41,7 +41,7 @@ for (const layerConfigJson of themeConfigJson.layers) {
|
||||||
layerConfigJson["source"] = {osmTags: tags}
|
layerConfigJson["source"] = {osmTags: tags}
|
||||||
}
|
}
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
const layerConfig = new LayerConfig(layerConfigJson, AllKnownLayers.sharedUnits, "fix theme", true)
|
const layerConfig = new LayerConfig(layerConfigJson, "fix theme", true)
|
||||||
const images: string[] = Array.from(layerConfig.ExtractImages())
|
const images: string[] = Array.from(layerConfig.ExtractImages())
|
||||||
const remoteImages = images.filter(img => img.startsWith("http"))
|
const remoteImages = images.filter(img => img.startsWith("http"))
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import ScriptUtils from "./ScriptUtils";
|
import ScriptUtils from "./ScriptUtils";
|
||||||
import {writeFileSync} from "fs";
|
import {writeFileSync} from "fs";
|
||||||
import * as licenses from "../assets/generated/license_info.json"
|
import * as licenses from "../assets/generated/license_info.json"
|
||||||
import AllKnownLayers from "../Customizations/AllKnownLayers";
|
|
||||||
import {LayoutConfigJson} from "../Models/ThemeConfig/Json/LayoutConfigJson";
|
import {LayoutConfigJson} from "../Models/ThemeConfig/Json/LayoutConfigJson";
|
||||||
import LayoutConfig from "../Models/ThemeConfig/LayoutConfig";
|
import LayoutConfig from "../Models/ThemeConfig/LayoutConfig";
|
||||||
import {LayerConfigJson} from "../Models/ThemeConfig/Json/LayerConfigJson";
|
import {LayerConfigJson} from "../Models/ThemeConfig/Json/LayerConfigJson";
|
||||||
|
@ -49,7 +48,7 @@ class LayerOverviewUtils {
|
||||||
errorCount.push("Layer " + layerJson.id + "still uses the old 'overpassTags'-format. Please use \"source\": {\"osmTags\": <tags>}' instead of \"overpassTags\": <tags> (note: this isn't your fault, the custom theme generator still spits out the old format)")
|
errorCount.push("Layer " + layerJson.id + "still uses the old 'overpassTags'-format. Please use \"source\": {\"osmTags\": <tags>}' instead of \"overpassTags\": <tags> (note: this isn't your fault, the custom theme generator still spits out the old format)")
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
const layer = new LayerConfig(layerJson, AllKnownLayers.sharedUnits, "test", true)
|
const layer = new LayerConfig(layerJson, "test", true)
|
||||||
const images = Array.from(layer.ExtractImages())
|
const images = Array.from(layer.ExtractImages())
|
||||||
const remoteImages = images.filter(img => img.indexOf("http") == 0)
|
const remoteImages = images.filter(img => img.indexOf("http") == 0)
|
||||||
for (const remoteImage of remoteImages) {
|
for (const remoteImage of remoteImages) {
|
||||||
|
@ -104,7 +103,7 @@ class LayerOverviewUtils {
|
||||||
throw "Duplicate identifier: " + layerFile.parsed.id + " in file " + layerFile.path
|
throw "Duplicate identifier: " + layerFile.parsed.id + " in file " + layerFile.path
|
||||||
}
|
}
|
||||||
layerErrorCount.push(...this.validateLayer(layerFile.parsed, layerFile.path, knownPaths))
|
layerErrorCount.push(...this.validateLayer(layerFile.parsed, layerFile.path, knownPaths))
|
||||||
knownLayerIds.set(layerFile.parsed.id, new LayerConfig(layerFile.parsed, AllKnownLayers.sharedUnits))
|
knownLayerIds.set(layerFile.parsed.id, new LayerConfig(layerFile.parsed))
|
||||||
}
|
}
|
||||||
|
|
||||||
let themeErrorCount = []
|
let themeErrorCount = []
|
||||||
|
@ -114,6 +113,9 @@ class LayerOverviewUtils {
|
||||||
if (typeof themeFile.language === "string") {
|
if (typeof themeFile.language === "string") {
|
||||||
themeErrorCount.push("The theme " + themeFile.id + " has a string as language. Please use a list of strings")
|
themeErrorCount.push("The theme " + themeFile.id + " has a string as language. Please use a list of strings")
|
||||||
}
|
}
|
||||||
|
if (themeFile["units"] !== undefined) {
|
||||||
|
themeErrorCount.push("The theme " + themeFile.id + " has units defined - these should be defined on the layer instead. (Hint: use overrideAll: { '+units': ... }) ")
|
||||||
|
}
|
||||||
for (const layer of themeFile.layers) {
|
for (const layer of themeFile.layers) {
|
||||||
if (typeof layer === "string") {
|
if (typeof layer === "string") {
|
||||||
if (!knownLayerIds.has(layer)) {
|
if (!knownLayerIds.has(layer)) {
|
||||||
|
@ -153,7 +155,6 @@ class LayerOverviewUtils {
|
||||||
themeErrorCount.push("Theme ids should be the same as the name.json, but we got id: " + theme.id + " and filename " + filename + " (" + themePath + ")")
|
themeErrorCount.push("Theme ids should be the same as the name.json, but we got id: " + theme.id + " and filename " + filename + " (" + themePath + ")")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
themeErrorCount.push("Could not parse theme " + themeFile["id"] + "due to", e)
|
themeErrorCount.push("Could not parse theme " + themeFile["id"] + "due to", e)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue