forked from MapComplete/MapComplete
		
	Rework units to allow picking different default units in different locations, fixes #1011
This commit is contained in:
		
							parent
							
								
									e981abd2aa
								
							
						
					
					
						commit
						5da76b9418
					
				
					 17 changed files with 149 additions and 100 deletions
				
			
		|  | @ -235,7 +235,7 @@ export default class SimpleMetaTaggers { | ||||||
| 
 | 
 | ||||||
|     private static canonicalize = new SimpleMetaTagger( |     private static canonicalize = new SimpleMetaTagger( | ||||||
|         { |         { | ||||||
|             doc: "If 'units' is defined in the layoutConfig, then this metatagger will rewrite the specified keys to have the canonical form (e.g. `1meter` will be rewritten to `1m`)", |             doc: "If 'units' is defined in the layoutConfig, then this metatagger will rewrite the specified keys to have the canonical form (e.g. `1meter` will be rewritten to `1m`; `1` will be rewritten to `1m` as well)", | ||||||
|             keys: ["Theme-defined keys"], |             keys: ["Theme-defined keys"], | ||||||
| 
 | 
 | ||||||
|         }, |         }, | ||||||
|  | @ -261,13 +261,14 @@ export default class SimpleMetaTaggers { | ||||||
|                         continue; |                         continue; | ||||||
|                     } |                     } | ||||||
|                     const value = feature.properties[key] |                     const value = feature.properties[key] | ||||||
|                     const denom = unit.findDenomination(value) |                     const denom = unit.findDenomination(value, () => feature.properties["_country"]) | ||||||
|                     if (denom === undefined) { |                     if (denom === undefined) { | ||||||
|                         // no valid value found
 |                         // no valid value found
 | ||||||
|                         break; |                         break; | ||||||
|                     } |                     } | ||||||
|                     const [, denomination] = denom; |                     const [, denomination] = denom; | ||||||
|                     let canonical = denomination?.canonicalValue(value) ?? undefined; |                     const defaultDenom = unit.getDefaultDenomination(() => feature.properties["_country"]) | ||||||
|  |                     let canonical = denomination?.canonicalValue(value, defaultDenom == denomination) ?? undefined; | ||||||
|                     if (canonical === value) { |                     if (canonical === value) { | ||||||
|                         break; |                         break; | ||||||
|                     } |                     } | ||||||
|  |  | ||||||
|  | @ -1,21 +1,22 @@ | ||||||
| import {Translation} from "../UI/i18n/Translation"; | import {Translation} from "../UI/i18n/Translation"; | ||||||
| import {ApplicableUnitJson} from "./ThemeConfig/Json/UnitConfigJson"; | import {DenominationConfigJson} from "./ThemeConfig/Json/UnitConfigJson"; | ||||||
| import Translations from "../UI/i18n/Translations"; | import Translations from "../UI/i18n/Translations"; | ||||||
| import {Store, UIEventSource} from "../Logic/UIEventSource"; | import {Store} from "../Logic/UIEventSource"; | ||||||
| import BaseUIElement from "../UI/BaseUIElement"; | import BaseUIElement from "../UI/BaseUIElement"; | ||||||
| import Toggle from "../UI/Input/Toggle"; | import Toggle from "../UI/Input/Toggle"; | ||||||
| 
 | 
 | ||||||
| export class Denomination { | export class Denomination { | ||||||
|     public readonly canonical: string; |     public readonly canonical: string; | ||||||
|     public readonly _canonicalSingular: string; |     public readonly _canonicalSingular: string; | ||||||
|     public readonly default: boolean; |     public readonly useAsDefaultInput: boolean | string[] | ||||||
|  |     public readonly useIfNoUnitGiven : boolean | string[] | ||||||
|     public readonly prefix: boolean; |     public readonly prefix: boolean; | ||||||
|     public readonly alternativeDenominations: string []; |     public readonly alternativeDenominations: string []; | ||||||
|     private readonly _human: Translation; |     private readonly _human: Translation; | ||||||
|     private readonly _humanSingular?: Translation; |     private readonly _humanSingular?: Translation; | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|     constructor(json: ApplicableUnitJson, context: string) { |     constructor(json: DenominationConfigJson, 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) { | ||||||
|  | @ -32,7 +33,11 @@ export class Denomination { | ||||||
| 
 | 
 | ||||||
|         this.alternativeDenominations = json.alternativeDenomination?.map(v => v.trim()) ?? [] |         this.alternativeDenominations = json.alternativeDenomination?.map(v => v.trim()) ?? [] | ||||||
| 
 | 
 | ||||||
|         this.default = json.default ?? false; |         if(json["default"] !== undefined) { | ||||||
|  |             throw `${context} uses the old 'default'-key. Use "useIfNoUnitGiven" or "useAsDefaultInput" instead` | ||||||
|  |         } | ||||||
|  |         this.useIfNoUnitGiven = json.useIfNoUnitGiven | ||||||
|  |         this.useAsDefaultInput = json.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") | ||||||
|  | @ -68,32 +73,31 @@ export class Denomination { | ||||||
|      * const unit = new Denomination({ |      * const unit = new Denomination({ | ||||||
|      *               canonicalDenomination: "m", |      *               canonicalDenomination: "m", | ||||||
|      *               alternativeDenomination: ["meter"], |      *               alternativeDenomination: ["meter"], | ||||||
|      *               'default': true, |  | ||||||
|      *               human: { |      *               human: { | ||||||
|      *                   en: "meter" |      *                   en: "meter" | ||||||
|      *               } |      *               } | ||||||
|      *           }, "test") |      *           }, "test") | ||||||
|      * unit.canonicalValue("42m") // =>"42 m"
 |      * unit.canonicalValue("42m", true) // =>"42 m"
 | ||||||
|      * unit.canonicalValue("42") // =>"42 m"
 |      * unit.canonicalValue("42", true) // =>"42 m"
 | ||||||
|      * unit.canonicalValue("42 m") // =>"42 m"
 |      * unit.canonicalValue("42 m", true) // =>"42 m"
 | ||||||
|      * unit.canonicalValue("42 meter") // =>"42 m"
 |      * unit.canonicalValue("42 meter", true) // =>"42 m"
 | ||||||
|      *  |      * unit.canonicalValue("42m", true) // =>"42 m"
 | ||||||
|  |      * unit.canonicalValue("42", true) // =>"42 m"
 | ||||||
|      *  |      *  | ||||||
|      * // Should be trimmed if canonical is empty
 |      * // Should be trimmed if canonical is empty
 | ||||||
|      * const unit = new Denomination({ |      * const unit = new Denomination({ | ||||||
|      *               canonicalDenomination: "", |      *               canonicalDenomination: "", | ||||||
|      *               alternativeDenomination: ["meter","m"], |      *               alternativeDenomination: ["meter","m"], | ||||||
|      *               'default': true, |  | ||||||
|      *               human: { |      *               human: { | ||||||
|      *                   en: "meter" |      *                   en: "meter" | ||||||
|      *               } |      *               } | ||||||
|      *           }, "test") |      *           }, "test") | ||||||
|      * unit.canonicalValue("42m") // =>"42"
 |      * unit.canonicalValue("42m", true) // =>"42"
 | ||||||
|      * unit.canonicalValue("42") // =>"42"
 |      * unit.canonicalValue("42", true) // =>"42"
 | ||||||
|      * unit.canonicalValue("42 m") // =>"42"
 |      * unit.canonicalValue("42 m", true) // =>"42"
 | ||||||
|      * unit.canonicalValue("42 meter") // =>"42"
 |      * unit.canonicalValue("42 meter", true) // =>"42"
 | ||||||
|      */ |      */ | ||||||
|     public canonicalValue(value: string, actAsDefault?: boolean) : string { |     public canonicalValue(value: string, actAsDefault: boolean) : string { | ||||||
|         if (value === undefined) { |         if (value === undefined) { | ||||||
|             return undefined; |             return undefined; | ||||||
|         } |         } | ||||||
|  | @ -114,7 +118,7 @@ export class Denomination { | ||||||
|      * |      * | ||||||
|      * Returns null if it doesn't match this unit |      * Returns null if it doesn't match this unit | ||||||
|      */ |      */ | ||||||
|     public StrippedValue(value: string, actAsDefault?: boolean): string { |     public StrippedValue(value: string, actAsDefault: boolean): string { | ||||||
| 
 | 
 | ||||||
|         if (value === undefined) { |         if (value === undefined) { | ||||||
|             return undefined; |             return undefined; | ||||||
|  | @ -153,15 +157,26 @@ export class Denomination { | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         if (this.default || actAsDefault) { |         if (!actAsDefault) { | ||||||
|  |             return null | ||||||
|  |         } | ||||||
|  |          | ||||||
|         const parsed = Number(value.trim()) |         const parsed = Number(value.trim()) | ||||||
|         if (!isNaN(parsed)) { |         if (!isNaN(parsed)) { | ||||||
|             return value.trim(); |             return value.trim(); | ||||||
|         } |         } | ||||||
|         } |  | ||||||
| 
 | 
 | ||||||
|         return null; |         return null; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  |     isDefaultUnit(country: () => string) { | ||||||
|  |         if(this.useIfNoUnitGiven === true){ | ||||||
|  |             return true | ||||||
|  |         } | ||||||
|  |         if(this.useIfNoUnitGiven === false){ | ||||||
|  |             return false | ||||||
|  |         } | ||||||
|  |         return this.useIfNoUnitGiven.indexOf(country()) >= 0 | ||||||
|  |     } | ||||||
| } | } | ||||||
|  | @ -14,13 +14,32 @@ export default interface UnitConfigJson { | ||||||
|     /** |     /** | ||||||
|      * The possible denominations |      * The possible denominations | ||||||
|      */ |      */ | ||||||
|     applicableUnits: ApplicableUnitJson[] |     applicableUnits: DenominationConfigJson[] | ||||||
| 
 | 
 | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export interface ApplicableUnitJson { | export interface DenominationConfigJson { | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|     /** |     /** | ||||||
|      * The canonical value which will be added to the value in OSM. |      * If this evaluates to true and the value to interpret has _no_ unit given, assumes that this unit is meant. | ||||||
|  |      * Alternatively, a list of country codes can be given where this acts as the default interpretation | ||||||
|  |      *  | ||||||
|  |      * E.g., a denomination using "meter" would probably set this flag to "true"; | ||||||
|  |      * a denomination for "mp/h" will use the condition "_country=gb" to indicate that it is the default in the UK. | ||||||
|  |      *  | ||||||
|  |      * If none of the units indicate that they are the default, the first denomination will be used instead | ||||||
|  |      */ | ||||||
|  |     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 |      * e.g. "m" for meters | ||||||
|      * If the user inputs '42', the canonical value will be added and it'll become '42m'. |      * If the user inputs '42', the canonical value will be added and it'll become '42m'. | ||||||
|      *  |      *  | ||||||
|  | @ -28,8 +47,11 @@ export interface ApplicableUnitJson { | ||||||
|      * In this case, an empty string should be used |      * In this case, an empty string should be used | ||||||
|      */ |      */ | ||||||
|     canonicalDenomination: string, |     canonicalDenomination: string, | ||||||
|  | 
 | ||||||
|  |      | ||||||
|     /** |     /** | ||||||
|      * 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 | ||||||
|      */ |      */ | ||||||
|     canonicalDenominationSingular?: string, |     canonicalDenominationSingular?: string, | ||||||
| 
 | 
 | ||||||
|  | @ -63,9 +85,5 @@ export interface ApplicableUnitJson { | ||||||
|      */ |      */ | ||||||
|     prefix?: boolean |     prefix?: boolean | ||||||
| 
 | 
 | ||||||
|     /** | 
 | ||||||
|      * The default interpretation - only one can be set. |  | ||||||
|      * If none is set, the first unit will be considered the default interpretation of a value without a unit |  | ||||||
|      */ |  | ||||||
|     default?: boolean |  | ||||||
| } | } | ||||||
|  | @ -8,14 +8,11 @@ export class Unit { | ||||||
|     public readonly appliesToKeys: Set<string>; |     public readonly appliesToKeys: Set<string>; | ||||||
|     public readonly denominations: Denomination[]; |     public readonly denominations: Denomination[]; | ||||||
|     public readonly denominationsSorted: Denomination[]; |     public readonly denominationsSorted: Denomination[]; | ||||||
|     public readonly defaultDenom: Denomination; |  | ||||||
|     public readonly eraseInvalid: boolean; |     public readonly eraseInvalid: boolean; | ||||||
|     private readonly possiblePostFixes: string[] = [] |  | ||||||
| 
 | 
 | ||||||
|     constructor(appliesToKeys: string[], applicableUnits: Denomination[], eraseInvalid: boolean) { |     constructor(appliesToKeys: string[], applicableDenominations: Denomination[], eraseInvalid: boolean) { | ||||||
|         this.appliesToKeys = new Set(appliesToKeys); |         this.appliesToKeys = new Set(appliesToKeys); | ||||||
|         this.denominations = applicableUnits; |         this.denominations = applicableDenominations; | ||||||
|         this.defaultDenom = applicableUnits.filter(denom => denom.default)[0] |  | ||||||
|         this.eraseInvalid = eraseInvalid |         this.eraseInvalid = eraseInvalid | ||||||
| 
 | 
 | ||||||
|         const seenUnitExtensions = new Set<string>(); |         const seenUnitExtensions = new Set<string>(); | ||||||
|  | @ -52,8 +49,6 @@ export class Unit { | ||||||
|             addPostfixesOf(denomination._canonicalSingular) |             addPostfixesOf(denomination._canonicalSingular) | ||||||
|             denomination.alternativeDenominations.forEach(addPostfixesOf) |             denomination.alternativeDenominations.forEach(addPostfixesOf) | ||||||
|         } |         } | ||||||
|         this.possiblePostFixes = Array.from(possiblePostFixes) |  | ||||||
|         this.possiblePostFixes.sort((a, b) => b.length - a.length) |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | @ -71,16 +66,12 @@ export class Unit { | ||||||
|         } |         } | ||||||
|         // Some keys do have unit handling
 |         // Some keys do have unit handling
 | ||||||
| 
 | 
 | ||||||
|         const defaultSet = json.applicableUnits.filter(u => u.default === true) |         if(json.applicableUnits.some(denom => denom.useAsDefaultInput !== undefined)){ | ||||||
|         // No default is defined - we pick the first as default
 |             json.applicableUnits.forEach(denom => { | ||||||
|         if (defaultSet.length === 0) { |                 denom.useAsDefaultInput = denom.useAsDefaultInput ?? false | ||||||
|             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}]`)) |         const applicable = json.applicableUnits.map((u, i) => new Denomination(u, `${ctx}.units[${i}]`)) | ||||||
|         return new Unit(appliesTo, applicable, json.eraseInvalidValues ?? false) |         return new Unit(appliesTo, applicable, json.eraseInvalidValues ?? false) | ||||||
|     } |     } | ||||||
|  | @ -96,12 +87,13 @@ export class Unit { | ||||||
|     /** |     /** | ||||||
|      * Finds which denomination is applicable and gives the stripped value back |      * Finds which denomination is applicable and gives the stripped value back | ||||||
|      */ |      */ | ||||||
|     findDenomination(valueWithDenom: string): [string, Denomination] { |     findDenomination(valueWithDenom: string, country: () => string): [string, Denomination] { | ||||||
|         if (valueWithDenom === undefined) { |         if (valueWithDenom === undefined) { | ||||||
|             return undefined; |             return undefined; | ||||||
|         } |         } | ||||||
|  |         const defaultDenom = this.getDefaultDenomination(country) | ||||||
|         for (const denomination of this.denominationsSorted) { |         for (const denomination of this.denominationsSorted) { | ||||||
|             const bare = denomination.StrippedValue(valueWithDenom) |             const bare = denomination.StrippedValue(valueWithDenom, defaultDenom === denomination) | ||||||
|             if (bare !== null) { |             if (bare !== null) { | ||||||
|                 return [bare, denomination] |                 return [bare, denomination] | ||||||
|             } |             } | ||||||
|  | @ -109,11 +101,11 @@ export class Unit { | ||||||
|         return [undefined, undefined] |         return [undefined, undefined] | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     asHumanLongValue(value: string): BaseUIElement { |     asHumanLongValue(value: string, country: () => string): BaseUIElement { | ||||||
|         if (value === undefined) { |         if (value === undefined) { | ||||||
|             return undefined; |             return undefined; | ||||||
|         } |         } | ||||||
|         const [stripped, denom] = this.findDenomination(value) |         const [stripped, denom] = this.findDenomination(value, country) | ||||||
|         const human = stripped === "1" ? denom?.humanSingular : denom?.human |         const human = stripped === "1" ? denom?.humanSingular : denom?.human | ||||||
|         if (human === undefined) { |         if (human === undefined) { | ||||||
|             return new FixedUiElement(stripped ?? value); |             return new FixedUiElement(stripped ?? value); | ||||||
|  | @ -124,24 +116,46 @@ export class Unit { | ||||||
| 
 | 
 | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** | 
 | ||||||
|      *   Returns the value without any (sub)parts of any denomination - usefull as preprocessing step for validating inputs. |     public getDefaultInput(country: () => string | string[]) { | ||||||
|      *   E.g. |         console.log("Searching the default denomination for input", country) | ||||||
|      *   if 'megawatt' is a possible denomination, then '5 Meg' will be rewritten to '5' (which can then be validated as a valid pnat) |         for (const denomination of this.denominations) { | ||||||
|      * |             if (denomination.useAsDefaultInput === true) { | ||||||
|      *   Returns the original string if nothign matches |                 return denomination | ||||||
|      */ |             } | ||||||
|     stripUnitParts(str: string) { |             if (denomination.useAsDefaultInput === undefined || denomination.useAsDefaultInput === false) { | ||||||
|         if (str === undefined) { |                 continue | ||||||
|             return undefined; |             } | ||||||
|  |             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 | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         return this.denominations[0] | ||||||
|     } |     } | ||||||
|      |      | ||||||
|         for (const denominationPart of this.possiblePostFixes) { |     public getDefaultDenomination(country: () => string){ | ||||||
|             if (str.endsWith(denominationPart)) { |         for (const denomination of this.denominations) { | ||||||
|                 return str.substring(0, str.length - denominationPart.length).trim() |             if (denomination.useIfNoUnitGiven === true || denomination.canonical === "") { | ||||||
|  |                 return denomination | ||||||
|             } |             } | ||||||
|  |             if (denomination.useIfNoUnitGiven === undefined || denomination.useIfNoUnitGiven === false) { | ||||||
|  |                 continue | ||||||
|  |             } | ||||||
|  |             let countries: string | string[] = country() | ||||||
|  |             if (typeof countries === "string") { | ||||||
|  |                 countries = countries.split(",") | ||||||
|  |             } | ||||||
|  |             const denominationCountries: string[] = denomination.useIfNoUnitGiven | ||||||
|  |             if (countries.some(country => denominationCountries.indexOf(country) >= 0)) { | ||||||
|  |                 return denomination | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         return this.denominations[0] | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|         return str; |  | ||||||
|     } |  | ||||||
| } | } | ||||||
|  | @ -20,6 +20,7 @@ import {QueryParameters} from "../../Logic/Web/QueryParameters"; | ||||||
| import {TagUtils} from "../../Logic/Tags/TagUtils"; | import {TagUtils} from "../../Logic/Tags/TagUtils"; | ||||||
| import {InputElement} from "../Input/InputElement"; | import {InputElement} from "../Input/InputElement"; | ||||||
| import {DropDown} from "../Input/DropDown"; | import {DropDown} from "../Input/DropDown"; | ||||||
|  | import {FixedUiElement} from "../Base/FixedUiElement"; | ||||||
| 
 | 
 | ||||||
| export default class FilterView extends VariableUiElement { | export default class FilterView extends VariableUiElement { | ||||||
|     constructor(filteredLayer: UIEventSource<FilteredLayer[]>,  |     constructor(filteredLayer: UIEventSource<FilteredLayer[]>,  | ||||||
|  | @ -91,7 +92,7 @@ export default class FilterView extends VariableUiElement { | ||||||
|         if (filteredLayer.layerDef.name === undefined) { |         if (filteredLayer.layerDef.name === undefined) { | ||||||
|             // Name is not defined: we hide this one
 |             // Name is not defined: we hide this one
 | ||||||
|             return new Toggle( |             return new Toggle( | ||||||
|                 filteredLayer?.layerDef?.description?.Clone()?.SetClass("subtle")                , |                 new FixedUiElement(filteredLayer?.layerDef?.id ).SetClass("block") , | ||||||
|                 undefined, |                 undefined, | ||||||
|                 state?.featureSwitchIsDebugging |                 state?.featureSwitchIsDebugging | ||||||
|             ); |             ); | ||||||
|  |  | ||||||
|  | @ -90,7 +90,7 @@ export class TextFieldDef { | ||||||
|         if (options.unit !== undefined) { |         if (options.unit !== undefined) { | ||||||
|             // Reformatting is handled by the unit in this case
 |             // Reformatting is handled by the unit in this case
 | ||||||
|             options["isValid"] = str => { |             options["isValid"] = str => { | ||||||
|                 const denom = options.unit.findDenomination(str); |                 const denom = options.unit.findDenomination(str, options?.country); | ||||||
|                 if (denom === undefined) { |                 if (denom === undefined) { | ||||||
|                     return false; |                     return false; | ||||||
|                 } |                 } | ||||||
|  | @ -153,7 +153,7 @@ export class TextFieldDef { | ||||||
|                             } |                             } | ||||||
|                         }) |                         }) | ||||||
|                     ) |                     ) | ||||||
|             unitDropDown.GetValue().setData(unit.defaultDenom) |             unitDropDown.GetValue().setData(unit.getDefaultInput(options.country)) | ||||||
|             unitDropDown.SetClass("w-min") |             unitDropDown.SetClass("w-min") | ||||||
| 
 | 
 | ||||||
|             const fixedDenom = unit.denominations.length === 1 ? unit.denominations[0] : undefined |             const fixedDenom = unit.denominations.length === 1 ? unit.denominations[0] : undefined | ||||||
|  | @ -169,7 +169,7 @@ export class TextFieldDef { | ||||||
|                 }, |                 }, | ||||||
|                 (valueWithDenom: string) => { |                 (valueWithDenom: string) => { | ||||||
|                     // Take the value from OSM and feed it into the textfield and the dropdown
 |                     // Take the value from OSM and feed it into the textfield and the dropdown
 | ||||||
|                     const withDenom = unit.findDenomination(valueWithDenom); |                     const withDenom = unit.findDenomination(valueWithDenom, options?.country); | ||||||
|                     if (withDenom === undefined) { |                     if (withDenom === undefined) { | ||||||
|                         // Not a valid value at all - we give it undefined and leave the details up to the other elements (but we keep the previous denomination)
 |                         // Not a valid value at all - we give it undefined and leave the details up to the other elements (but we keep the previous denomination)
 | ||||||
|                         return [undefined, fixedDenom] |                         return [undefined, fixedDenom] | ||||||
|  |  | ||||||
|  | @ -617,6 +617,7 @@ export default class TagRenderingQuestion extends Combine { | ||||||
|         const tagsData = tags.data; |         const tagsData = tags.data; | ||||||
|         const feature = state?.allElements?.ContainingFeatures?.get(tagsData.id) |         const feature = state?.allElements?.ContainingFeatures?.get(tagsData.id) | ||||||
|         const center = feature != undefined ? GeoOperations.centerpointCoordinates(feature) : [0, 0] |         const center = feature != undefined ? GeoOperations.centerpointCoordinates(feature) : [0, 0] | ||||||
|  |         console.log("Creating a tr-question with applicableUnit", applicableUnit) | ||||||
|         const input: InputElement<string> = ValidatedTextField.ForType(configuration.freeform.type)?.ConstructInputElement({ |         const input: InputElement<string> = ValidatedTextField.ForType(configuration.freeform.type)?.ConstructInputElement({ | ||||||
|             country: () => tagsData._country, |             country: () => tagsData._country, | ||||||
|             location: [center[1], center[0]], |             location: [center[1], center[0]], | ||||||
|  |  | ||||||
|  | @ -1273,6 +1273,9 @@ export default class SpecialVisualizations { | ||||||
|                             const tagRendering = layer.tagRenderings.find(tr => tr.id === tagRenderingId) |                             const tagRendering = layer.tagRenderings.find(tr => tr.id === tagRenderingId) | ||||||
|                             tagRenderings.push([layer, tagRendering]) |                             tagRenderings.push([layer, tagRendering]) | ||||||
|                         } |                         } | ||||||
|  |                         if(tagRenderings.length === 0){ | ||||||
|  |                             throw "Could not create stolen tagrenddering: tagRenderings not found" | ||||||
|  |                         } | ||||||
|                         return new VariableUiElement(featureTags.map(tags => { |                         return new VariableUiElement(featureTags.map(tags => { | ||||||
|                             const featureId = tags[featureIdKey] |                             const featureId = tags[featureIdKey] | ||||||
|                             if (featureId === undefined) { |                             if (featureId === undefined) { | ||||||
|  |  | ||||||
|  | @ -185,6 +185,7 @@ | ||||||
|           "alternativeDenomination": [ |           "alternativeDenomination": [ | ||||||
|             "meter" |             "meter" | ||||||
|           ], |           ], | ||||||
|  |           "useIfNoUnitGiven": true, | ||||||
|           "human": { |           "human": { | ||||||
|             "en": "meter", |             "en": "meter", | ||||||
|             "fr": "mètre", |             "fr": "mètre", | ||||||
|  | @ -193,7 +194,7 @@ | ||||||
|           } |           } | ||||||
|         }, |         }, | ||||||
|         { |         { | ||||||
|           "default": true, |           "useAsDefaultInput": true, | ||||||
|           "canonicalDenomination": "cm", |           "canonicalDenomination": "cm", | ||||||
|           "alternativeDenomination": [ |           "alternativeDenomination": [ | ||||||
|             "centimeter", |             "centimeter", | ||||||
|  |  | ||||||
|  | @ -467,10 +467,12 @@ | ||||||
|   "units": [ |   "units": [ | ||||||
|     { |     { | ||||||
|       "appliesToKey": [ |       "appliesToKey": [ | ||||||
|         "kerb:height" |         "kerb:height", | ||||||
|  |         "width" | ||||||
|       ], |       ], | ||||||
|       "applicableUnits": [ |       "applicableUnits": [ | ||||||
|         {   |         {   | ||||||
|  |           "useIfNoUnitGiven": true, | ||||||
|           "canonicalDenomination": "m", |           "canonicalDenomination": "m", | ||||||
|           "alternativeDenomination": [ |           "alternativeDenomination": [ | ||||||
|             "meter" |             "meter" | ||||||
|  | @ -483,7 +485,7 @@ | ||||||
|           } |           } | ||||||
|         }, |         }, | ||||||
|         { |         { | ||||||
|           "default": true, |           "useAsDefaultInput": true, | ||||||
|           "canonicalDenomination": "cm", |           "canonicalDenomination": "cm", | ||||||
|           "alternativeDenomination": [ |           "alternativeDenomination": [ | ||||||
|             "centimeter", |             "centimeter", | ||||||
|  |  | ||||||
|  | @ -456,7 +456,6 @@ | ||||||
|     { |     { | ||||||
|       "applicableUnits": [ |       "applicableUnits": [ | ||||||
|         { |         { | ||||||
|           "default": true, |  | ||||||
|           "canonicalDenomination": "", |           "canonicalDenomination": "", | ||||||
|           "alternativeDenomination": [ |           "alternativeDenomination": [ | ||||||
|             "mm", |             "mm", | ||||||
|  |  | ||||||
|  | @ -339,8 +339,7 @@ | ||||||
|             "nl": "centimeter", |             "nl": "centimeter", | ||||||
|             "de": "Zentimeter", |             "de": "Zentimeter", | ||||||
|             "fr": "centimètre" |             "fr": "centimètre" | ||||||
|           }, |           } | ||||||
|           "default": true |  | ||||||
|         }, |         }, | ||||||
|         { |         { | ||||||
|           "canonicalDenomination": "m", |           "canonicalDenomination": "m", | ||||||
|  |  | ||||||
|  | @ -154,7 +154,6 @@ | ||||||
|             "kmh", |             "kmh", | ||||||
|             "kph" |             "kph" | ||||||
|           ], |           ], | ||||||
|           "default": true, |  | ||||||
|           "human": { |           "human": { | ||||||
|             "en": "kilometers/hour", |             "en": "kilometers/hour", | ||||||
|             "ca": "quilòmetres/hora", |             "ca": "quilòmetres/hora", | ||||||
|  | @ -172,6 +171,7 @@ | ||||||
|         }, |         }, | ||||||
|         { |         { | ||||||
|           "canonicalDenomination": "mph", |           "canonicalDenomination": "mph", | ||||||
|  |           "useIfNoUnitGiven": ["gb","us"], | ||||||
|           "alternativeDenomination": [ |           "alternativeDenomination": [ | ||||||
|             "m/u", |             "m/u", | ||||||
|             "mh", |             "mh", | ||||||
|  |  | ||||||
|  | @ -63,6 +63,7 @@ | ||||||
|       ], |       ], | ||||||
|       "applicableUnits": [ |       "applicableUnits": [ | ||||||
|         { |         { | ||||||
|  |           "useIfNoUnitGiven": true, | ||||||
|           "canonicalDenomination": "m", |           "canonicalDenomination": "m", | ||||||
|           "alternativeDenomination": [ |           "alternativeDenomination": [ | ||||||
|             "meter" |             "meter" | ||||||
|  | @ -74,7 +75,7 @@ | ||||||
|           } |           } | ||||||
|         }, |         }, | ||||||
|         { |         { | ||||||
|           "default": true, |           "useAsDefaultInput": true, | ||||||
|           "canonicalDenomination": "cm", |           "canonicalDenomination": "cm", | ||||||
|           "alternativeDenomination": [ |           "alternativeDenomination": [ | ||||||
|             "centimeter", |             "centimeter", | ||||||
|  |  | ||||||
|  | @ -132,8 +132,7 @@ | ||||||
|                   "ca": " metre", |                   "ca": " metre", | ||||||
|                   "nb_NO": " meter", |                   "nb_NO": " meter", | ||||||
|                   "es": " metro" |                   "es": " metro" | ||||||
|                 }, |                 } | ||||||
|                 "default": true |  | ||||||
|               }, |               }, | ||||||
|               { |               { | ||||||
|                 "canonicalDenomination": "ft", |                 "canonicalDenomination": "ft", | ||||||
|  |  | ||||||
|  | @ -215,10 +215,6 @@ | ||||||
|                 "if": "theme=indoors", |                 "if": "theme=indoors", | ||||||
|                 "then": "./assets/layers/entrance/entrance.svg" |                 "then": "./assets/layers/entrance/entrance.svg" | ||||||
|               }, |               }, | ||||||
|               { |  | ||||||
|                 "if": "theme=kakampink", |  | ||||||
|                 "then": "bug" |  | ||||||
|               }, |  | ||||||
|               { |               { | ||||||
|                 "if": "theme=kerbs_and_crossings", |                 "if": "theme=kerbs_and_crossings", | ||||||
|                 "then": "./assets/layers/kerbs/KerbIcon.svg" |                 "then": "./assets/layers/kerbs/KerbIcon.svg" | ||||||
|  |  | ||||||
|  | @ -7,22 +7,21 @@ describe("Unit", () => { | ||||||
| 
 | 
 | ||||||
|         it("should convert a value back and forth", () => { |         it("should convert a value back and forth", () => { | ||||||
| 
 | 
 | ||||||
|             const unit = new Denomination({ |             const denomintion = new Denomination({ | ||||||
|                 "canonicalDenomination": "MW", |                 "canonicalDenomination": "MW", | ||||||
|                 "alternativeDenomination": ["megawatts", "megawatt"], |                 "alternativeDenomination": ["megawatts", "megawatt"], | ||||||
|                 "human": { |                 "human": { | ||||||
|                     "en": " megawatts", |                     "en": " megawatts", | ||||||
|                     "nl": " megawatt" |                     "nl": " megawatt" | ||||||
|                 }, |                 }, | ||||||
|                 "default": true |  | ||||||
|             }, "test"); |             }, "test"); | ||||||
| 
 | 
 | ||||||
|             const canonical = unit.canonicalValue("5") |             const canonical = denomintion.canonicalValue("5", true) | ||||||
|             expect(canonical).eq( "5 MW") |             expect(canonical).eq( "5 MW") | ||||||
|             const units = new Unit(["key"], [unit], false) |             const units = new Unit(["key"], [denomintion], false) | ||||||
|             const [detected, detectedDenom] = units.findDenomination("5 MW") |             const [detected, detectedDenom] = units.findDenomination("5 MW", () => "be") | ||||||
|             expect(detected).eq( "5") |             expect(detected).eq( "5") | ||||||
|             expect(detectedDenom).eq( unit) |             expect(detectedDenom).eq( denomintion) | ||||||
|         } |         } | ||||||
|     ) |     ) | ||||||
| }) | }) | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue