Move 'defaultInputUnit' into Unit, away from denomination

This commit is contained in:
Pieter Vander Vennet 2023-01-02 02:35:40 +01:00
parent 03057b2eff
commit 67b5a33f0b
9 changed files with 135 additions and 84 deletions

View file

@ -15,7 +15,7 @@ export class Denomination {
private readonly _human: Translation private readonly _human: Translation
private readonly _humanSingular?: Translation private readonly _humanSingular?: Translation
constructor(json: DenominationConfigJson, context: string) { constructor(json: DenominationConfigJson, useAsDefaultInput: boolean, 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) {
@ -35,7 +35,7 @@ export class Denomination {
throw `${context} uses the old 'default'-key. Use "useIfNoUnitGiven" or "useAsDefaultInput" instead` throw `${context} uses the old 'default'-key. Use "useIfNoUnitGiven" or "useAsDefaultInput" instead`
} }
this.useIfNoUnitGiven = json.useIfNoUnitGiven this.useIfNoUnitGiven = json.useIfNoUnitGiven
this.useAsDefaultInput = json.useAsDefaultInput ?? json.useIfNoUnitGiven this.useAsDefaultInput = useAsDefaultInput ?? json.useIfNoUnitGiven
this._human = Translations.T(json.human, context + "human") this._human = Translations.T(json.human, context + "human")
this._humanSingular = Translations.T(json.humanSingular, context + "humanSingular") this._humanSingular = Translations.T(json.humanSingular, context + "humanSingular")
@ -69,7 +69,7 @@ export class Denomination {
* human: { * human: {
* en: "meter" * en: "meter"
* } * }
* }, "test") * }, false, "test")
* unit.canonicalValue("42m", true) // =>"42 m" * unit.canonicalValue("42m", true) // =>"42 m"
* unit.canonicalValue("42", true) // =>"42 m" * unit.canonicalValue("42", true) // =>"42 m"
* unit.canonicalValue("42 m", true) // =>"42 m" * unit.canonicalValue("42 m", true) // =>"42 m"
@ -84,7 +84,7 @@ export class Denomination {
* human: { * human: {
* en: "meter" * en: "meter"
* } * }
* }, "test") * }, false, "test")
* unit.canonicalValue("42m", true) // =>"42" * unit.canonicalValue("42m", true) // =>"42"
* unit.canonicalValue("42", true) // =>"42" * unit.canonicalValue("42", true) // =>"42"
* unit.canonicalValue("42 m", true) // =>"42" * unit.canonicalValue("42 m", true) // =>"42"

View file

@ -389,62 +389,7 @@ 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, ...) * @see UnitConfigJson
*
* 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[] units?: UnitConfigJson[]

View file

@ -1,3 +1,61 @@
/**
* 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
*
*/
export default interface UnitConfigJson { export default interface UnitConfigJson {
/** /**
* Every key from this list will be normalized. * Every key from this list will be normalized.
@ -11,9 +69,19 @@ export default interface UnitConfigJson {
*/ */
eraseInvalidValues?: boolean eraseInvalidValues?: boolean
/** /**
* The possible denominations * The possible denominations for this unit.
* For length, denominations could be "meter", "kilometer", "miles", "foot"
*/ */
applicableUnits: DenominationConfigJson[] applicableUnits: DenominationConfigJson[]
/**
* In some cases, the default denomination is not the most user friendly to input.
* E.g., when measuring kerb heights, it is illogical to ask contributors to input an amount in meters.
*
* When a default input method should be used, this can be specified by setting the canonical denomination here, e.g.
* `defaultInput: "cm"`. This must be a denomination which appears in the applicableUnits
*/
defaultInput?: string
} }
export interface DenominationConfigJson { export interface DenominationConfigJson {
@ -28,12 +96,6 @@ export interface DenominationConfigJson {
*/ */
useIfNoUnitGiven?: boolean | string[] useIfNoUnitGiven?: boolean | string[]
/**
* Use this value as default denomination when the user inputs a value (e.g. to force using 'centimeters' instead of 'meters' by default).
* If unset for all values, this will use 'useIfNoUnitGiven'. If at least one denomination has this set, this will default to false
*/
useAsDefaultInput?: boolean | string[]
/** /**
* The canonical value for this denomination which will be added to the value in OSM. * The canonical value for this denomination which will be added to the value in OSM.
* e.g. "m" for meters * e.g. "m" for meters
@ -46,12 +108,15 @@ export interface DenominationConfigJson {
/** /**
* The canonical denomination in the case that the unit is precisely '1'. * The canonical denomination in the case that the unit is precisely '1'.
* Used for display purposes * Used for display purposes only.
*
* E.g.: for duration of something in minutes: `2 minutes` but `1 minute`; the `minute` goes here
*/ */
canonicalDenominationSingular?: string canonicalDenominationSingular?: string
/** /**
* A list of alternative values which can occur in the OSM database - used for parsing. * A list of alternative values which can occur in the OSM database - used for parsing.
* E.g.: while 'm' is canonical, `meter`, `mtrs`, ... can occur as well
*/ */
alternativeDenomination?: string[] alternativeDenomination?: string[]
@ -62,16 +127,16 @@ export interface DenominationConfigJson {
* "fr": "metre" * "fr": "metre"
* } * }
*/ */
human?: string | any human?: string | Record<string, string>
/** /**
* The value for humans in the dropdown. This should not use abbreviations and should be translated, e.g. * The value for humans in the dropdown. This should not use abbreviations and should be translated, e.g.
* { * {
* "en": "minute", * "en": "minute",
* "nl": "minuut"x² * "nl": "minuut"
* } * }
*/ */
humanSingular?: string | any humanSingular?: string | Record<string, string>
/** /**
* If set, then the canonical value will be prefixed instead, e.g. for '€' * If set, then the canonical value will be prefixed instead, e.g. for '€'

View file

@ -60,6 +60,44 @@ export class Unit {
} }
} }
/**
*
* // Should detect invalid defaultInput
* let threwError = false
* try{
* Unit.fromJson({
* appliesToKey: ["length"],
* defaultInput: "xcm",
* applicableUnits: [
* {
* canonicalDenomination: "m",
* useIfNoUnitGiven: true,
* human: "meter"
* }
* ]
* },"test")
* }catch(e){
* threwError =true
* }
* threwError // => false
*
* // Should work
* Unit.fromJson({
* appliesToKey: ["length"],
* defaultInput: "xcm",
* applicableUnits: [
* {
* canonicalDenomination: "m",
* useIfNoUnitGiven: true,
* humen: "meter"
* },
* {
* canonicalDenomination: "cm",
* human: "centimeter"
* }
* ]
* }, "test")
*/
static fromJson(json: UnitConfigJson, ctx: string) { static fromJson(json: UnitConfigJson, ctx: string) {
const appliesTo = json.appliesToKey const appliesTo = json.appliesToKey
for (let i = 0; i < appliesTo.length; i++) { for (let i = 0; i < appliesTo.length; i++) {
@ -74,14 +112,13 @@ export class Unit {
} }
// Some keys do have unit handling // Some keys do have unit handling
if (json.applicableUnits.some((denom) => denom.useAsDefaultInput !== undefined)) {
json.applicableUnits.forEach((denom) => {
denom.useAsDefaultInput = denom.useAsDefaultInput ?? false
})
}
const applicable = json.applicableUnits.map( const applicable = json.applicableUnits.map(
(u, i) => new Denomination(u, `${ctx}.units[${i}]`) (u, i) =>
new Denomination(
u,
u.canonicalDenomination.trim() === json.defaultInput,
`${ctx}.units[${i}]`
)
) )
return new Unit(appliesTo, applicable, json.eraseInvalidValues ?? false) return new Unit(appliesTo, applicable, json.eraseInvalidValues ?? false)
} }

View file

@ -205,6 +205,7 @@
"elevator:width", "elevator:width",
"elevator:depth" "elevator:depth"
], ],
"defaultInput": "cm",
"applicableUnits": [ "applicableUnits": [
{ {
"canonicalDenomination": "m", "canonicalDenomination": "m",
@ -221,7 +222,6 @@
} }
}, },
{ {
"useAsDefaultInput": true,
"canonicalDenomination": "cm", "canonicalDenomination": "cm",
"alternativeDenomination": [ "alternativeDenomination": [
"centimeter", "centimeter",
@ -238,4 +238,4 @@
] ]
} }
] ]
} }

View file

@ -473,6 +473,7 @@
"kerb:height", "kerb:height",
"width" "width"
], ],
"defaultInput": "cm",
"applicableUnits": [ "applicableUnits": [
{ {
"useIfNoUnitGiven": true, "useIfNoUnitGiven": true,
@ -489,7 +490,6 @@
} }
}, },
{ {
"useAsDefaultInput": true,
"canonicalDenomination": "cm", "canonicalDenomination": "cm",
"alternativeDenomination": [ "alternativeDenomination": [
"centimeter", "centimeter",
@ -506,4 +506,4 @@
] ]
} }
] ]
} }

View file

@ -0,0 +1,3 @@
{
}

View file

@ -63,6 +63,7 @@
"width", "width",
"_biggest_width" "_biggest_width"
], ],
"defaultUnit": "cm",
"applicableUnits": [ "applicableUnits": [
{ {
"useIfNoUnitGiven": true, "useIfNoUnitGiven": true,
@ -79,7 +80,6 @@
} }
}, },
{ {
"useAsDefaultInput": true,
"canonicalDenomination": "cm", "canonicalDenomination": "cm",
"alternativeDenomination": [ "alternativeDenomination": [
"centimeter", "centimeter",
@ -150,4 +150,4 @@
"condition": "_biggest_width_id~*" "condition": "_biggest_width_id~*"
} }
] ]
} }

View file

@ -14,6 +14,7 @@ describe("Unit", () => {
nl: " megawatt", nl: " megawatt",
}, },
}, },
false,
"test" "test"
) )