UNPKG

@itwin/ecschema-metadata

Version:

ECObjects core concepts in typescript

115 lines 5.63 kB
/*--------------------------------------------------------------------------------------------- * Copyright (c) Bentley Systems, Incorporated. All rights reserved. * See LICENSE.md in the project root for license terms and full copyright notice. *--------------------------------------------------------------------------------------------*/ import { BentleyError, BentleyStatus } from "@itwin/core-bentley"; import { SchemaItem } from "../Metadata/SchemaItem"; import { Unit } from "../Metadata/Unit"; import { SchemaKey } from "../SchemaKey"; import { UnitConversion } from "./UnitConversion"; import { UnitGraph } from "./UnitTree"; /** * Class constructed with SchemaContext and used to calculate [[UnitConversion]] between Units * @internal */ export class UnitConverter { _context; _uGraph; /** * Create Converter context * @param _context SchemaContext with contexts added to it. */ constructor(_context) { this._context = _context; this._uGraph = new 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.parseFullName(fromUnit); const [toSchemaName, toSchemaItemName] = SchemaItem.parseFullName(toUnit); const fromSchemaKey = new SchemaKey(fromSchemaName); const toSchemaKey = new SchemaKey(toSchemaName); const fromSchema = await this._context.getSchema(fromSchemaKey); const toSchema = await this._context.getSchema(toSchemaKey); if (!fromSchema || !toSchema) { throw new BentleyError(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.identity; const areCompatible = await Unit.areCompatible(from, to); if (!areCompatible) throw new BentleyError(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 BentleyError(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.identity; const toMap = toMapStore.get(to.key.fullName) || 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.parseFullName(key); if (schemaItemName === "ONE" || value === 0) { fromBaseUnits.delete(key); } } for (const [key, value] of toBaseUnits.entries()) { const [, schemaItemName] = 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; } } //# sourceMappingURL=UnitConverter.js.map