forked from MapComplete/MapComplete
Refactoring: allow to reuse units, move all units into central file
This commit is contained in:
parent
067fb549c1
commit
94e07d5b13
30 changed files with 1495 additions and 1307 deletions
|
@ -137,11 +137,12 @@ export default class DetermineLayout {
|
|||
if (json.layers === undefined && json.tagRenderings !== undefined) {
|
||||
// We got fed a layer instead of a theme
|
||||
const layerConfig = <LayerConfigJson>json
|
||||
const iconTr: string | TagRenderingConfigJson = <any>(
|
||||
layerConfig.pointRendering
|
||||
.map((mr) => mr?.marker?.find((icon) => icon.icon !== undefined)?.icon)
|
||||
.find((i) => i !== undefined)
|
||||
) ?? "bug"
|
||||
const iconTr: string | TagRenderingConfigJson =
|
||||
<any>(
|
||||
layerConfig.pointRendering
|
||||
.map((mr) => mr?.marker?.find((icon) => icon.icon !== undefined)?.icon)
|
||||
.find((i) => i !== undefined)
|
||||
) ?? "bug"
|
||||
const icon = new TagRenderingConfig(iconTr).render.txt
|
||||
json = {
|
||||
id: json.id,
|
||||
|
@ -156,8 +157,8 @@ export default class DetermineLayout {
|
|||
}
|
||||
|
||||
const knownLayersDict = new Map<string, LayerConfigJson>()
|
||||
for (const key in known_layers.layers) {
|
||||
const layer = known_layers.layers[key]
|
||||
for (const key in known_layers["layers"]) {
|
||||
const layer = known_layers["layers"][key]
|
||||
knownLayersDict.set(layer.id, <LayerConfigJson>layer)
|
||||
}
|
||||
const convertState: DesugaringContext = {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { Translation } from "../UI/i18n/Translation"
|
||||
import { Translation, TypedTranslation } from "../UI/i18n/Translation"
|
||||
import { DenominationConfigJson } from "./ThemeConfig/Json/UnitConfigJson"
|
||||
import Translations from "../UI/i18n/Translations"
|
||||
|
||||
|
@ -9,20 +9,39 @@ import Translations from "../UI/i18n/Translations"
|
|||
export class Denomination {
|
||||
public readonly canonical: string
|
||||
public readonly _canonicalSingular: string
|
||||
public readonly useAsDefaultInput: boolean | string[]
|
||||
public readonly useIfNoUnitGiven: boolean | string[]
|
||||
public readonly prefix: boolean
|
||||
public readonly addSpace: boolean
|
||||
public readonly alternativeDenominations: string[]
|
||||
private readonly _human: Translation
|
||||
private readonly _humanSingular?: Translation
|
||||
public readonly human: TypedTranslation<{ quantity: string }>
|
||||
public readonly humanSingular?: Translation
|
||||
|
||||
constructor(json: DenominationConfigJson, useAsDefaultInput: boolean, context: string) {
|
||||
private constructor(
|
||||
canonical: string,
|
||||
_canonicalSingular: string,
|
||||
useIfNoUnitGiven: boolean | string[],
|
||||
prefix: boolean,
|
||||
addSpace: boolean,
|
||||
alternativeDenominations: string[],
|
||||
_human: TypedTranslation<{ quantity: string }>,
|
||||
_humanSingular?: Translation
|
||||
) {
|
||||
this.canonical = canonical
|
||||
this._canonicalSingular = _canonicalSingular
|
||||
this.useIfNoUnitGiven = useIfNoUnitGiven
|
||||
this.prefix = prefix
|
||||
this.addSpace = addSpace
|
||||
this.alternativeDenominations = alternativeDenominations
|
||||
this.human = _human
|
||||
this.humanSingular = _humanSingular
|
||||
}
|
||||
|
||||
public static fromJson(json: DenominationConfigJson, context: string) {
|
||||
context = `${context}.unit(${json.canonicalDenomination})`
|
||||
this.canonical = json.canonicalDenomination.trim()
|
||||
if (this.canonical === undefined) {
|
||||
const canonical = json.canonicalDenomination.trim()
|
||||
if (canonical === undefined) {
|
||||
throw `${context}: this unit has no decent canonical value defined`
|
||||
}
|
||||
this._canonicalSingular = json.canonicalDenominationSingular?.trim()
|
||||
|
||||
json.alternativeDenomination?.forEach((v, i) => {
|
||||
if ((v?.trim() ?? "") === "") {
|
||||
|
@ -30,40 +49,67 @@ export class Denomination {
|
|||
}
|
||||
})
|
||||
|
||||
this.alternativeDenominations = json.alternativeDenomination?.map((v) => v.trim()) ?? []
|
||||
|
||||
if (json["default" /* @code-quality: ignore*/] !== undefined) {
|
||||
throw `${context} uses the old 'default'-key. Use "useIfNoUnitGiven" or "useAsDefaultInput" instead`
|
||||
}
|
||||
this.useIfNoUnitGiven = json.useIfNoUnitGiven
|
||||
this.useAsDefaultInput = useAsDefaultInput ?? json.useIfNoUnitGiven
|
||||
|
||||
this._human = Translations.T(json.human, context + "human")
|
||||
this._humanSingular = Translations.T(json.humanSingular, context + "humanSingular")
|
||||
|
||||
this.prefix = json.prefix ?? false
|
||||
const humanTexts = Translations.T(json.human, context + "human")
|
||||
humanTexts.OnEveryLanguage((text, language) => {
|
||||
if (text.indexOf("{quantity}") < 0) {
|
||||
throw `In denomination: a human text should contain {quantity} (at ${context}.human.${language})`
|
||||
}
|
||||
return text
|
||||
})
|
||||
return new Denomination(
|
||||
canonical,
|
||||
json.canonicalDenominationSingular?.trim(),
|
||||
json.useIfNoUnitGiven,
|
||||
json.prefix ?? false,
|
||||
json.addSpace ?? false,
|
||||
json.alternativeDenomination?.map((v) => v.trim()) ?? [],
|
||||
humanTexts,
|
||||
Translations.T(json.humanSingular, context + "humanSingular")
|
||||
)
|
||||
}
|
||||
|
||||
get human(): Translation {
|
||||
return this._human.Clone()
|
||||
public clone() {
|
||||
return new Denomination(
|
||||
this.canonical,
|
||||
this._canonicalSingular,
|
||||
this.useIfNoUnitGiven,
|
||||
this.prefix,
|
||||
this.addSpace,
|
||||
this.alternativeDenominations,
|
||||
this.human,
|
||||
this.humanSingular
|
||||
)
|
||||
}
|
||||
|
||||
get humanSingular(): Translation {
|
||||
return (this._humanSingular ?? this._human).Clone()
|
||||
public withBlankCanonical() {
|
||||
return new Denomination(
|
||||
"",
|
||||
this._canonicalSingular,
|
||||
this.useIfNoUnitGiven,
|
||||
this.prefix,
|
||||
this.addSpace,
|
||||
[this.canonical, ...this.alternativeDenominations],
|
||||
this.human,
|
||||
this.humanSingular
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a representation of the given value
|
||||
* Create the canonical, human representation of the given value
|
||||
* @param value the value from OSM
|
||||
* @param actAsDefault if set and the value can be parsed as number, will be parsed and trimmed
|
||||
*
|
||||
* const unit = new Denomination({
|
||||
* const unit = Denomination.fromJson({
|
||||
* canonicalDenomination: "m",
|
||||
* alternativeDenomination: ["meter"],
|
||||
* human: {
|
||||
* en: "meter"
|
||||
* en: "{quantity} meter"
|
||||
* }
|
||||
* }, false, "test")
|
||||
* }, "test")
|
||||
* unit.canonicalValue("42m", true) // =>"42 m"
|
||||
* unit.canonicalValue("42", true) // =>"42 m"
|
||||
* unit.canonicalValue("42 m", true) // =>"42 m"
|
||||
|
@ -72,13 +118,13 @@ export class Denomination {
|
|||
* unit.canonicalValue("42", true) // =>"42 m"
|
||||
*
|
||||
* // Should be trimmed if canonical is empty
|
||||
* const unit = new Denomination({
|
||||
* const unit = Denomination.fromJson({
|
||||
* canonicalDenomination: "",
|
||||
* alternativeDenomination: ["meter","m"],
|
||||
* human: {
|
||||
* en: "meter"
|
||||
* en: "{quantity} meter"
|
||||
* }
|
||||
* }, false, "test")
|
||||
* }, "test")
|
||||
* unit.canonicalValue("42m", true) // =>"42"
|
||||
* unit.canonicalValue("42", true) // =>"42"
|
||||
* unit.canonicalValue("42 m", true) // =>"42"
|
||||
|
@ -160,14 +206,4 @@ export class Denomination {
|
|||
|
||||
return null
|
||||
}
|
||||
|
||||
isDefaultDenomination(country: () => string) {
|
||||
if (this.useIfNoUnitGiven === true) {
|
||||
return true
|
||||
}
|
||||
if (this.useIfNoUnitGiven === false) {
|
||||
return false
|
||||
}
|
||||
return this.useIfNoUnitGiven.indexOf(country()) >= 0
|
||||
}
|
||||
}
|
||||
|
|
|
@ -517,7 +517,10 @@ export interface LayerConfigJson {
|
|||
*
|
||||
* group: editing
|
||||
*/
|
||||
units?: UnitConfigJson[]
|
||||
units?: (
|
||||
| UnitConfigJson
|
||||
| Record<string, string | { quantity: string; denominations: string[]; canonical?: string }>
|
||||
)[]
|
||||
|
||||
/**
|
||||
* If set, synchronizes whether or not this layer is enabled.
|
||||
|
|
|
@ -57,12 +57,16 @@
|
|||
*
|
||||
*/
|
||||
export default interface UnitConfigJson {
|
||||
/**
|
||||
* What is quantified? E.g. 'speed', 'length' (including width, diameter, ...), 'electric tension', 'electric current', 'duration'
|
||||
*/
|
||||
quantity?: string
|
||||
/**
|
||||
* Every key from this list will be normalized.
|
||||
*
|
||||
* To render the value properly (with a human readable denomination), use `{canonical(<key>)}`
|
||||
*/
|
||||
appliesToKey: string[]
|
||||
appliesToKey?: string[]
|
||||
/**
|
||||
* If set, invalid values will be erased in the MC application (but not in OSM of course!)
|
||||
* Be careful with setting this
|
||||
|
@ -143,4 +147,11 @@ export interface DenominationConfigJson {
|
|||
* Note that if all values use 'prefix', the dropdown might move to before the text field
|
||||
*/
|
||||
prefix?: boolean
|
||||
|
||||
/**
|
||||
* If set, add a space between the quantity and the denomination.
|
||||
*
|
||||
* E.g.: `50 mph` instad of `50mph`
|
||||
*/
|
||||
addSpace?: boolean
|
||||
}
|
||||
|
|
|
@ -105,8 +105,10 @@ export default class LayerConfig extends WithContextLoader {
|
|||
".units: the 'units'-section should be a list; you probably have an object there"
|
||||
)
|
||||
}
|
||||
this.units = (json.units ?? []).map((unitJson, i) =>
|
||||
Unit.fromJson(unitJson, `${context}.unit[${i}]`)
|
||||
this.units = [].concat(
|
||||
...(json.units ?? []).map((unitJson, i) =>
|
||||
Unit.fromJson(unitJson, `${context}.unit[${i}]`)
|
||||
)
|
||||
)
|
||||
|
||||
if (json.description !== undefined) {
|
||||
|
|
|
@ -3,18 +3,23 @@ import { FixedUiElement } from "../UI/Base/FixedUiElement"
|
|||
import Combine from "../UI/Base/Combine"
|
||||
import { Denomination } from "./Denomination"
|
||||
import UnitConfigJson from "./ThemeConfig/Json/UnitConfigJson"
|
||||
import unit from "../../assets/layers/unit/unit.json"
|
||||
|
||||
export class Unit {
|
||||
private static allUnits = this.initUnits()
|
||||
public readonly appliesToKeys: Set<string>
|
||||
public readonly denominations: Denomination[]
|
||||
public readonly denominationsSorted: Denomination[]
|
||||
public readonly eraseInvalid: boolean
|
||||
public readonly quantity: string
|
||||
|
||||
constructor(
|
||||
quantity: string,
|
||||
appliesToKeys: string[],
|
||||
applicableDenominations: Denomination[],
|
||||
eraseInvalid: boolean
|
||||
) {
|
||||
this.quantity = quantity
|
||||
this.appliesToKeys = new Set(appliesToKeys)
|
||||
this.denominations = applicableDenominations
|
||||
this.eraseInvalid = eraseInvalid
|
||||
|
@ -60,12 +65,24 @@ export class Unit {
|
|||
}
|
||||
}
|
||||
|
||||
static fromJson(
|
||||
json:
|
||||
| UnitConfigJson
|
||||
| Record<string, string | { quantity: string; denominations: string[] }>,
|
||||
ctx: string
|
||||
): Unit[] {
|
||||
if (!json.appliesToKey && !json.quantity) {
|
||||
return this.loadFromLibrary(<any>json, ctx)
|
||||
}
|
||||
return [this.parse(<UnitConfigJson>json, ctx)]
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* // Should detect invalid defaultInput
|
||||
* let threwError = false
|
||||
* try{
|
||||
* Unit.fromJson({
|
||||
* Unit.parse({
|
||||
* appliesToKey: ["length"],
|
||||
* defaultInput: "xcm",
|
||||
* applicableUnits: [
|
||||
|
@ -82,7 +99,7 @@ export class Unit {
|
|||
* threwError // => true
|
||||
*
|
||||
* // Should work
|
||||
* Unit.fromJson({
|
||||
* Unit.parse({
|
||||
* appliesToKey: ["length"],
|
||||
* defaultInput: "xcm",
|
||||
* applicableUnits: [
|
||||
|
@ -98,9 +115,9 @@ export class Unit {
|
|||
* ]
|
||||
* }, "test")
|
||||
*/
|
||||
static fromJson(json: UnitConfigJson, ctx: string) {
|
||||
private static parse(json: UnitConfigJson, ctx: string): Unit {
|
||||
const appliesTo = json.appliesToKey
|
||||
for (let i = 0; i < appliesTo.length; i++) {
|
||||
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`
|
||||
|
@ -112,15 +129,8 @@ export class Unit {
|
|||
}
|
||||
// Some keys do have unit handling
|
||||
|
||||
const applicable = json.applicableUnits.map(
|
||||
(u, i) =>
|
||||
new Denomination(
|
||||
u,
|
||||
u.canonicalDenomination === undefined
|
||||
? undefined
|
||||
: u.canonicalDenomination.trim() === json.defaultInput,
|
||||
`${ctx}.units[${i}]`
|
||||
)
|
||||
const applicable = json.applicableUnits.map((u, i) =>
|
||||
Denomination.fromJson(u, `${ctx}.units[${i}]`)
|
||||
)
|
||||
|
||||
if (
|
||||
|
@ -133,7 +143,85 @@ export class Unit {
|
|||
.map((denom) => denom.canonical)
|
||||
.join(", ")}`
|
||||
}
|
||||
return new Unit(appliesTo, applicable, json.eraseInvalidValues ?? false)
|
||||
return new Unit(
|
||||
json.quantity ?? "",
|
||||
appliesTo,
|
||||
applicable,
|
||||
json.eraseInvalidValues ?? false
|
||||
)
|
||||
}
|
||||
|
||||
private static initUnits(): Map<string, Unit> {
|
||||
const m = new Map<string, Unit>()
|
||||
const units = (<UnitConfigJson[]>unit.units).map((json, i) =>
|
||||
this.parse(json, "unit.json.units." + i)
|
||||
)
|
||||
|
||||
for (const unit of units) {
|
||||
m.set(unit.quantity, unit)
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
private static getFromLibrary(name: string, ctx: string): Unit {
|
||||
const loaded = this.allUnits.get(name)
|
||||
if (loaded === undefined) {
|
||||
throw (
|
||||
"No unit with quantity name " +
|
||||
name +
|
||||
" found (at " +
|
||||
ctx +
|
||||
"). Try one of: " +
|
||||
Array.from(this.allUnits.keys()).join(", ")
|
||||
)
|
||||
}
|
||||
return loaded
|
||||
}
|
||||
|
||||
private static loadFromLibrary(
|
||||
spec: Record<
|
||||
string,
|
||||
string | { quantity: string; denominations: string[]; canonical?: string }
|
||||
>,
|
||||
ctx: string
|
||||
): Unit[] {
|
||||
const units: Unit[] = []
|
||||
for (const key in spec) {
|
||||
const toLoad = spec[key]
|
||||
if (typeof toLoad === "string") {
|
||||
const loaded = this.getFromLibrary(toLoad, ctx)
|
||||
units.push(
|
||||
new Unit(loaded.quantity, [key], loaded.denominations, loaded.eraseInvalid)
|
||||
)
|
||||
continue
|
||||
}
|
||||
|
||||
const loaded = this.getFromLibrary(toLoad.quantity, ctx)
|
||||
const quantity = toLoad.quantity
|
||||
function fetchDenom(d: string): Denomination {
|
||||
const found = loaded.denominations.find(
|
||||
(denom) => denom.canonical.toLowerCase() === d
|
||||
)
|
||||
if (!found) {
|
||||
throw (
|
||||
`Could not find a denomination \`${d}\`for quantity ${quantity} at ${ctx}. Perhaps you meant to use on of ` +
|
||||
loaded.denominations.map((d) => d.canonical).join(", ")
|
||||
)
|
||||
}
|
||||
return found
|
||||
}
|
||||
|
||||
const denoms = toLoad.denominations
|
||||
.map((d) => d.toLowerCase())
|
||||
.map((d) => fetchDenom(d))
|
||||
|
||||
if (toLoad.canonical) {
|
||||
const canonical = fetchDenom(toLoad.canonical)
|
||||
denoms.unshift(canonical.withBlankCanonical())
|
||||
}
|
||||
units.push(new Unit(loaded.quantity, [key], denoms, loaded.eraseInvalid))
|
||||
}
|
||||
return units
|
||||
}
|
||||
|
||||
isApplicableToKey(key: string | undefined): boolean {
|
||||
|
@ -161,47 +249,34 @@ export class Unit {
|
|||
return [undefined, undefined]
|
||||
}
|
||||
|
||||
asHumanLongValue(value: string, country: () => string): BaseUIElement {
|
||||
asHumanLongValue(value: string, country: () => string): BaseUIElement | string {
|
||||
if (value === undefined) {
|
||||
return undefined
|
||||
}
|
||||
const [stripped, denom] = this.findDenomination(value, country)
|
||||
const human = stripped === "1" ? denom?.humanSingular : denom?.human
|
||||
if (stripped === "1") {
|
||||
return denom?.humanSingular ?? stripped
|
||||
}
|
||||
const human = denom?.human
|
||||
if (human === undefined) {
|
||||
return new FixedUiElement(stripped ?? value)
|
||||
return stripped ?? value
|
||||
}
|
||||
|
||||
const elems = denom.prefix ? [human, stripped] : [stripped, human]
|
||||
return new Combine(elems)
|
||||
return human.Subs({ quantity: value })
|
||||
}
|
||||
|
||||
public getDefaultInput(country: () => string | string[]) {
|
||||
console.log("Searching the default denomination for input", country)
|
||||
for (const denomination of this.denominations) {
|
||||
if (denomination.useAsDefaultInput === true) {
|
||||
return denomination
|
||||
}
|
||||
if (
|
||||
denomination.useAsDefaultInput === undefined ||
|
||||
denomination.useAsDefaultInput === false
|
||||
) {
|
||||
continue
|
||||
}
|
||||
let countries: string | string[] = country()
|
||||
if (typeof countries === "string") {
|
||||
countries = countries.split(",")
|
||||
}
|
||||
const denominationCountries: string[] = denomination.useAsDefaultInput
|
||||
if (countries.some((country) => denominationCountries.indexOf(country) >= 0)) {
|
||||
return denomination
|
||||
}
|
||||
public toOsm(value: string, denomination: string) {
|
||||
const denom = this.denominations.find((d) => d.canonical === denomination)
|
||||
const space = denom.addSpace ? " " : ""
|
||||
if (denom.prefix) {
|
||||
return denom.canonical + space + value
|
||||
}
|
||||
return this.denominations[0]
|
||||
return value + space + denom.canonical
|
||||
}
|
||||
|
||||
public getDefaultDenomination(country: () => string) {
|
||||
for (const denomination of this.denominations) {
|
||||
if (denomination.useIfNoUnitGiven === true || denomination.canonical === "") {
|
||||
if (denomination.useIfNoUnitGiven === true) {
|
||||
return denomination
|
||||
}
|
||||
if (
|
||||
|
@ -219,6 +294,11 @@ export class Unit {
|
|||
return denomination
|
||||
}
|
||||
}
|
||||
for (const denomination of this.denominations) {
|
||||
if (denomination.canonical === "") {
|
||||
return denomination
|
||||
}
|
||||
}
|
||||
return this.denominations[0]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,95 +1,102 @@
|
|||
<script lang="ts">
|
||||
import { UIEventSource } from "../../Logic/UIEventSource"
|
||||
import type { ValidatorType } from "./Validators"
|
||||
import Validators from "./Validators"
|
||||
import { ExclamationIcon } from "@rgossiaux/svelte-heroicons/solid"
|
||||
import { Translation } from "../i18n/Translation"
|
||||
import { createEventDispatcher, onDestroy } from "svelte"
|
||||
import { Validator } from "./Validator"
|
||||
import { Unit } from "../../Models/Unit"
|
||||
import UnitInput from "../Popup/UnitInput.svelte"
|
||||
import { Utils } from "../../Utils"
|
||||
import { twMerge } from "tailwind-merge"
|
||||
import { UIEventSource } from "../../Logic/UIEventSource";
|
||||
import type { ValidatorType } from "./Validators";
|
||||
import Validators from "./Validators";
|
||||
import { ExclamationIcon } from "@rgossiaux/svelte-heroicons/solid";
|
||||
import { Translation } from "../i18n/Translation";
|
||||
import { createEventDispatcher, onDestroy } from "svelte";
|
||||
import { Validator } from "./Validator";
|
||||
import { Unit } from "../../Models/Unit";
|
||||
import UnitInput from "../Popup/UnitInput.svelte";
|
||||
import { Utils } from "../../Utils";
|
||||
import { twMerge } from "tailwind-merge";
|
||||
|
||||
export let type: ValidatorType
|
||||
export let feedback: UIEventSource<Translation> | undefined = undefined
|
||||
export let cls: string = undefined
|
||||
export let getCountry: () => string | undefined
|
||||
export let placeholder: string | Translation | undefined
|
||||
export let unit: Unit = undefined
|
||||
export let value: UIEventSource<string>
|
||||
export let type: ValidatorType;
|
||||
export let feedback: UIEventSource<Translation> | undefined = undefined;
|
||||
export let cls: string = undefined;
|
||||
export let getCountry: () => string | undefined;
|
||||
export let placeholder: string | Translation | undefined;
|
||||
export let unit: Unit = undefined;
|
||||
/**
|
||||
* Valid state, exported to the calling component
|
||||
*/
|
||||
export let value: UIEventSource<string | undefined>;
|
||||
/**
|
||||
* Internal state bound to the input element.
|
||||
*
|
||||
* This is only copied to 'value' when appropriate so that no invalid values leak outside;
|
||||
* Additionally, the unit is added when copying
|
||||
*/
|
||||
let _value = new UIEventSource(value.data ?? "")
|
||||
let _value = new UIEventSource(value.data ?? "");
|
||||
|
||||
let validator: Validator = Validators.get(type ?? "string")
|
||||
let validator: Validator = Validators.get(type ?? "string");
|
||||
if (validator === undefined) {
|
||||
console.warn("Didn't find a validator for type", type)
|
||||
console.warn("Didn't find a validator for type", type);
|
||||
}
|
||||
let selectedUnit: UIEventSource<string> = new UIEventSource<string>(undefined)
|
||||
let _placeholder = placeholder ?? validator?.getPlaceholder() ?? type
|
||||
let selectedUnit: UIEventSource<string> = new UIEventSource<string>(undefined);
|
||||
let _placeholder = placeholder ?? validator?.getPlaceholder() ?? type;
|
||||
|
||||
function initValueAndDenom() {
|
||||
if (unit && value.data) {
|
||||
const [v, denom] = unit?.findDenomination(value.data, getCountry)
|
||||
const [v, denom] = unit?.findDenomination(value.data, getCountry);
|
||||
if (denom) {
|
||||
_value.setData(v)
|
||||
selectedUnit.setData(denom.canonical)
|
||||
_value.setData(v);
|
||||
selectedUnit.setData(denom.canonical);
|
||||
} else {
|
||||
_value.setData(value.data ?? "")
|
||||
_value.setData(value.data ?? "");
|
||||
}
|
||||
} else {
|
||||
_value.setData(value.data ?? "")
|
||||
_value.setData(value.data ?? "");
|
||||
}
|
||||
}
|
||||
|
||||
initValueAndDenom()
|
||||
initValueAndDenom();
|
||||
|
||||
$: {
|
||||
// The type changed -> reset some values
|
||||
validator = Validators.get(type ?? "string")
|
||||
validator = Validators.get(type ?? "string");
|
||||
|
||||
_placeholder = placeholder ?? validator?.getPlaceholder() ?? type
|
||||
feedback?.setData(validator?.getFeedback(_value.data, getCountry))
|
||||
_placeholder = placeholder ?? validator?.getPlaceholder() ?? type;
|
||||
feedback?.setData(validator?.getFeedback(_value.data, getCountry));
|
||||
|
||||
initValueAndDenom()
|
||||
initValueAndDenom();
|
||||
}
|
||||
|
||||
function setValues() {
|
||||
// Update the value stores
|
||||
const v = _value.data
|
||||
const v = _value.data;
|
||||
if (!validator?.isValid(v, getCountry) || v === "") {
|
||||
feedback?.setData(validator?.getFeedback(v, getCountry))
|
||||
value.setData("")
|
||||
return
|
||||
feedback?.setData(validator?.getFeedback(v, getCountry));
|
||||
value.setData("");
|
||||
return;
|
||||
}
|
||||
|
||||
if (unit !== undefined && isNaN(Number(v))) {
|
||||
value.setData(undefined)
|
||||
return
|
||||
value.setData(undefined);
|
||||
return;
|
||||
}
|
||||
|
||||
feedback?.setData(undefined)
|
||||
feedback?.setData(undefined);
|
||||
if (selectedUnit.data) {
|
||||
value.setData(v + selectedUnit.data)
|
||||
value.setData(unit.toOsm(v, selectedUnit.data))
|
||||
} else {
|
||||
value.setData(v)
|
||||
value.setData(v);
|
||||
}
|
||||
}
|
||||
|
||||
onDestroy(_value.addCallbackAndRun((_) => setValues()))
|
||||
onDestroy(
|
||||
value.addCallbackAndRunD((fromUpstream) => {
|
||||
if (_value.data !== fromUpstream && fromUpstream !== "") {
|
||||
_value.setData(fromUpstream)
|
||||
}
|
||||
})
|
||||
)
|
||||
onDestroy(selectedUnit.addCallback((_) => setValues()))
|
||||
onDestroy(_value.addCallbackAndRun((_) => setValues()));
|
||||
if (unit === undefined) {
|
||||
onDestroy(
|
||||
value.addCallbackAndRunD((fromUpstream) => {
|
||||
if (_value.data !== fromUpstream && fromUpstream !== "") {
|
||||
_value.setData(fromUpstream);
|
||||
}
|
||||
})
|
||||
);
|
||||
}else{
|
||||
// Handled by the UnitInput
|
||||
}
|
||||
onDestroy(selectedUnit.addCallback((_) => setValues()));
|
||||
if (validator === undefined) {
|
||||
throw (
|
||||
"Not a valid type (no validator found) for type '" +
|
||||
|
@ -102,17 +109,17 @@
|
|||
)
|
||||
.slice(0, 5)
|
||||
.join(", ")
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
const isValid = _value.map((v) => validator?.isValid(v, getCountry) ?? true)
|
||||
const isValid = _value.map((v) => validator?.isValid(v, getCountry) ?? true);
|
||||
|
||||
let htmlElem: HTMLInputElement
|
||||
let htmlElem: HTMLInputElement;
|
||||
|
||||
let dispatch = createEventDispatcher<{ selected; submit }>()
|
||||
let dispatch = createEventDispatcher<{ selected; submit }>();
|
||||
$: {
|
||||
if (htmlElem !== undefined) {
|
||||
htmlElem.onfocus = () => dispatch("selected")
|
||||
htmlElem.onfocus = () => dispatch("selected");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -121,9 +128,9 @@
|
|||
*/
|
||||
function sendSubmit() {
|
||||
if (feedback?.data) {
|
||||
console.log("Not sending a submit as there is feedback")
|
||||
console.log("Not sending a submit as there is feedback");
|
||||
}
|
||||
dispatch("submit")
|
||||
dispatch("submit");
|
||||
}
|
||||
</script>
|
||||
|
||||
|
@ -150,7 +157,7 @@
|
|||
{/if}
|
||||
|
||||
{#if unit !== undefined}
|
||||
<UnitInput {unit} {selectedUnit} textValue={_value} upstreamValue={value} />
|
||||
<UnitInput {unit} {selectedUnit} textValue={_value} upstreamValue={value} {getCountry} />
|
||||
{/if}
|
||||
</form>
|
||||
{/if}
|
||||
|
|
|
@ -1,56 +1,67 @@
|
|||
<script lang="ts">
|
||||
import { Unit } from "../../Models/Unit"
|
||||
import { Store, UIEventSource } from "../../Logic/UIEventSource"
|
||||
import Tr from "../Base/Tr.svelte"
|
||||
import { onDestroy } from "svelte"
|
||||
import { Unit } from "../../Models/Unit";
|
||||
import { Store, UIEventSource } from "../../Logic/UIEventSource";
|
||||
import Tr from "../Base/Tr.svelte";
|
||||
import { onDestroy, onMount } from "svelte";
|
||||
import { Denomination } from "../../Models/Denomination";
|
||||
|
||||
export let unit: Unit
|
||||
export let unit: Unit;
|
||||
|
||||
/**
|
||||
* The current value of the input field
|
||||
* Not necessarily a correct number
|
||||
* Not necessarily a correct number, should not contain the denomination
|
||||
*/
|
||||
export let textValue: UIEventSource<string>
|
||||
export let textValue: UIEventSource<string>;
|
||||
/**
|
||||
* The actual _valid_ value that is upstreamed
|
||||
* The actual _valid_ value that is upstreamed, including the denomination
|
||||
*/
|
||||
export let upstreamValue: Store<string>
|
||||
export let upstreamValue: Store<string>;
|
||||
|
||||
let isSingle: Store<boolean> = textValue.map((v) => Number(v) === 1)
|
||||
let isSingle: Store<boolean> = textValue.map((v) => Number(v) === 1);
|
||||
|
||||
export let selectedUnit: UIEventSource<string> = new UIEventSource<string>(undefined)
|
||||
export let getCountry = () => "be"
|
||||
console.log("Unit", unit)
|
||||
export let selectedUnit: UIEventSource<string> = new UIEventSource<string>(undefined);
|
||||
export let getCountry = () => "?";
|
||||
|
||||
onMount(() => {
|
||||
console.log("Setting selected unit based on country", getCountry(), upstreamValue.data)
|
||||
if(upstreamValue.data === undefined || upstreamValue.data === ""){
|
||||
// Init the selected unit
|
||||
let denomination: Denomination = unit.getDefaultDenomination(getCountry);
|
||||
console.log("Found denom", denomination.canonical)
|
||||
selectedUnit.setData(denomination.canonical)
|
||||
}
|
||||
})
|
||||
|
||||
onDestroy(
|
||||
upstreamValue.addCallbackAndRun((v) => {
|
||||
if (v === undefined) {
|
||||
if (!selectedUnit.data) {
|
||||
selectedUnit.setData(unit.getDefaultDenomination(getCountry).canonical)
|
||||
}
|
||||
if(v === undefined || v === ""){
|
||||
return
|
||||
}
|
||||
const selected = unit.findDenomination(v, getCountry)
|
||||
if (selected === undefined) {
|
||||
selectedUnit.setData(unit.getDefaultDenomination(getCountry).canonical)
|
||||
return
|
||||
let denomination: Denomination = unit.getDefaultDenomination(getCountry);
|
||||
const selected = unit.findDenomination(v, getCountry);
|
||||
if(selected){
|
||||
denomination = selected[1];
|
||||
}
|
||||
const [value, denomination] = selected
|
||||
selectedUnit.setData(denomination.canonical)
|
||||
return
|
||||
selectedUnit.setData(denomination.canonical);
|
||||
})
|
||||
)
|
||||
);
|
||||
|
||||
onDestroy(
|
||||
textValue.addCallbackAndRunD((v) => {
|
||||
// Fallback in case that the user manually types a denomination
|
||||
const [value, denomination] = unit.findDenomination(v, getCountry)
|
||||
const [value, denomination] = unit.findDenomination(v, getCountry);
|
||||
if (value === undefined || denomination === undefined) {
|
||||
return
|
||||
return;
|
||||
}
|
||||
textValue.setData(value)
|
||||
selectedUnit.setData(denomination.canonical)
|
||||
if(value === v){
|
||||
// The input value actually didn't have a denomination typed out - so lets ignore this one
|
||||
// If a denomination is given, it is the default value anyway
|
||||
return;
|
||||
}
|
||||
textValue.setData(value);
|
||||
selectedUnit.setData(denomination.canonical);
|
||||
})
|
||||
)
|
||||
);
|
||||
</script>
|
||||
|
||||
<select bind:value={$selectedUnit}>
|
||||
|
@ -59,7 +70,7 @@
|
|||
{#if $isSingle}
|
||||
<Tr t={denom.humanSingular} />
|
||||
{:else}
|
||||
<Tr t={denom.human} />
|
||||
<Tr t={denom.human.Subs({quantity: ""})} />
|
||||
{/if}
|
||||
</option>
|
||||
{/each}
|
||||
|
|
|
@ -83,6 +83,7 @@ import NearbyImages from "./Image/NearbyImages.svelte"
|
|||
import NearbyImagesCollapsed from "./Image/NearbyImagesCollapsed.svelte"
|
||||
import { svelte } from "@sveltejs/vite-plugin-svelte"
|
||||
import MoveWizard from "./Popup/MoveWizard.svelte"
|
||||
import { Unit } from "../Models/Unit"
|
||||
|
||||
class NearbyImageVis implements SpecialVisualization {
|
||||
// Class must be in SpecialVisualisations due to weird cyclical import that breaks the tests
|
||||
|
@ -890,7 +891,7 @@ export default class SpecialVisualizations {
|
|||
if (value === undefined) {
|
||||
return undefined
|
||||
}
|
||||
const allUnits = [].concat(
|
||||
const allUnits: Unit[] = [].concat(
|
||||
...(state?.layout?.layers?.map((lyr) => lyr.units) ?? [])
|
||||
)
|
||||
const unit = allUnits.filter((unit) =>
|
||||
|
@ -899,7 +900,9 @@ export default class SpecialVisualizations {
|
|||
if (unit === undefined) {
|
||||
return value
|
||||
}
|
||||
return unit.asHumanLongValue(value)
|
||||
const getCountry = () => tagSource.data._country
|
||||
const [v, denom] = unit.findDenomination(value, getCountry)
|
||||
return unit.asHumanLongValue(v, getCountry)
|
||||
})
|
||||
)
|
||||
},
|
||||
|
|
|
@ -131,7 +131,7 @@ export default class Translations {
|
|||
}
|
||||
|
||||
static isProbablyATranslation(transl: any) {
|
||||
if (typeof transl !== "object") {
|
||||
if (!transl || typeof transl !== "object") {
|
||||
return false
|
||||
}
|
||||
if (Object.keys(transl).length == 0) {
|
||||
|
|
|
@ -1081,7 +1081,19 @@
|
|||
"items": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/TagRenderingConfigJson"
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/TagRenderingConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
|
@ -1103,7 +1115,16 @@
|
|||
"maxItems": 1
|
||||
}
|
||||
],
|
||||
"description": "Small icons shown next to the title.\nIf not specified, the OsmLink and wikipedia links will be used by default.\nUse an empty array to hide them.\nNote that \"defaults\" will insert all the default titleIcons (which are added automatically)"
|
||||
"description": "Small icons shown next to the title.\nIf not specified, the OsmLink and wikipedia links will be used by default.\nUse an empty array to hide them.\nNote that \"defaults\" will insert all the default titleIcons (which are added automatically)\nUse `auto:<tagrenderingId>` to automatically create an icon based on a tagRendering which has icons"
|
||||
},
|
||||
{
|
||||
"path": [
|
||||
"titleIcons"
|
||||
],
|
||||
"required": false,
|
||||
"hints": {},
|
||||
"type": "object",
|
||||
"description": "A TagRenderingConfigJson is a single piece of code which converts one ore more tags into a HTML-snippet.\nFor an _editable_ tagRendering, use 'QuestionableTagRenderingConfigJson' instead, which extends this one"
|
||||
},
|
||||
{
|
||||
"path": [
|
||||
|
@ -10380,6 +10401,10 @@
|
|||
"if": "value=tree_node",
|
||||
"then": "tree_node - A layer showing trees"
|
||||
},
|
||||
{
|
||||
"if": "value=unit",
|
||||
"then": "unit - Library layer with all common units"
|
||||
},
|
||||
{
|
||||
"if": "value=usersettings",
|
||||
"then": "usersettings - A special layer which is not meant to be shown on a map, but which is used to set user settings"
|
||||
|
@ -10447,7 +10472,7 @@
|
|||
"hints": {
|
||||
"typehint": "tagrendering[]",
|
||||
"group": "tagrenderings",
|
||||
"question": "Edit this attribute showing piece/question"
|
||||
"question": "Edit this way this attributed is displayed or queried"
|
||||
},
|
||||
"type": [
|
||||
{
|
||||
|
@ -15969,21 +15994,20 @@
|
|||
},
|
||||
{
|
||||
"path": [
|
||||
"units"
|
||||
"units",
|
||||
"quantity"
|
||||
],
|
||||
"required": false,
|
||||
"hints": {
|
||||
"default": "ult: true,"
|
||||
},
|
||||
"type": "object",
|
||||
"description": "In some cases, a value is represented in a certain unit (such as meters for heigt/distance/..., km/h for speed, ...)\nSometimes, multiple denominations are possible (e.g. km/h vs mile/h; megawatt vs kilowatt vs gigawatt for power generators, ...)\nThis brings in some troubles, as there are multiple ways to write it (no denomitation, 'm' vs 'meter' 'metre', ...)\nNot only do we want to write consistent data to OSM, we also want to present this consistently to the user.\nThis is handled by defining units.\n# Rendering\nTo render a value with long (human) denomination, use {canonical(key)}\n# Usage\nFirst of all, you define which keys have units applied, for example:\n```\nunits: [\n appliesTo: [\"maxspeed\", \"maxspeed:hgv\", \"maxspeed:bus\"]\n applicableUnits: [\n ...\n ]\n]\n```\nApplicableUnits defines which is the canonical extension, how it is presented to the user, ...:\n```\napplicableUnits: [\n{\n canonicalDenomination: \"km/h\",\n alternativeDenomination: [\"km/u\", \"kmh\", \"kph\"]\n human: {\n en: \"kilometer/hour\",\n nl: \"kilometer/uur\"\n },\n humanShort: {\n en: \"km/h\",\n nl: \"km/u\"\n }\n},\n{\n canoncialDenomination: \"mph\",\n ... similar for miles an hour ...\n}\n]\n```\nIf this is defined, then every key which the denominations apply to (`maxspeed`, `maxspeed:hgv` and `maxspeed:bus`) will be rewritten at the metatagging stage:\nevery value will be parsed and the canonical extension will be added add presented to the other parts of the code.\nAlso, if a freeform text field is used, an extra dropdown with applicable denominations will be given"
|
||||
"hints": {},
|
||||
"type": "string",
|
||||
"description": "What is quantified? E.g. 'speed', 'length' (including width, diameter, ...), 'electric tension', 'electric current', 'duration'"
|
||||
},
|
||||
{
|
||||
"path": [
|
||||
"units",
|
||||
"appliesToKey"
|
||||
],
|
||||
"required": true,
|
||||
"required": false,
|
||||
"hints": {},
|
||||
"type": "array",
|
||||
"description": "Every key from this list will be normalized.\nTo render the value properly (with a human readable denomination), use `{canonical(<key>)}`"
|
||||
|
@ -16109,6 +16133,17 @@
|
|||
"type": "boolean",
|
||||
"description": "If set, then the canonical value will be prefixed instead, e.g. for '€'\nNote that if all values use 'prefix', the dropdown might move to before the text field"
|
||||
},
|
||||
{
|
||||
"path": [
|
||||
"units",
|
||||
"applicableUnits",
|
||||
"addSpace"
|
||||
],
|
||||
"required": false,
|
||||
"hints": {},
|
||||
"type": "boolean",
|
||||
"description": "If set, add a space between the quantity and the denomination.\nE.g.: `50 mph` instad of `50mph`"
|
||||
},
|
||||
{
|
||||
"path": [
|
||||
"units",
|
||||
|
|
|
@ -832,6 +832,10 @@
|
|||
"if": "value=tree_node",
|
||||
"then": "tree_node - A layer showing trees"
|
||||
},
|
||||
{
|
||||
"if": "value=unit",
|
||||
"then": "unit - Library layer with all common units"
|
||||
},
|
||||
{
|
||||
"if": "value=usersettings",
|
||||
"then": "usersettings - A special layer which is not meant to be shown on a map, but which is used to set user settings"
|
||||
|
@ -1183,14 +1187,26 @@
|
|||
"type": "boolean"
|
||||
},
|
||||
"titleIcons": {
|
||||
"description": "Small icons shown next to the title.\nIf not specified, the OsmLink and wikipedia links will be used by default.\nUse an empty array to hide them.\nNote that \"defaults\" will insert all the default titleIcons (which are added automatically)\n\nType: icon[]\ngroup: infobox",
|
||||
"description": "Small icons shown next to the title.\nIf not specified, the OsmLink and wikipedia links will be used by default.\nUse an empty array to hide them.\nNote that \"defaults\" will insert all the default titleIcons (which are added automatically)\n\nUse `auto:<tagrenderingId>` to automatically create an icon based on a tagRendering which has icons\n\nType: icon[]\ngroup: infobox",
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "array",
|
||||
"items": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/TagRenderingConfigJson"
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/TagRenderingConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
|
@ -1300,7 +1316,7 @@
|
|||
}
|
||||
},
|
||||
"tagRenderings": {
|
||||
"description": "question: Edit this attribute showing piece/question\n\nA tag rendering is a block that either shows the known value or asks a question.\n\nRefer to the class `TagRenderingConfigJson` to see the possibilities.\n\nNote that we can also use a string here - where the string refers to a tag rendering defined in `assets/questions/questions.json`,\nwhere a few very general questions are defined e.g. website, phone number, ...\nFurthermore, _all_ the questions of another layer can be reused with `otherlayer.*`\nIf you need only a single of the tagRenderings, use `otherlayer.tagrenderingId`\nIf one or more questions have a 'group' or 'label' set, select all the entries with the corresponding group or label with `otherlayer.*group`\nRemark: if a tagRendering is 'lent' from another layer, the 'source'-tags are copied and added as condition.\nIf they are not wanted, remove them with an override\n\nA special value is 'questions', which indicates the location of the questions box. If not specified, it'll be appended to the bottom of the featureInfobox.\n\nAt last, one can define a group of renderings where parts of all strings will be replaced by multiple other strings.\nThis is mainly create questions for a 'left' and a 'right' side of the road.\nThese will be grouped and questions will be asked together\n\ntype: tagrendering[]\ngroup: tagrenderings",
|
||||
"description": "question: Edit this way this attributed is displayed or queried\n\nA tag rendering is a block that either shows the known value or asks a question.\n\nRefer to the class `TagRenderingConfigJson` to see the possibilities.\n\nNote that we can also use a string here - where the string refers to a tag rendering defined in `assets/questions/questions.json`,\nwhere a few very general questions are defined e.g. website, phone number, ...\nFurthermore, _all_ the questions of another layer can be reused with `otherlayer.*`\nIf you need only a single of the tagRenderings, use `otherlayer.tagrenderingId`\nIf one or more questions have a 'group' or 'label' set, select all the entries with the corresponding group or label with `otherlayer.*group`\nRemark: if a tagRendering is 'lent' from another layer, the 'source'-tags are copied and added as condition.\nIf they are not wanted, remove them with an override\n\nA special value is 'questions', which indicates the location of the questions box. If not specified, it'll be appended to the bottom of the featureInfobox.\n\nAt last, one can define a group of renderings where parts of all strings will be replaced by multiple other strings.\nThis is mainly create questions for a 'left' and a 'right' side of the road.\nThese will be grouped and questions will be asked together\n\ntype: tagrendering[]\ngroup: tagrenderings",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"anyOf": [
|
||||
|
@ -1706,7 +1722,14 @@
|
|||
"units": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/default_2"
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/default_2"
|
||||
},
|
||||
{
|
||||
"$ref": "#/definitions/Record<string,string|{quantity:string;denominations:string[];}>"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"syncSelection": {
|
||||
|
@ -2889,7 +2912,19 @@
|
|||
"items": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/TagRenderingConfigJson"
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/TagRenderingConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
|
@ -2911,7 +2946,17 @@
|
|||
"maxItems": 1
|
||||
}
|
||||
],
|
||||
"description": "Small icons shown next to the title.\nIf not specified, the OsmLink and wikipedia links will be used by default.\nUse an empty array to hide them.\nNote that \"defaults\" will insert all the default titleIcons (which are added automatically)"
|
||||
"description": "Small icons shown next to the title.\nIf not specified, the OsmLink and wikipedia links will be used by default.\nUse an empty array to hide them.\nNote that \"defaults\" will insert all the default titleIcons (which are added automatically)\nUse `auto:<tagrenderingId>` to automatically create an icon based on a tagRendering which has icons"
|
||||
},
|
||||
{
|
||||
"path": [
|
||||
"layers",
|
||||
"titleIcons"
|
||||
],
|
||||
"required": false,
|
||||
"hints": {},
|
||||
"type": "object",
|
||||
"description": "A TagRenderingConfigJson is a single piece of code which converts one ore more tags into a HTML-snippet.\nFor an _editable_ tagRendering, use 'QuestionableTagRenderingConfigJson' instead, which extends this one"
|
||||
},
|
||||
{
|
||||
"path": [
|
||||
|
@ -12476,6 +12521,10 @@
|
|||
"if": "value=tree_node",
|
||||
"then": "tree_node - A layer showing trees"
|
||||
},
|
||||
{
|
||||
"if": "value=unit",
|
||||
"then": "unit - Library layer with all common units"
|
||||
},
|
||||
{
|
||||
"if": "value=usersettings",
|
||||
"then": "usersettings - A special layer which is not meant to be shown on a map, but which is used to set user settings"
|
||||
|
@ -12545,7 +12594,7 @@
|
|||
"hints": {
|
||||
"typehint": "tagrendering[]",
|
||||
"group": "tagrenderings",
|
||||
"question": "Edit this attribute showing piece/question"
|
||||
"question": "Edit this way this attributed is displayed or queried"
|
||||
},
|
||||
"type": [
|
||||
{
|
||||
|
@ -18273,14 +18322,13 @@
|
|||
{
|
||||
"path": [
|
||||
"layers",
|
||||
"units"
|
||||
"units",
|
||||
"quantity"
|
||||
],
|
||||
"required": false,
|
||||
"hints": {
|
||||
"default": "ult: true,"
|
||||
},
|
||||
"type": "object",
|
||||
"description": "In some cases, a value is represented in a certain unit (such as meters for heigt/distance/..., km/h for speed, ...)\nSometimes, multiple denominations are possible (e.g. km/h vs mile/h; megawatt vs kilowatt vs gigawatt for power generators, ...)\nThis brings in some troubles, as there are multiple ways to write it (no denomitation, 'm' vs 'meter' 'metre', ...)\nNot only do we want to write consistent data to OSM, we also want to present this consistently to the user.\nThis is handled by defining units.\n# Rendering\nTo render a value with long (human) denomination, use {canonical(key)}\n# Usage\nFirst of all, you define which keys have units applied, for example:\n```\nunits: [\n appliesTo: [\"maxspeed\", \"maxspeed:hgv\", \"maxspeed:bus\"]\n applicableUnits: [\n ...\n ]\n]\n```\nApplicableUnits defines which is the canonical extension, how it is presented to the user, ...:\n```\napplicableUnits: [\n{\n canonicalDenomination: \"km/h\",\n alternativeDenomination: [\"km/u\", \"kmh\", \"kph\"]\n human: {\n en: \"kilometer/hour\",\n nl: \"kilometer/uur\"\n },\n humanShort: {\n en: \"km/h\",\n nl: \"km/u\"\n }\n},\n{\n canoncialDenomination: \"mph\",\n ... similar for miles an hour ...\n}\n]\n```\nIf this is defined, then every key which the denominations apply to (`maxspeed`, `maxspeed:hgv` and `maxspeed:bus`) will be rewritten at the metatagging stage:\nevery value will be parsed and the canonical extension will be added add presented to the other parts of the code.\nAlso, if a freeform text field is used, an extra dropdown with applicable denominations will be given"
|
||||
"hints": {},
|
||||
"type": "string",
|
||||
"description": "What is quantified? E.g. 'speed', 'length' (including width, diameter, ...), 'electric tension', 'electric current', 'duration'"
|
||||
},
|
||||
{
|
||||
"path": [
|
||||
|
@ -18288,7 +18336,7 @@
|
|||
"units",
|
||||
"appliesToKey"
|
||||
],
|
||||
"required": true,
|
||||
"required": false,
|
||||
"hints": {},
|
||||
"type": "array",
|
||||
"description": "Every key from this list will be normalized.\nTo render the value properly (with a human readable denomination), use `{canonical(<key>)}`"
|
||||
|
@ -18423,6 +18471,18 @@
|
|||
"type": "boolean",
|
||||
"description": "If set, then the canonical value will be prefixed instead, e.g. for '€'\nNote that if all values use 'prefix', the dropdown might move to before the text field"
|
||||
},
|
||||
{
|
||||
"path": [
|
||||
"layers",
|
||||
"units",
|
||||
"applicableUnits",
|
||||
"addSpace"
|
||||
],
|
||||
"required": false,
|
||||
"hints": {},
|
||||
"type": "boolean",
|
||||
"description": "If set, add a space between the quantity and the denomination.\nE.g.: `50 mph` instad of `50mph`"
|
||||
},
|
||||
{
|
||||
"path": [
|
||||
"layers",
|
||||
|
@ -19646,7 +19706,19 @@
|
|||
"items": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/TagRenderingConfigJson"
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/TagRenderingConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
|
@ -19668,7 +19740,18 @@
|
|||
"maxItems": 1
|
||||
}
|
||||
],
|
||||
"description": "Small icons shown next to the title.\nIf not specified, the OsmLink and wikipedia links will be used by default.\nUse an empty array to hide them.\nNote that \"defaults\" will insert all the default titleIcons (which are added automatically)"
|
||||
"description": "Small icons shown next to the title.\nIf not specified, the OsmLink and wikipedia links will be used by default.\nUse an empty array to hide them.\nNote that \"defaults\" will insert all the default titleIcons (which are added automatically)\nUse `auto:<tagrenderingId>` to automatically create an icon based on a tagRendering which has icons"
|
||||
},
|
||||
{
|
||||
"path": [
|
||||
"layers",
|
||||
"override",
|
||||
"titleIcons"
|
||||
],
|
||||
"required": false,
|
||||
"hints": {},
|
||||
"type": "object",
|
||||
"description": "A TagRenderingConfigJson is a single piece of code which converts one ore more tags into a HTML-snippet.\nFor an _editable_ tagRendering, use 'QuestionableTagRenderingConfigJson' instead, which extends this one"
|
||||
},
|
||||
{
|
||||
"path": [
|
||||
|
@ -29521,6 +29604,10 @@
|
|||
"if": "value=tree_node",
|
||||
"then": "tree_node - A layer showing trees"
|
||||
},
|
||||
{
|
||||
"if": "value=unit",
|
||||
"then": "unit - Library layer with all common units"
|
||||
},
|
||||
{
|
||||
"if": "value=usersettings",
|
||||
"then": "usersettings - A special layer which is not meant to be shown on a map, but which is used to set user settings"
|
||||
|
@ -29592,7 +29679,7 @@
|
|||
"hints": {
|
||||
"typehint": "tagrendering[]",
|
||||
"group": "tagrenderings",
|
||||
"question": "Edit this attribute showing piece/question"
|
||||
"question": "Edit this way this attributed is displayed or queried"
|
||||
},
|
||||
"type": [
|
||||
{
|
||||
|
@ -35526,14 +35613,13 @@
|
|||
"path": [
|
||||
"layers",
|
||||
"override",
|
||||
"units"
|
||||
"units",
|
||||
"quantity"
|
||||
],
|
||||
"required": false,
|
||||
"hints": {
|
||||
"default": "ult: true,"
|
||||
},
|
||||
"type": "object",
|
||||
"description": "In some cases, a value is represented in a certain unit (such as meters for heigt/distance/..., km/h for speed, ...)\nSometimes, multiple denominations are possible (e.g. km/h vs mile/h; megawatt vs kilowatt vs gigawatt for power generators, ...)\nThis brings in some troubles, as there are multiple ways to write it (no denomitation, 'm' vs 'meter' 'metre', ...)\nNot only do we want to write consistent data to OSM, we also want to present this consistently to the user.\nThis is handled by defining units.\n# Rendering\nTo render a value with long (human) denomination, use {canonical(key)}\n# Usage\nFirst of all, you define which keys have units applied, for example:\n```\nunits: [\n appliesTo: [\"maxspeed\", \"maxspeed:hgv\", \"maxspeed:bus\"]\n applicableUnits: [\n ...\n ]\n]\n```\nApplicableUnits defines which is the canonical extension, how it is presented to the user, ...:\n```\napplicableUnits: [\n{\n canonicalDenomination: \"km/h\",\n alternativeDenomination: [\"km/u\", \"kmh\", \"kph\"]\n human: {\n en: \"kilometer/hour\",\n nl: \"kilometer/uur\"\n },\n humanShort: {\n en: \"km/h\",\n nl: \"km/u\"\n }\n},\n{\n canoncialDenomination: \"mph\",\n ... similar for miles an hour ...\n}\n]\n```\nIf this is defined, then every key which the denominations apply to (`maxspeed`, `maxspeed:hgv` and `maxspeed:bus`) will be rewritten at the metatagging stage:\nevery value will be parsed and the canonical extension will be added add presented to the other parts of the code.\nAlso, if a freeform text field is used, an extra dropdown with applicable denominations will be given"
|
||||
"hints": {},
|
||||
"type": "string",
|
||||
"description": "What is quantified? E.g. 'speed', 'length' (including width, diameter, ...), 'electric tension', 'electric current', 'duration'"
|
||||
},
|
||||
{
|
||||
"path": [
|
||||
|
@ -35542,7 +35628,7 @@
|
|||
"units",
|
||||
"appliesToKey"
|
||||
],
|
||||
"required": true,
|
||||
"required": false,
|
||||
"hints": {},
|
||||
"type": "array",
|
||||
"description": "Every key from this list will be normalized.\nTo render the value properly (with a human readable denomination), use `{canonical(<key>)}`"
|
||||
|
@ -35686,6 +35772,19 @@
|
|||
"type": "boolean",
|
||||
"description": "If set, then the canonical value will be prefixed instead, e.g. for '€'\nNote that if all values use 'prefix', the dropdown might move to before the text field"
|
||||
},
|
||||
{
|
||||
"path": [
|
||||
"layers",
|
||||
"override",
|
||||
"units",
|
||||
"applicableUnits",
|
||||
"addSpace"
|
||||
],
|
||||
"required": false,
|
||||
"hints": {},
|
||||
"type": "boolean",
|
||||
"description": "If set, add a space between the quantity and the denomination.\nE.g.: `50 mph` instad of `50mph`"
|
||||
},
|
||||
{
|
||||
"path": [
|
||||
"layers",
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue