@itwin/ecschema-metadata
Version:
ECObjects core concepts in typescript
119 lines • 6.01 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.UnitConverter = void 0;
/*---------------------------------------------------------------------------------------------
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
* See LICENSE.md in the project root for license terms and full copyright notice.
*--------------------------------------------------------------------------------------------*/
const core_bentley_1 = require("@itwin/core-bentley");
const SchemaItem_1 = require("../Metadata/SchemaItem");
const Unit_1 = require("../Metadata/Unit");
const SchemaKey_1 = require("../SchemaKey");
const UnitConversion_1 = require("./UnitConversion");
const UnitTree_1 = require("./UnitTree");
/**
* Class constructed with SchemaContext and used to calculate [[UnitConversion]] between Units
* @internal
*/
class UnitConverter {
_context;
_uGraph;
/**
* Create Converter context
* @param _context SchemaContext with contexts added to it.
*/
constructor(_context) {
this._context = _context;
this._uGraph = new UnitTree_1.UnitGraph(this._context);
}
/**
* Find conversion between from and to units, formatted {schemaName}.{schemaItemName} or {schemaName}:{schemaItemName}
* @param fromUnit SchemaItem full name of source unit
* @param toUnit SchemaItem full name of target unit
* @returns [[UnitConversion]] converting fromUnit -> toUnit with a factor and an offset
* @throws Error if from and to Units' SchemaItem is not found in Schema or Schema prefix is not found in SchemaContext
* @throws Error if from and to Units do not belong to the same phenomenon
* @throws Error if definitions' SchemaItems cannot be found in its own or referenced Schemas
* @throws Error if base units of source and target unit do not match
*/
async calculateConversion(fromUnit, toUnit) {
const [fromSchemaName, fromSchemaItemName] = SchemaItem_1.SchemaItem.parseFullName(fromUnit);
const [toSchemaName, toSchemaItemName] = SchemaItem_1.SchemaItem.parseFullName(toUnit);
const fromSchemaKey = new SchemaKey_1.SchemaKey(fromSchemaName);
const toSchemaKey = new SchemaKey_1.SchemaKey(toSchemaName);
const fromSchema = await this._context.getSchema(fromSchemaKey);
const toSchema = await this._context.getSchema(toSchemaKey);
if (!fromSchema || !toSchema) {
throw new core_bentley_1.BentleyError(core_bentley_1.BentleyStatus.ERROR, "Cannot find from's and/or to's schema", () => {
return { from: fromUnit, fromSchema: fromSchemaName, to: toUnit, toSchema: toSchemaName };
});
}
const from = await this._uGraph.resolveUnit(fromSchemaItemName, fromSchema);
const to = await this._uGraph.resolveUnit(toSchemaItemName, toSchema);
return this.processUnits(from, to);
}
/**
* @param from Source unit converted from
* @param to Target unit converted to
* @internal
*/
async processUnits(from, to) {
if (from.key.matches(to.key))
return UnitConversion_1.UnitConversion.identity;
const areCompatible = await Unit_1.Unit.areCompatible(from, to);
if (!areCompatible)
throw new core_bentley_1.BentleyError(core_bentley_1.BentleyStatus.ERROR, `Source and target units do not belong to same phenomenon`, () => {
return { from, to };
});
// Add nodes and subsequent children to graph
await this._uGraph.addUnit(from);
await this._uGraph.addUnit(to);
const fromBaseUnits = new Map();
const toBaseUnits = new Map();
// Calculate map of UnitConversions to get between from -> base
const fromMapStore = this._uGraph.reduce(from, fromBaseUnits);
// Calculate map of UnitConversions to get between base -> to
const toMapStore = this._uGraph.reduce(to, toBaseUnits);
if (!this.checkBaseUnitsMatch(fromBaseUnits, toBaseUnits))
throw new core_bentley_1.BentleyError(core_bentley_1.BentleyStatus.ERROR, `Source and target units do not have matching base units`, () => {
return { from, to };
});
// Final calculations to get singular UnitConversion between from -> to
const fromMap = fromMapStore.get(from.key.fullName) || UnitConversion_1.UnitConversion.identity;
const toMap = toMapStore.get(to.key.fullName) || UnitConversion_1.UnitConversion.identity;
const fromInverse = fromMap.inverse();
return fromInverse.compose(toMap);
}
/**
* Check if fromBaseUnits's base units and exponents matches toBaseUnits's
* @param fromBaseUnits Map of base units for source unit
* @param toBaseUnits Map of base units for target unit
* @internal
*/
checkBaseUnitsMatch(fromBaseUnits, toBaseUnits) {
// Trim maps of "One" and value that equal zero as they do not affect the base units and calculations
for (const [key, value] of fromBaseUnits.entries()) {
const [, schemaItemName] = SchemaItem_1.SchemaItem.parseFullName(key);
if (schemaItemName === "ONE" || value === 0) {
fromBaseUnits.delete(key);
}
}
for (const [key, value] of toBaseUnits.entries()) {
const [, schemaItemName] = SchemaItem_1.SchemaItem.parseFullName(key);
if (schemaItemName === "ONE" || value === 0) {
toBaseUnits.delete(key);
}
}
if (fromBaseUnits.size !== toBaseUnits.size)
return false;
for (const key of fromBaseUnits.keys()) {
if (!toBaseUnits.has(key) || fromBaseUnits.get(key) !== toBaseUnits.get(key)) {
// Mismatching key or value
return false;
}
}
return true;
}
}
exports.UnitConverter = UnitConverter;
//# sourceMappingURL=UnitConverter.js.map