Units: add possibility to have an "inverted" time unit, e.g. for charge; add charge units to bike_parking

This commit is contained in:
Pieter Vander Vennet 2024-04-28 23:04:09 +02:00
parent 4ccfe3efe4
commit bf523848fb
8 changed files with 94 additions and 32 deletions

View file

@ -110,19 +110,21 @@ export class Denomination {
* @param value the value from OSM
* @param actAsDefault if set and the value can be parsed as number, will be parsed and trimmed
*
* import Validators from "../UI/InputElement/Validators"
*
* const unit = Denomination.fromJson({
* canonicalDenomination: "m",
* alternativeDenomination: ["meter"],
* human: {
* en: "{quantity} meter"
* }
* }, "test")
* unit.canonicalValue("42m", true) // =>"42 m"
* unit.canonicalValue("42", true) // =>"42 m"
* unit.canonicalValue("42 m", true) // =>"42 m"
* unit.canonicalValue("42 meter", true) // =>"42 m"
* unit.canonicalValue("42m", true) // =>"42 m"
* unit.canonicalValue("42", true) // =>"42 m"
* }, Validators.get("float"), "test")
* unit.canonicalValue("42m", true, false) // =>"42 m"
* unit.canonicalValue("42", true, false) // =>"42 m"
* unit.canonicalValue("42 m", true, false) // =>"42 m"
* unit.canonicalValue("42 meter", true, false) // =>"42 m"
* unit.canonicalValue("42m", true, false) // =>"42 m"
* unit.canonicalValue("42", true, false) // =>"42 m"
*
* // Should be trimmed if canonical is empty
* const unit = Denomination.fromJson({
@ -131,22 +133,26 @@ export class Denomination {
* human: {
* en: "{quantity} meter"
* }
* }, "test")
* unit.canonicalValue("42m", true) // =>"42"
* unit.canonicalValue("42", true) // =>"42"
* unit.canonicalValue("42 m", true) // =>"42"
* unit.canonicalValue("42 meter", true) // =>"42"
* }, Validators.get("float"), "test")
* unit.canonicalValue("42m", true, false) // =>"42"
* unit.canonicalValue("42", true, false) // =>"42"
* unit.canonicalValue("42 m", true, false) // =>"42"
* unit.canonicalValue("42 meter", true, false) // =>"42"
*
*
*/
public canonicalValue(value: string, actAsDefault: boolean): string {
public canonicalValue(value: string, actAsDefault: boolean, inverted: boolean): string {
if (value === undefined) {
return undefined
}
const stripped = this.StrippedValue(value, actAsDefault)
const stripped = this.StrippedValue(value, actAsDefault, inverted)
if (stripped === null) {
return null
}
if(inverted){
return (stripped + "/" + this.canonical).trim()
}
if (stripped === "1" && this._canonicalSingular !== undefined) {
return ("1 " + this._canonicalSingular).trim()
}
@ -160,7 +166,7 @@ export class Denomination {
*
* Returns null if it doesn't match this unit
*/
public StrippedValue(value: string, actAsDefault: boolean): string {
public StrippedValue(value: string, actAsDefault: boolean, inverted: boolean): string {
if (value === undefined) {
return undefined
}
@ -178,10 +184,16 @@ export class Denomination {
function substr(key) {
if (self.prefix) {
return value.substr(key.length).trim()
} else {
return value.substring(0, value.length - key.length).trim()
return value.substring(key.length).trim()
}
let trimmed = value.substring(0, value.length - key.length).trim()
if(!inverted){
return trimmed
}
if(trimmed.endsWith("/")){
trimmed = trimmed.substring(0, trimmed.length - 1).trim()
}
return trimmed
}
if (this.canonical !== "" && startsWith(this.canonical.toLowerCase())) {

View file

@ -519,6 +519,7 @@ export interface LayerConfigJson {
/**
* Either a list with [{"key": "unitname", "key2": {"quantity": "unitname", "denominations": ["denom", "denom"]}}]
*
* Use `"inverted": true` if the amount should be _divided_ by the denomination, e.g. for charge over time (`€5/day`)
*
* @see UnitConfigJson
*
@ -526,7 +527,7 @@ export interface LayerConfigJson {
*/
units?: (
| UnitConfigJson
| Record<string, string | { quantity: string; denominations: string[]; canonical?: string }>
| Record<string, string | { quantity: string; denominations: string[]; canonical?: string, inverted?: boolean }>
)[]
/**

View file

@ -15,16 +15,19 @@ export class Unit {
public readonly eraseInvalid: boolean
public readonly quantity: string
private readonly _validator: Validator
public readonly inverted: boolean
constructor(
quantity: string,
appliesToKeys: string[],
applicableDenominations: Denomination[],
eraseInvalid: boolean,
validator: Validator
validator: Validator,
inverted: boolean = false
) {
this.quantity = quantity
this._validator = validator
this.inverted = inverted
this.appliesToKeys = new Set(appliesToKeys)
this.denominations = applicableDenominations
this.eraseInvalid = eraseInvalid
@ -73,7 +76,7 @@ export class Unit {
static fromJson(
json:
| UnitConfigJson
| Record<string, string | { quantity: string; denominations: string[] }>,
| Record<string, string | { quantity: string; denominations: string[], inverted?: boolean }>,
tagRenderings: TagRenderingConfig[],
ctx: string
): Unit[] {
@ -210,7 +213,7 @@ export class Unit {
private static loadFromLibrary(
spec: Record<
string,
string | { quantity: string; denominations: string[]; canonical?: string }
string | { quantity: string; denominations: string[]; canonical?: string, inverted?: boolean }
>,
types: Record<string, ValidatorType>,
ctx: string
@ -222,7 +225,7 @@ export class Unit {
if (typeof toLoad === "string") {
const loaded = this.getFromLibrary(toLoad, ctx)
units.push(
new Unit(loaded.quantity, [key], loaded.denominations, loaded.eraseInvalid, validator)
new Unit(loaded.quantity, [key], loaded.denominations, loaded.eraseInvalid, validator, toLoad["inverted"])
)
continue
}
@ -252,7 +255,7 @@ export class Unit {
const canonical = fetchDenom(toLoad.canonical).withValidator(validator)
denoms.unshift(canonical.withBlankCanonical())
}
units.push(new Unit(loaded.quantity, [key], denoms, loaded.eraseInvalid, validator))
units.push(new Unit(loaded.quantity, [key], denoms, loaded.eraseInvalid, validator, toLoad["inverted"]))
}
return units
}
@ -274,7 +277,7 @@ export class Unit {
}
const defaultDenom = this.getDefaultDenomination(country)
for (const denomination of this.denominationsSorted) {
const bare = denomination.StrippedValue(valueWithDenom, defaultDenom === denomination)
const bare = denomination.StrippedValue(valueWithDenom, defaultDenom === denomination, this.inverted)
if (bare !== null) {
return [bare, denomination]
}
@ -287,10 +290,13 @@ export class Unit {
return undefined
}
const [stripped, denom] = this.findDenomination(value, country)
const human = denom?.human
if(this.inverted ){
return human.Subs({quantity: stripped+"/"})
}
if (stripped === "1") {
return denom?.humanSingular ?? stripped
}
const human = denom?.human
if (human === undefined) {
return stripped ?? value
}
@ -300,6 +306,10 @@ export class Unit {
public toOsm(value: string, denomination: string) {
const denom = this.denominations.find((d) => d.canonical === denomination)
if(this.inverted){
return value+"/"+denom._canonicalSingular
}
const space = denom.addSpace ? " " : ""
if (denom.prefix) {
return denom.canonical + space + value
@ -307,7 +317,7 @@ export class Unit {
return value + space + denom.canonical
}
public getDefaultDenomination(country: () => string) {
public getDefaultDenomination(country: () => string): Denomination {
for (const denomination of this.denominations) {
if (denomination.useIfNoUnitGiven === true) {
return denomination