From 11027b5312f6289c89657d7ced819a4a4adfe4d5 Mon Sep 17 00:00:00 2001 From: Auke de Jong Date: Fri, 3 Mar 2023 21:39:42 +0100 Subject: [PATCH] - Version to 0.6.0 - Added config speed_range_step and speed_range_max to customize the speed ranges. Default depend on output speed unit. --- README.md | 40 +++--- src/CardConfig.ts | 2 + src/CardConfigWrapper.ts | 39 ++++++ src/WindBarCalculator.ts | 14 +- src/WindBarCanvas.ts | 16 ++- src/WindDirectionCalculator.ts | 11 +- src/WindRoseCalculator.ts | 13 +- src/WindRoseCanvas.ts | 8 +- src/WindRoseCard.ts | 18 ++- src/WindSpeedConverter.ts | 242 +++++++++++++++------------------ 10 files changed, 216 insertions(+), 187 deletions(-) diff --git a/README.md b/README.md index 59f23cb..254f032 100644 --- a/README.md +++ b/README.md @@ -54,25 +54,27 @@ Else, if you prefer the graphical editor, use the menu to add the resource: ### Card options -| Name | Type | Default | Required | Description | -|----------------------------|:-------:|:---------------:|:--------:|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| type | string | | x | `custom:windrose-card`. | -| title | string | | - | The card title. | -| wind_direction_entity | string | | x | The wind direction entity, having directing in degrees as the state. | -| windspeed_entities | object | | x | One are more windspeed entities. Only the first is used for the windrose. (for now) | -| refresh_interval | number | 300 | - | Refresh interval in seconds | -| hours_to_show | number | 4 | - | Show winddata for the last number of hours. | -| max_width | number | null | - | Use to limit the with (and height) of the windrose. | -| windspeed_bar_location | string | bottom | - | Location of the speed bar graph: `bottom`, `right` | -| windspeed_bar_full | boolean | true | - | When true, renders all wind ranges, when false, doesn't render the speed range without measurements. | -| wind_direction_unit | string | degrees | - | Wind direction unit, options: `degrees`, `letters`. Where letters being N, NE upto 32 directions. | -| input_speed_unit | string | mps | - | Windspeed unit of measurement, options: `mps`, `kph`, `mph`, `knots`. | -| output_speed_unit | string | bft | - | Windspeed unit used on card, options: `mps`, `kph`, `mph`, `knots`, `bft`. | -| direction_compensation | number | 0 | - | Compensate the measured direction in degrees. | -| cardinal_direction_letters | string | NESW | - | The cardinal letters used in the windrose. | -| wind_direction_count | string | 16 | - | How many wind direction the windrose can display, min. 4 max. 32 | -| matching_strategy | string | direction-first | - | How to match direction and speed measurements. Find a speed with each direction or a direction with each speed measurement. Options: `direction-frist`, `speed-first` | -| direction_speed_time_diff | string | 1 | - | How many seconds a speed measurement time can be earlier or later then the direction measurement time. Or the other way around, depending on thie matching_strategy | +| Name | Type | Default | Required | Description | +|---------------------------|:-------:|:----------------------------:|:--------:|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| type | string | | x | `custom:windrose-card`. | +| title | string | | - | The card title. | +| wind_direction_entity | string | | x | The wind direction entity, having directing in degrees as the state. | +| windspeed_entities | object | | x | One are more windspeed entities. Only the first is used for the windrose. (for now) | +| refresh_interval | number | 300 | - | Refresh interval in seconds | +| hours_to_show | number | 4 | - | Show winddata for the last number of hours. | +| max_width | number | null | - | Use to limit the with (and height) of the windrose. | +| windspeed_bar_location | string | bottom | - | Location of the speed bar graph: `bottom`, `right` | +| windspeed_bar_full | boolean | true | - | When true, renders all wind ranges, when false, doesn't render the speed range without measurements. | +| wind_direction_unit | string | degrees | - | Wind direction unit, options: `degrees`, `letters`. Where letters being N, NE upto 32 directions. | +| input_speed_unit | string | mps | - | Windspeed unit of measurement, options: `mps`, `kph`, `mph`, `knots`. | +| output_speed_unit | string | bft | - | Windspeed unit used on card, options: `mps`, `kph`, `mph`, `knots`, `bft`. | +| speed_range_step | number | depends on output speed unit | - | Sets the speed range step to use. Not possible for output speed unit bft (Beaufort) . | +| speed_range_max | number | depends on output speed unit | - | Sets the speed range max to use. Not possible for output speed unit bft (Beaufort). For example: step 5, max 20 creates ranges: 0-5, 5-10, 10-15, 15-20, 20-infinity | +| direction_compensation | number | 0 | - | Compensate the measured direction in degrees. | +| cardinal_direction_letters | string | NESW | - | The cardinal letters used in the windrose. | +| wind_direction_count | string | 16 | - | How many wind direction the windrose can display, min. 4 max. 32 | +| matching_strategy | string | direction-first | - | How to match direction and speed measurements. Find a speed with each direction or a direction with each speed measurement. Options: `direction-frist`, `speed-first` | +| direction_speed_time_diff | string | 1 | - | How many seconds a speed measurement time can be earlier or later then the direction measurement time. Or the other way around, depending on thie matching_strategy | #### Object windspeed_entities diff --git a/src/CardConfig.ts b/src/CardConfig.ts index 2569822..124cf9d 100644 --- a/src/CardConfig.ts +++ b/src/CardConfig.ts @@ -10,6 +10,8 @@ export interface CardConfig { wind_direction_unit: string; input_speed_unit: string; output_speed_unit: string; + speed_range_step: number; + speed_range_max: number; direction_compensation: number; windspeed_bar_location: string; diff --git a/src/CardConfigWrapper.ts b/src/CardConfigWrapper.ts index a30d99b..4dd83c0 100644 --- a/src/CardConfigWrapper.ts +++ b/src/CardConfigWrapper.ts @@ -17,6 +17,8 @@ export class CardConfigWrapper { windDirectionUnit: string; inputSpeedUnit: string; outputSpeedUnit: string; + speedRangeStep: number | undefined; + speedRangeMax: number | undefined; matchingStrategy: string; directionSpeedTimeDiff: number; @@ -41,6 +43,8 @@ export class CardConfigWrapper { wind_direction_unit: GlobalConfig.defaultWindDirectionUnit, input_speed_unit: GlobalConfig.defaultInputSpeedUnit, output_speed_unit: GlobalConfig.defaultOutputSpeedUnit, + speed_range_step: undefined, + speed_range_max: undefined, direction_compensation: 0, cardinal_direction_letters: GlobalConfig.defaultCardinalDirectionLetters, matching_strategy: GlobalConfig.defaultMatchingStategy, @@ -63,6 +67,9 @@ export class CardConfigWrapper { this.windDirectionUnit = this.checkWindDirectionUnit(); this.inputSpeedUnit = this.checkInputSpeedUnit(); this.outputSpeedUnit = this.checkOutputSpeedUnit(); + this.speedRangeStep = this.checkSpeedRangeStep(); + this.speedRangeMax = this.checkSpeedRangeMax(); + this.checkSpeedRangeCombi(); this.matchingStrategy = this.checkMatchingStrategy(); this.directionSpeedTimeDiff = this.checkDirectionSpeedTimeDiff(); this.filterEntitiesQueryParameter = this.createEntitiesQueryParameter(); @@ -204,6 +211,38 @@ export class CardConfigWrapper { return GlobalConfig.defaultOutputSpeedUnit; } + private checkSpeedRangeStep(): number | undefined { + if (this.cardConfig.speed_range_step && isNaN(this.cardConfig.speed_range_step)) { + throw new Error('WindRoseCard: Invalid speed_range_step, should be a positive number.'); + } else if (this.cardConfig.max_width <= 0) { + throw new Error('WindRoseCard: Invalid speed_range_step, should be a positive number.') + } else if (this.cardConfig.speed_range_step) { + return this.cardConfig.speed_range_step; + } + return undefined; + } + + private checkSpeedRangeMax(): number | undefined { + if (this.cardConfig.speed_range_max && isNaN(this.cardConfig.speed_range_max)) { + throw new Error('WindRoseCard: Invalid speed_range_max, should be a positive number.'); + } else if (this.cardConfig.max_width <= 0) { + throw new Error('WindRoseCard: Invalid speed_range_max, should be a positive number.') + } else if (this.cardConfig.speed_range_max) { + return this.cardConfig.speed_range_max; + } + return undefined; + } + + private checkSpeedRangeCombi(): void { + if (this.outputSpeedUnit === 'bft' && (this.speedRangeStep || this.speedRangeMax)) { + throw new Error("WindRoseCard: speed_range_step and/or speed_range_max should not be set when using output " + + "speed unit Beaufort (bft). Beaufort uses fixed speed ranges."); + } + if ((this.speedRangeStep && !this.speedRangeMax) || (!this.speedRangeStep && this.speedRangeMax)) { + throw new Error("WindRoseCard: speed_range_step and speed_range_max should both be set.") + } + } + private checkMatchingStrategy(): string { if (this.cardConfig.matching_strategy) { if (this.cardConfig.matching_strategy !== 'direction-first' && this.cardConfig.matching_strategy !== 'speed-first') { diff --git a/src/WindBarCalculator.ts b/src/WindBarCalculator.ts index 3deb66a..b499a8a 100644 --- a/src/WindBarCalculator.ts +++ b/src/WindBarCalculator.ts @@ -1,10 +1,10 @@ -import {SpeedUnits, WindSpeedConverter} from "./WindSpeedConverter"; +import {WindSpeedConverter} from "./WindSpeedConverter"; import {WindBarData} from "./WindBarData"; import {WindBarConfig} from "./WindBarConfig"; export class WindBarCalculator { - readonly windSpeedConverter = new WindSpeedConverter(); + readonly windSpeedConverter: WindSpeedConverter; readonly config: WindBarConfig; speeds: number[] = []; modified = false; @@ -15,12 +15,12 @@ export class WindBarCalculator { speedConverterFunction: (speed: number) => number; rangeCount: number; - constructor(config: WindBarConfig) { + constructor(config: WindBarConfig, windSpeedConverter: WindSpeedConverter) { this.config = config; - this.speedRangeFunction = this.windSpeedConverter.getRangeFunction(this.config.outputUnit); - this.speedConverterFunction = this.windSpeedConverter.getSpeedConverter(this.config.inputUnit, - this.config.outputUnit); - this.rangeCount = SpeedUnits.getSpeedUnit(this.config.outputUnit).speedRanges.length; + this.windSpeedConverter = windSpeedConverter; + this.speedRangeFunction = this.windSpeedConverter.getRangeFunction() + this.speedConverterFunction = this.windSpeedConverter.getSpeedConverter(); + this.rangeCount = this.windSpeedConverter.getRangeCount(); } addSpeeds(speeds: number[]) { diff --git a/src/WindBarCanvas.ts b/src/WindBarCanvas.ts index b206353..97152d9 100644 --- a/src/WindBarCanvas.ts +++ b/src/WindBarCanvas.ts @@ -3,19 +3,21 @@ import {ColorUtil} from "./ColorUtil"; import {WindBarData} from "./WindBarData"; import {WindBarConfig} from "./WindBarConfig"; import {GlobalConfig} from "./GlobalConfig"; -import {SpeedRange, SpeedUnit, SpeedUnits} from "./WindSpeedConverter"; +import {SpeedRange, WindSpeedConverter} from "./WindSpeedConverter"; export class WindBarCanvas { readonly colorUtil: ColorUtil; readonly config: WindBarConfig; - readonly speedUnit: SpeedUnit; + readonly windSpeedConverter: WindSpeedConverter; + readonly outputUnitName: string; readonly speedRanges: SpeedRange[]; - constructor(config: WindBarConfig) { + constructor(config: WindBarConfig, windSpeedConverter: WindSpeedConverter) { this.config = config; - this.speedUnit = SpeedUnits.getSpeedUnit(this.config.outputUnit) - this.speedRanges = this.speedUnit.speedRanges; + this.windSpeedConverter = windSpeedConverter; + this.outputUnitName = this.windSpeedConverter.getOutputSpeedUnit().name; + this.speedRanges = this.windSpeedConverter.getOutputSpeedUnit().speedRanges; this.colorUtil = new ColorUtil(this.speedRanges.length); } @@ -124,7 +126,7 @@ export class WindBarCanvas { canvasContext.textAlign = 'center'; canvasContext.textBaseline = 'bottom'; canvasContext.fillStyle = GlobalConfig.getTextColor(); - canvasContext.fillText(this.speedUnit.name, this.config.posX + (this.config.height / 2), this.config.posY - this.config.length - 2); + canvasContext.fillText(this.outputUnitName, this.config.posX + (this.config.height / 2), this.config.posY - this.config.length - 2); canvasContext.fill(); } @@ -187,7 +189,7 @@ export class WindBarCanvas { canvasContext.textAlign = 'right'; canvasContext.textBaseline = 'bottom'; canvasContext.fillStyle = GlobalConfig.getTextColor(); - canvasContext.fillText(this.speedUnit.name, this.config.posX + this.config.length, this.config.posY); + canvasContext.fillText(this.outputUnitName, this.config.posX + this.config.length, this.config.posY); canvasContext.fill(); } diff --git a/src/WindDirectionCalculator.ts b/src/WindDirectionCalculator.ts index c89370f..130eb11 100644 --- a/src/WindDirectionCalculator.ts +++ b/src/WindDirectionCalculator.ts @@ -3,7 +3,7 @@ import {WindDirectionData} from "./WindDirectionData"; import {WindSpeedConverter} from "./WindSpeedConverter"; export class WindDirectionCalculator { - readonly windSpeedConverter = new WindSpeedConverter(); + readonly windSpeedConverter: WindSpeedConverter; data = new WindDirectionData(); speeds: number[] = []; speedRangeCounts: number[] = []; @@ -12,12 +12,13 @@ export class WindDirectionCalculator { speedRangeFunction: (speed: number) => number; speedConverterFunction: (speed: number) => number; - constructor(minDegrees: number, centerDegrees: number, maxDegrees: number, config: WindRoseConfig) { + constructor(minDegrees: number, centerDegrees: number, maxDegrees: number, config: WindRoseConfig, + windSpeedConverter: WindSpeedConverter) { this.data.centerDegrees = centerDegrees; this.config = config; - this.speedRangeFunction = this.windSpeedConverter.getRangeFunction(this.config.outputUnit); - this.speedConverterFunction = this.windSpeedConverter.getSpeedConverter(this.config.inputUnit, - this.config.outputUnit); + this.windSpeedConverter = windSpeedConverter; + this.speedRangeFunction = this.windSpeedConverter.getRangeFunction(); + this.speedConverterFunction = this.windSpeedConverter.getSpeedConverter(); if (minDegrees < 0) { this.data.minDegrees = minDegrees + 360; } else { diff --git a/src/WindRoseCalculator.ts b/src/WindRoseCalculator.ts index 0b94f5b..92b66ae 100644 --- a/src/WindRoseCalculator.ts +++ b/src/WindRoseCalculator.ts @@ -6,7 +6,7 @@ import {WindDirectionConverter} from "./WindDirectionConverter"; export class WindRoseCalculator { - readonly windSpeedConverter = new WindSpeedConverter(); + readonly windSpeedConverter: WindSpeedConverter; readonly windDirectionConverter = new WindDirectionConverter(); data = new WindRoseData(); @@ -21,17 +21,18 @@ export class WindRoseCalculator { speedRangeFunction: (speed: number) => number; speedConverterFunction: (speed: number) => number; - constructor(config: WindRoseConfig) { + constructor(config: WindRoseConfig, windSpeedConverter: WindSpeedConverter) { this.config = config; - this.speedRangeFunction = this.windSpeedConverter.getRangeFunction(this.config.outputUnit); - this.speedConverterFunction = this.windSpeedConverter.getSpeedConverter(this.config.inputUnit, - this.config.outputUnit); + this.windSpeedConverter = windSpeedConverter; + this.speedRangeFunction = this.windSpeedConverter.getRangeFunction(); + this.speedConverterFunction = this.windSpeedConverter.getSpeedConverter(); const leaveDegrees = 360 / config.windDirectionCount; for (let i = 0; i < config.windDirectionCount; i++) { const degrees = (i * leaveDegrees); const minDegrees = degrees - (leaveDegrees / 2); const maxDegrees = degrees + (leaveDegrees / 2); - this.windDirections.push(new WindDirectionCalculator(minDegrees, degrees, maxDegrees, this.config)); + this.windDirections.push(new WindDirectionCalculator(minDegrees, degrees, maxDegrees, this.config, + windSpeedConverter)); } } diff --git a/src/WindRoseCanvas.ts b/src/WindRoseCanvas.ts index 95f85d5..ae63ebc 100644 --- a/src/WindRoseCanvas.ts +++ b/src/WindRoseCanvas.ts @@ -4,17 +4,19 @@ import {DrawUtil} from "./DrawUtil"; import {WindDirectionData} from "./WindDirectionData"; import {ColorUtil} from "./ColorUtil"; import {GlobalConfig} from "./GlobalConfig"; -import {SpeedUnits} from "./WindSpeedConverter"; +import {WindSpeedConverter} from "./WindSpeedConverter"; export class WindRoseCanvas { readonly colorUtil: ColorUtil; readonly config: WindRoseConfig; + readonly windSpeedConverter: WindSpeedConverter; readonly rangeCount: number; windRoseData!: WindRoseData; - constructor(config: WindRoseConfig) { + constructor(config: WindRoseConfig, windSpeedConverter: WindSpeedConverter) { this.config = config; - this.rangeCount = SpeedUnits.getSpeedUnit(this.config.outputUnit).speedRanges.length; + this.windSpeedConverter = windSpeedConverter; + this.rangeCount = this.windSpeedConverter.getRangeCount(); this.colorUtil = new ColorUtil(this.rangeCount); } diff --git a/src/WindRoseCard.ts b/src/WindRoseCard.ts index de4a3fc..a1b3abe 100644 --- a/src/WindRoseCard.ts +++ b/src/WindRoseCard.ts @@ -10,6 +10,7 @@ import {WindBarData} from "./WindBarData"; import {customElement, query} from "lit/decorators" import {CardConfigWrapper} from "./CardConfigWrapper"; import {MeasurementMatcher} from "./MeasurementMatcher"; +import {WindSpeedConverter} from "./WindSpeedConverter"; (window as any).customCards = (window as any).customCards || []; (window as any).customCards.push({ @@ -20,7 +21,7 @@ import {MeasurementMatcher} from "./MeasurementMatcher"; /* eslint no-console: 0 */ console.info( - `%c WINROSE-CARD %c Version 0.5.0 `, + `%c WINROSE-CARD %c Version 0.6.0 `, 'color: orange; font-weight: bold; background: black', 'color: white; font-weight: bold; background: dimgray', ); @@ -41,6 +42,7 @@ export class WindRoseCard extends LitElement { @query('.card-content') parentDiv!: HTMLDivElement; windRoseConfigFactory!: WindRoseConfigFactory; + windSpeedConverter!: WindSpeedConverter; windRoseCanvas: WindRoseCanvas | undefined; windBarCanvases: WindBarCanvas[] = []; @@ -155,15 +157,17 @@ export class WindRoseCard extends LitElement { this.windRoseConfigFactory = new WindRoseConfigFactory(cardConfig); const windRoseConfig = this.windRoseConfigFactory.createWindRoseConfig(canvasWidth); - this.windRoseCalculator = new WindRoseCalculator(windRoseConfig); - this.windRoseCanvas = new WindRoseCanvas(windRoseConfig); + this.windSpeedConverter = new WindSpeedConverter(this.cardConfig.inputSpeedUnit, + this.cardConfig.outputSpeedUnit, this.cardConfig.speedRangeStep, this.cardConfig.speedRangeMax); + this.windRoseCalculator = new WindRoseCalculator(windRoseConfig, this.windSpeedConverter); + this.windRoseCanvas = new WindRoseCanvas(windRoseConfig, this.windSpeedConverter); const windBarConfigs = this.windRoseConfigFactory.createWindBarConfigs(canvasWidth); this.windBarCalculators = []; this.windBarCanvases = []; for (let i = 0; i < this.cardConfig.windBarCount(); i++) { - this.windBarCalculators.push(new WindBarCalculator(windBarConfigs[i])); - this.windBarCanvases.push(new WindBarCanvas(windBarConfigs[i])); + this.windBarCalculators.push(new WindBarCalculator(windBarConfigs[i], this.windSpeedConverter)); + this.windBarCanvases.push(new WindBarCanvas(windBarConfigs[i], this.windSpeedConverter)); } } @@ -216,12 +220,12 @@ export class WindRoseCard extends LitElement { this.canvas.width = canvasWidth; this.canvas.height = this.windRoseConfigFactory.canvasHeight as number; const windRoseConfig = this.windRoseConfigFactory.createWindRoseConfig(canvasWidth); - this.windRoseCanvas = new WindRoseCanvas(windRoseConfig); + this.windRoseCanvas = new WindRoseCanvas(windRoseConfig, this.windSpeedConverter); const windBarConfigs = this.windRoseConfigFactory.createWindBarConfigs(canvasWidth); this.windBarCanvases = []; for (const windBarConfig of windBarConfigs) { - this.windBarCanvases.push(new WindBarCanvas(windBarConfig)); + this.windBarCanvases.push(new WindBarCanvas(windBarConfig, this.windSpeedConverter)); } } diff --git a/src/WindSpeedConverter.ts b/src/WindSpeedConverter.ts index 512b350..3d5b9fc 100644 --- a/src/WindSpeedConverter.ts +++ b/src/WindSpeedConverter.ts @@ -1,34 +1,12 @@ -export class WindSpeedConverter { - - getSpeedConverter(inputUnit: string, outputUnit: string): (speed: number) => number { - if (inputUnit === outputUnit) { - return (inputSpeed: number) => inputSpeed; - } else if (inputUnit === 'mps') { - return SpeedUnits.getSpeedUnit(outputUnit).fromMpsFunc; - } - const toMpsFunction = SpeedUnits.getSpeedUnit(inputUnit).toMpsFunc; - const fromMpsFunction = SpeedUnits.getSpeedUnit(outputUnit).fromMpsFunc; - return (speed: number) => fromMpsFunction(toMpsFunction(speed)); - } - - getRangeFunction(outputUnit: string): (speed: number) => number { - const speedRanges = SpeedUnits.getSpeedUnit(outputUnit).speedRanges; - return (speed: number) => { - const speedRange = speedRanges.find(speedRange => speedRange.isRangeMatch(speed)); - if (speedRange) { - return speedRange.range; - } - throw new Error("Speed is not in a speedrange: " + speed + " unit: " + outputUnit); - } - } -} export class SpeedUnit { + public speedRanges: SpeedRange[] = []; constructor( public readonly name: string, public readonly toMpsFunc: (speed: number) => number, public readonly fromMpsFunc: (speed: number) => number, - public readonly speedRanges: SpeedRange[]) { + public readonly speedRangeStep: number | undefined, + public readonly speedRangeMax: number | undefined) { } } @@ -45,126 +23,124 @@ export class SpeedRange { } } -export class SpeedUnits { - static speedRangesBft = [ - new SpeedRange(0, 0, 0.3), - new SpeedRange(1, 0.3, 1.6), - new SpeedRange(2, 1.6, 3.4), - new SpeedRange(3, 3.4, 5.5), - new SpeedRange(4, 5.5, 8), - new SpeedRange(5, 8, 10.8), - new SpeedRange(6, 10.8, 13.9), - new SpeedRange(7, 13.9, 17.2), - new SpeedRange(8, 17.2, 20.8), - new SpeedRange(9, 20.8, 24.5), - new SpeedRange(10, 24.5, 28.5), - new SpeedRange(11, 28.5, 32.7), - new SpeedRange(12, 32.7, -1) - ]; - static speedRangesMps = [ - new SpeedRange(0, 0, 1), - new SpeedRange(1, 1, 2), - new SpeedRange(2, 2, 4), - new SpeedRange(3, 4, 6), - new SpeedRange(4, 6, 8), - new SpeedRange(5, 8, 11), - new SpeedRange(6, 11, 14), - new SpeedRange(7, 14, 17), - new SpeedRange(8, 17, 20), - new SpeedRange(9, 20, 25), - new SpeedRange(10, 25, 29), - new SpeedRange(11, 29, 33), - new SpeedRange(12, 33, -1) - ]; - static speedRangesMph = [ - new SpeedRange(0, 0, 5), - new SpeedRange(1, 5, 10), - new SpeedRange(2, 10, 15), - new SpeedRange(3, 15, 20), - new SpeedRange(4, 20, 30), - new SpeedRange(5, 30, 40), - new SpeedRange(6, 40, 50), - new SpeedRange(7, 50, 60), - new SpeedRange(8, 60, 70), - new SpeedRange(9, 70, -1), - ]; - static speedRangesKph = [ - new SpeedRange(0, 0, 10), - new SpeedRange(1, 10, 20), - new SpeedRange(2, 20, 30), - new SpeedRange(3, 30, 40), - new SpeedRange(4, 40, 50), - new SpeedRange(5, 50, 60), - new SpeedRange(6, 60, 70), - new SpeedRange(7, 70, 80), - new SpeedRange(8, 80, -1), - ]; - static speedRangesFps = [ - new SpeedRange(0, 0, 5), - new SpeedRange(1, 5, 10), - new SpeedRange(2, 10, 20), - new SpeedRange(3, 20, 30), - new SpeedRange(4, 30, 40), - new SpeedRange(5, 40, 50), - new SpeedRange(6, 50, 60), - new SpeedRange(7, 60, 70), - new SpeedRange(8, 70, 80), - new SpeedRange(9, 80, 90), - new SpeedRange(10, 90, -1), - ]; - static speedRangesKnots = [ - new SpeedRange(0, 0, 5), - new SpeedRange(1, 5, 10), - new SpeedRange(2, 10, 15), - new SpeedRange(3, 15, 20), - new SpeedRange(4, 20, 30), - new SpeedRange(5, 30, 40), - new SpeedRange(6, 40, 50), - new SpeedRange(7, 50, 60), - new SpeedRange(8, 60, -1), - ]; - - static bft = new SpeedUnit("Beaufort", - (speed: number) => speed, - (speed: number) => speed, - SpeedUnits.speedRangesBft - ) - - static mps = new SpeedUnit("m/s", +export class WindSpeedConverter { + + readonly bft = new SpeedUnit("Beaufort", (speed: number) => speed, + (speed: number) => speed, undefined, undefined); + + readonly mps = new SpeedUnit("m/s", (speed: number) => speed, - SpeedUnits.speedRangesMps - ); - static kph = new SpeedUnit("km/h", + (speed: number) => speed, 5, 30); + + readonly kph = new SpeedUnit("km/h", (speed: number) => speed / 3.6, - (speed: number) => speed * 3.6, - SpeedUnits.speedRangesKph); + (speed: number) => speed * 3.6, 10, 100); - static mph = new SpeedUnit("m/h", + readonly mph = new SpeedUnit("m/h", (speed: number) => speed / 2.2369, - (speed: number) => speed * 2.2369, - SpeedUnits.speedRangesMph); + (speed: number) => speed * 2.2369, 10, 70); - static fps = new SpeedUnit("ft/s", + readonly fps = new SpeedUnit("ft/s", (speed: number) => speed / 3.2808399, - (speed: number) => speed * 3.2808399, - SpeedUnits.speedRangesFps); + (speed: number) => speed * 3.2808399, 10, 100); - static knots = new SpeedUnit("knots", + readonly knots = new SpeedUnit("knots", (speed: number) => speed / 1.9438444924406, - (speed: number) => speed * 1.9438444924406, - SpeedUnits.speedRangesKnots); + (speed: number) => speed * 1.9438444924406, 5, 60); + + readonly inputSpeedUnit: SpeedUnit; + readonly outputSpeedUnit: SpeedUnit; + + constructor(private readonly inputUnit: string, + private readonly outputUnit: string, + private readonly rangeStep?: number, + private readonly rangeMax?: number) { + this.inputSpeedUnit = this.getSpeedUnit(this.inputUnit); + this.outputSpeedUnit = this.getSpeedUnit(this.outputUnit); - static getSpeedUnit(unit: string): SpeedUnit { + if (outputUnit === 'bft') { + this.outputSpeedUnit.speedRanges = this.generateBeaufortSpeedRanges(); + + } else if (rangeStep && rangeMax) { + this.outputSpeedUnit.speedRanges = this.generateSpeedRanges(rangeStep, rangeMax); + + } else { + this.outputSpeedUnit.speedRanges = this.generateSpeedRanges(this.outputSpeedUnit.speedRangeStep!, + this.outputSpeedUnit.speedRangeMax!); + } + console.log('Output: ', this.outputSpeedUnit); + } + + getOutputSpeedUnit(): SpeedUnit { + return this.outputSpeedUnit; + } + + getSpeedConverter(): (speed: number) => number { + if (this.inputUnit === this.outputUnit) { + return (inputSpeed: number) => inputSpeed; + } else if (this.inputUnit === 'mps') { + return this.outputSpeedUnit.fromMpsFunc; + } + const toMpsFunction = this.inputSpeedUnit.toMpsFunc; + const fromMpsFunction = this.outputSpeedUnit.fromMpsFunc; + return (speed: number) => fromMpsFunction(toMpsFunction(speed)); + } + + getRangeFunction(): (speed: number) => number { + return (speed: number) => { + const speedRange = this.outputSpeedUnit.speedRanges.find(speedRange => speedRange.isRangeMatch(speed)); + if (speedRange) { + return speedRange.range; + } + throw new Error("Speed is not in a speedrange: " + speed + " unit: " + this.outputUnit); + } + } + + getRangeCount(): number { + return this.outputSpeedUnit.speedRanges.length; + } + + private getSpeedUnit(unit: string): SpeedUnit { switch (unit) { - case 'bft': return SpeedUnits.bft; - case 'mps': return SpeedUnits.mps; - case 'kph': return SpeedUnits.kph; - case 'mph': return SpeedUnits.mph; - case 'fps': return SpeedUnits.fps; - case 'knots': return SpeedUnits.knots; + case 'bft': return this.bft; + case 'mps': return this.mps; + case 'kph': return this.kph; + case 'mph': return this.mph; + case 'fps': return this.fps; + case 'knots': return this.knots; default: throw new Error("Unknown speed unit: " + unit); } } -} + private generateSpeedRanges(step: number, max: number): SpeedRange[] { + const speedRanges = [] as SpeedRange[]; + let currentSpeed = 0; + let range = 0; + while (currentSpeed <= max - step) { + speedRanges.push(new SpeedRange(range, currentSpeed, currentSpeed + step)); + range++; + currentSpeed += step; + } + speedRanges.push(new SpeedRange(range, currentSpeed, -1)); + return speedRanges; + } + + private generateBeaufortSpeedRanges(): SpeedRange[] { + return [ + new SpeedRange(0, 0, 0.3), + new SpeedRange(1, 0.3, 1.6), + new SpeedRange(2, 1.6, 3.4), + new SpeedRange(3, 3.4, 5.5), + new SpeedRange(4, 5.5, 8), + new SpeedRange(5, 8, 10.8), + new SpeedRange(6, 10.8, 13.9), + new SpeedRange(7, 13.9, 17.2), + new SpeedRange(8, 17.2, 20.8), + new SpeedRange(9, 20.8, 24.5), + new SpeedRange(10, 24.5, 28.5), + new SpeedRange(11, 28.5, 32.7), + new SpeedRange(12, 32.7, -1) + ]; + } +}