diff --git a/Models/Denomination.ts b/Models/Denomination.ts index 03770ef6c1..cddd0ac7d7 100644 --- a/Models/Denomination.ts +++ b/Models/Denomination.ts @@ -15,7 +15,7 @@ export class Denomination { private readonly _human: Translation private readonly _humanSingular?: Translation - constructor(json: DenominationConfigJson, context: string) { + constructor(json: DenominationConfigJson, useAsDefaultInput: boolean, context: string) { context = `${context}.unit(${json.canonicalDenomination})` this.canonical = json.canonicalDenomination.trim() if (this.canonical === undefined) { @@ -35,7 +35,7 @@ export class Denomination { throw `${context} uses the old 'default'-key. Use "useIfNoUnitGiven" or "useAsDefaultInput" instead` } this.useIfNoUnitGiven = json.useIfNoUnitGiven - this.useAsDefaultInput = json.useAsDefaultInput ?? json.useIfNoUnitGiven + this.useAsDefaultInput = useAsDefaultInput ?? json.useIfNoUnitGiven this._human = Translations.T(json.human, context + "human") this._humanSingular = Translations.T(json.humanSingular, context + "humanSingular") @@ -69,7 +69,7 @@ export class Denomination { * human: { * en: "meter" * } - * }, "test") + * }, false, "test") * unit.canonicalValue("42m", true) // =>"42 m" * unit.canonicalValue("42", true) // =>"42 m" * unit.canonicalValue("42 m", true) // =>"42 m" @@ -84,7 +84,7 @@ export class Denomination { * human: { * en: "meter" * } - * }, "test") + * }, false, "test") * unit.canonicalValue("42m", true) // =>"42" * unit.canonicalValue("42", true) // =>"42" * unit.canonicalValue("42 m", true) // =>"42" diff --git a/Models/ThemeConfig/Json/LayerConfigJson.ts b/Models/ThemeConfig/Json/LayerConfigJson.ts index e5e177221f..fd80dc4b50 100644 --- a/Models/ThemeConfig/Json/LayerConfigJson.ts +++ b/Models/ThemeConfig/Json/LayerConfigJson.ts @@ -389,62 +389,7 @@ export interface LayerConfigJson { 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 - * + * @see UnitConfigJson */ units?: UnitConfigJson[] diff --git a/Models/ThemeConfig/Json/UnitConfigJson.ts b/Models/ThemeConfig/Json/UnitConfigJson.ts index a997ae1644..88d54009f6 100644 --- a/Models/ThemeConfig/Json/UnitConfigJson.ts +++ b/Models/ThemeConfig/Json/UnitConfigJson.ts @@ -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 { /** * Every key from this list will be normalized. @@ -11,9 +69,19 @@ export default interface UnitConfigJson { */ eraseInvalidValues?: boolean /** - * The possible denominations + * The possible denominations for this unit. + * For length, denominations could be "meter", "kilometer", "miles", "foot" */ 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 { @@ -28,12 +96,6 @@ export interface DenominationConfigJson { */ 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. * e.g. "m" for meters @@ -46,12 +108,15 @@ export interface DenominationConfigJson { /** * 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 /** * 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[] @@ -62,16 +127,16 @@ export interface DenominationConfigJson { * "fr": "metre" * } */ - human?: string | any + human?: string | Record /** * The value for humans in the dropdown. This should not use abbreviations and should be translated, e.g. * { * "en": "minute", - * "nl": "minuut"x² + * "nl": "minuut" * } */ - humanSingular?: string | any + humanSingular?: string | Record /** * If set, then the canonical value will be prefixed instead, e.g. for '€' diff --git a/Models/Unit.ts b/Models/Unit.ts index 7a48719752..90661ecc94 100644 --- a/Models/Unit.ts +++ b/Models/Unit.ts @@ -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) { const appliesTo = json.appliesToKey for (let i = 0; i < appliesTo.length; i++) { @@ -74,14 +112,13 @@ export class Unit { } // 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( - (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) } diff --git a/assets/layers/elevator/elevator.json b/assets/layers/elevator/elevator.json index 620ae73ac6..3ba73772e4 100644 --- a/assets/layers/elevator/elevator.json +++ b/assets/layers/elevator/elevator.json @@ -205,6 +205,7 @@ "elevator:width", "elevator:depth" ], + "defaultInput": "cm", "applicableUnits": [ { "canonicalDenomination": "m", @@ -221,7 +222,6 @@ } }, { - "useAsDefaultInput": true, "canonicalDenomination": "cm", "alternativeDenomination": [ "centimeter", @@ -238,4 +238,4 @@ ] } ] -} \ No newline at end of file +} diff --git a/assets/layers/entrance/entrance.json b/assets/layers/entrance/entrance.json index d677f71821..3f467621d0 100644 --- a/assets/layers/entrance/entrance.json +++ b/assets/layers/entrance/entrance.json @@ -473,6 +473,7 @@ "kerb:height", "width" ], + "defaultInput": "cm", "applicableUnits": [ { "useIfNoUnitGiven": true, @@ -489,7 +490,6 @@ } }, { - "useAsDefaultInput": true, "canonicalDenomination": "cm", "alternativeDenomination": [ "centimeter", @@ -506,4 +506,4 @@ ] } ] -} \ No newline at end of file +} diff --git a/assets/layers/units/units.json b/assets/layers/units/units.json new file mode 100644 index 0000000000..0db3279e44 --- /dev/null +++ b/assets/layers/units/units.json @@ -0,0 +1,3 @@ +{ + +} diff --git a/assets/layers/walls_and_buildings/walls_and_buildings.json b/assets/layers/walls_and_buildings/walls_and_buildings.json index 86461de309..a94389e624 100644 --- a/assets/layers/walls_and_buildings/walls_and_buildings.json +++ b/assets/layers/walls_and_buildings/walls_and_buildings.json @@ -63,6 +63,7 @@ "width", "_biggest_width" ], + "defaultUnit": "cm", "applicableUnits": [ { "useIfNoUnitGiven": true, @@ -79,7 +80,6 @@ } }, { - "useAsDefaultInput": true, "canonicalDenomination": "cm", "alternativeDenomination": [ "centimeter", @@ -150,4 +150,4 @@ "condition": "_biggest_width_id~*" } ] -} \ No newline at end of file +} diff --git a/test/Models/Units.spec.ts b/test/Models/Units.spec.ts index 1490ba6a17..513744f31e 100644 --- a/test/Models/Units.spec.ts +++ b/test/Models/Units.spec.ts @@ -14,6 +14,7 @@ describe("Unit", () => { nl: " megawatt", }, }, + false, "test" )