Skip to content

Commit

Permalink
refactor: unit system functions now returning/taking definition objec…
Browse files Browse the repository at this point in the history
…ts and unit conversions in time series/data table
  • Loading branch information
claustres committed Sep 9, 2024
1 parent 48ee543 commit 1986a06
Show file tree
Hide file tree
Showing 6 changed files with 48 additions and 66 deletions.
8 changes: 1 addition & 7 deletions core/client/components/chart/KDataTable.vue
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ import _ from 'lodash'
import moment from 'moment'
import Papa from 'papaparse'
import { ref, watch } from 'vue'
import { downloadAsBlob, convertTimeSerie } from '../../utils'
import { downloadAsBlob } from '../../utils'
import { useSchema } from '../../composables'
import { Units } from '../../units.js'
import { Time } from '../../time.js'
Expand Down Expand Up @@ -71,7 +71,6 @@ const height = ref(0)
// Used to store template compilers per field
const compilers = {}
const exportCompilers = {}
let propertiesToConvert = []
// Watch
watch(() => props.tables, update)
Expand All @@ -82,14 +81,11 @@ async function update () {
await compile(props.schema)
columns.value = []
const invisibleColumns = []
propertiesToConvert = []
_.forOwn(schema.value.properties, (value, key) => {
const type = _.get(value, 'type')
// FIXME: allow for custom representation of complex objects
if (type === 'object') return
const label = _.get(value, 'field.label', _.get(value, 'field.helper', key))
const convertToDefaultUnit = _.get(value, 'field.defaultUnit', false)
if (convertToDefaultUnit) propertiesToConvert.push(key)
const visible = _.get(value, 'field.visible', true)
if (!visible) invisibleColumns.push(key)
const formatter = _.has(value, 'field.formatter') ? _.get(props.formatters, value.field.formatter) : null
Expand Down Expand Up @@ -135,7 +131,6 @@ async function update () {
rows.value = []
for (const table of props.tables) {
const data = await table.data
convertTimeSerie(data, table.variable, propertiesToConvert)
rows.value = rows.value.concat(data)
}
}
Expand All @@ -148,7 +143,6 @@ async function exportData (options = {}) {
for (let i = 0; i < props.tables.length; i++) {
const table = props.tables[i]
const data = await table.data
await convertTimeSerie(data, table.variable, propertiesToConvert)
for (const item of data) {
const row = {}
_.forOwn(schema.value.properties, (value, key) => {
Expand Down
15 changes: 3 additions & 12 deletions core/client/components/chart/KTimeSeriesChart.vue
Original file line number Diff line number Diff line change
Expand Up @@ -81,9 +81,6 @@ function getUnit (timeSerie) {
function getTargetUnit (timeSerie) {
return _.get(timeSerie, 'variable.targetUnit')
}
function setUnit (timeserie, targetUnit) {
_.set(timeserie, 'variable.unit', targetUnit)
}
function getZoom () {
const start = moment.utc(_.get(chart, 'scales.x.min'))
const end = moment.utc(_.get(chart, 'scales.x.max'))
Expand Down Expand Up @@ -246,8 +243,8 @@ function makeScales (datasets) {
let axisId = 0
for (const timeSerie of props.timeSeries) {
const unit = getUnit(timeSerie)
const targetUnit = getTargetUnit(timeSerie) || unit
const unitName = targetUnit.name
const targetUnit = getTargetUnit(timeSerie)
const unitName = (targetUnit ? targetUnit.name : unit.name)
if (!unit2axis.has(unitName)) {
// Ensure a related dataset does exist
const axisDatasets = _.filter(datasets, dataset => (_.get(dataset, 'targetUnit.name', _.get(dataset, 'unit.name')) === unitName))
Expand All @@ -257,12 +254,11 @@ function makeScales (datasets) {
axisDatasets.forEach(dataset => Object.assign(dataset, { yAxisID: axis }))
unit2axis.set(unitName, axis)
scales[axis] = _.merge({
targetUnit: unitName,
type: props.logarithmic ? 'logarithmic' : 'linear',
position: (axisId + 1) % 2 ? 'left' : 'right',
title: {
display: true,
text: i18n.tie(targetUnit.symbol)
text: i18n.tie(targetUnit ? targetUnit.symbol : unit.symbol)
},
ticks: {
callback: function (value, index, values) {
Expand All @@ -288,7 +284,6 @@ async function makeDatasets () {
const label = _.get(timeSerie, 'variable.label')
const unit = getUnit(timeSerie)
const targetUnit = getTargetUnit(timeSerie)
if (targetUnit) setUnit(timeSerie, targetUnit)
const data = await timeSerie.data
// No data ?
if (_.isEmpty(data)) continue
Expand All @@ -308,10 +303,6 @@ async function makeDatasets () {
if (_.has(item, yAxisKey)) {
let value = _.get(item, yAxisKey)
if (_.isFinite(value)) {
if (targetUnit) {
value = Units.convert(value, unit.name, targetUnit.name)
_.set(item, yAxisKey, value)
}
if (_.isNil(min[unitName]) || (value < min[unitName])) min[unitName] = value
if (_.isNil(max[unitName]) || (value > max[unitName])) max[unitName] = value
}
Expand Down
60 changes: 38 additions & 22 deletions core/client/units.js
Original file line number Diff line number Diff line change
Expand Up @@ -286,60 +286,76 @@ export const Units = {
},
// Get unit definition by name
getUnit (unit) {
return _.find(this.getUnits(), { name: unit })
// Not optimized
//return _.find(this.getUnits(), { name: unit })
let definition
_.forOwn(this.get(), (units, quantity) => {
// Already found ?
if (definition) return
else if (_.has(units, unit)) definition = Object.assign({ name: unit, quantity }, _.get(units, unit))
})
return definition
},
// Get unit symbol by unit name
// Get unit symbol by unit name/definition
getUnitSymbol (unit) {
const definition = this.getUnit(unit)
const definition = (typeof unit === 'object' ? unit : this.getUnit(unit))
return (definition ? i18n.tie(definition.symbol) : unit)
},
// Get default unit definition (if any) for a given quantity/unit name
// Get default unit definition (if any) for a given quantity/unit name/definition
getDefaultUnit (quantityOrUnit) {
if (!quantityOrUnit) return null
if (typeof quantityOrUnit === 'object') quantityOrUnit = quantityOrUnit.name
// Check for quantity first
let defaultUnit = Store.get(`units.default.${quantityOrUnit}`)
// If not check by matching quantity based on given unit
if (!defaultUnit) {
const baseUnit = this.getUnit(quantityOrUnit)
// Get default unit for this quantity instead
if (baseUnit) defaultUnit = this.getDefaultUnit(baseUnit.quantity)
} else {
// Jump from name to definition
defaultUnit = this.getUnit(defaultUnit)
}
return defaultUnit
},
// Set default unit name for a quantity
setDefaultUnit (quantity, unit) {
Store.set(`units.default.${quantity}`, unit)
},
// Get symbol of default unit (if any) for a given quantity/unit name
// Get symbol of default unit (if any) for a given quantity/unit name/definition
getDefaultUnitSymbol (quantityOrUnit) {
return this.getUnitSymbol(this.getDefaultUnit(quantityOrUnit))
},
// Get target unit for a source unit, will be default unit (if any) or source unit
// Get target unit definition for a source unit name/definition, will be default unit (if any) or source unit
getTargetUnit (sourceUnit) {
return this.getDefaultUnit(sourceUnit) || sourceUnit
return this.getDefaultUnit(sourceUnit) || (typeof sourceUnit === 'object' ? sourceUnit : this.getUnit(sourceUnit))
},
// Get target unit symbol for a source unit, will be default unit symbol (if any) or source unit symbol
// Get target unit symbol for a source unit name/definition, will be default unit symbol (if any) or source unit symbol
getTargetUnitSymbol (sourceUnit) {
return this.getUnitSymbol(this.getTargetUnit(sourceUnit))
},
// Convert between units by names
// Convert between units by names/definitions
// If target unit is not specified will use default unit (if any) for source unit
convert (value, sourceUnit, targetUnit) {
if (_.isNil(value)) {
logger.warn('[KDK] cannont convert a nil value')
return
}
if (_.isNil(value) || !_.isFinite(value)) return value
if (value === Number.MIN_VALUE || value === Number.MAX_VALUE) return value
// If target unit is same as source unit does nothing
if (targetUnit === sourceUnit) return value
if (typeof sourceUnit === 'string') sourceUnit = this.getUnit(sourceUnit)
if (typeof targetUnit === 'string') targetUnit = this.getUnit(targetUnit)
// If target unit is not given use default one
if (!targetUnit) targetUnit = this.getDefaultUnit(sourceUnit)
// Check if the target unit does exist
if (!targetUnit) return value
// Check if the source unit does exist
if (!math.Unit.isValuelessUnit(sourceUnit)) return value
let n = math.unit(value, sourceUnit)
n = n.toNumber(targetUnit)
// Check if the source/target unit does exist
if (!sourceUnit || !targetUnit) return value
// If target unit is same as source unit does nothing
if (sourceUnit && targetUnit && (targetUnit.name === sourceUnit.name)) return value
// Check if the source unit is declared in the units system
if (!math.Unit.isValuelessUnit(sourceUnit.name)) return value
// Check if the value is a valid number
if (!_.isFinite(value)) return value
// Now convert
let n = math.unit(value, sourceUnit.name)
n = n.toNumber(targetUnit.name)
// Remap from [-180,+180[ to [0,360[ for angles
n = (targetUnit === 'deg' ? (n < 0.0 ? n + 360.0 : n) : n)
n = (targetUnit.name === 'deg' ? (n < 0.0 ? n + 360.0 : n) : n)
return n
},
// Format display of source value in target unit, converting from source unit
Expand Down
1 change: 0 additions & 1 deletion core/client/utils/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ export * from './utils.account.js'
export * from './utils.actions.js'
export * from './utils.colors.js'
export * from './utils.content.js'
export * from './utils.data.js'
export * from './utils.locale.js'
export * from './utils.math.js'
export * from './utils.platform.js'
Expand Down
22 changes: 0 additions & 22 deletions core/client/utils/utils.data.js

This file was deleted.

8 changes: 6 additions & 2 deletions map/client/utils/utils.time-series.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,10 @@ async function getDataForVariable(data, variable, forecastLevel, runTime) {
// Aggregated variable available for feature ?
if (properties[name] && Array.isArray(properties[name])) {
// Build data structure as expected by visualisation
values = properties[name].map((value, index) => ({ time: moment.utc(times[name][index]).valueOf(), [name]: value }))
values = properties[name].map((value, index) => {
value = Units.convert(value, variable.unit, variable.targetUnit)
return { time: moment.utc(times[name][index]).valueOf(), [name]: value }
})
// Keep only selected value if multiple are provided for the same time (eg different forecasts)
if (variable.runTimes && runTime && !_.isEmpty(_.get(runTimes, name))) {
values = values.filter((value, index) => (runTimes[name][index] === runTime.toISOString()))
Expand Down Expand Up @@ -86,14 +89,15 @@ export function getTimeSeries({
const baseUnit = _.get(properties, 'unit', variable.unit)
// Known by the unit system ?
const unit = Units.getUnit(baseUnit) || { name: baseUnit }
const targetUnit = Units.getTargetUnit(baseUnit)
const serie = {
probedLocation: data,
data: getDataForVariable(data, variable, forecastLevel),
variable: {
name: variable.name,
label: `${i18n.tie(variable.label)} (${Units.getTargetUnitSymbol(baseUnit)})`,
unit,
targetUnit: Units.getTargetUnit(unit),
targetUnit,
chartjs: Object.assign({
parsing: {
xAxisKey: 'time',
Expand Down

0 comments on commit 1986a06

Please sign in to comment.