@lhncbc/ucum-lhc 4.1.8 → 4.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -86,14 +86,14 @@ var Ucum = {
86
86
  * displaying messages on a web site; should be blank when output is
87
87
  * to a file.
88
88
  */
89
- openEmphHTML_: '<span class="emphSpan">',
89
+ openEmphHTML_: ' <span class="emphSpan">',
90
90
 
91
91
  /**
92
92
  * Closing HTML used to emphasize portions of error messages. Used when
93
93
  * displaying messages on a web site; should be blank when output is
94
94
  * to a file.
95
95
  */
96
- closeEmphHTML_: '</span>',
96
+ closeEmphHTML_: '</span> ',
97
97
 
98
98
  /**
99
99
  * Message that is displayed when annotations are included in a unit
@@ -1817,6 +1817,81 @@ var UcumLhcUtils = /*#__PURE__*/function () {
1817
1817
  return returnObj;
1818
1818
  } // end convertUnitTo
1819
1819
 
1820
+ /**
1821
+ * Converts the given unit string into its base units, their exponents, and
1822
+ * a magnitude, and returns that data.
1823
+ * @param fromUnit the unit string to be converted to base units information
1824
+ * @param fromVal the number of "from" units to be converted
1825
+ * @returns an object with the properties:
1826
+ * 'msg': an array of one or more messages, if the string is invalid or
1827
+ * an error occurred, indicating the problem, or a suggestion of a
1828
+ * substitution such as the substitution of 'G' for 'Gauss', or
1829
+ * an empty array if no messages were generated. If this is not empty,
1830
+ * no other information will be returned.
1831
+ * 'magnitude': the new value when fromVal units of fromUnits is expressed in the base units.
1832
+ * 'fromUnitIsSpecial': whether the input unit fromUnit is a "special unit"
1833
+ * as defined in UCUM. This means there is some function applied to convert
1834
+ * between fromUnit and the base units, so the returned magnitude is likely not
1835
+ * useful as a scale factor for other conversions (i.e., it only has validity
1836
+ * and usefulness for the input values that produced it).
1837
+ * 'unitToExp': a map of base units in uStr to their exponent
1838
+ */
1839
+
1840
+ }, {
1841
+ key: "convertToBaseUnits",
1842
+ value: function convertToBaseUnits(fromUnit, fromVal) {
1843
+ var inputUnitLookup = this.getSpecifiedUnit(fromUnit, 'validate');
1844
+ var retObj = {};
1845
+ var unit = inputUnitLookup.unit;
1846
+ retObj.msg = inputUnitLookup.retMsg || [];
1847
+
1848
+ if (!unit) {
1849
+ var _inputUnitLookup$retM;
1850
+
1851
+ if (((_inputUnitLookup$retM = inputUnitLookup.retMsg) === null || _inputUnitLookup$retM === void 0 ? void 0 : _inputUnitLookup$retM.length) == 0) retObj.msg.push('Could not find unit information for ' + fromUnit);
1852
+ } else if (unit.isArbitrary_) {
1853
+ retObj.msg.push('Arbitrary units cannot be converted to base units or other units.');
1854
+ } else if (retObj.msg.length == 0) {
1855
+ var _unit$dim_, _retUnitLookup$retMsg;
1856
+
1857
+ var unitToExp = {};
1858
+ var dimVec = (_unit$dim_ = unit.dim_) === null || _unit$dim_ === void 0 ? void 0 : _unit$dim_.dimVec_;
1859
+ var baseUnitString = '1';
1860
+
1861
+ if (dimVec) {
1862
+ var dimVecIndexToBaseUnit = UnitTables.getInstance().dimVecIndexToBaseUnit_;
1863
+
1864
+ for (var i = 0, len = dimVec.length; i < len; ++i) {
1865
+ var exp = dimVec[i];
1866
+
1867
+ if (exp) {
1868
+ unitToExp[dimVecIndexToBaseUnit[i]] = exp;
1869
+ baseUnitString += '.' + dimVecIndexToBaseUnit[i] + exp;
1870
+ }
1871
+ }
1872
+ } // The unit might have a conversion function, which has to be applied; we
1873
+ // cannot just assume unit_.magnitude_ is the magnitude in base units.
1874
+
1875
+
1876
+ var retUnitLookup = this.getSpecifiedUnit(baseUnitString, 'validate'); // There should not be any error in retUnitLookup, unless there is a bug.
1877
+
1878
+ var retUnit = retUnitLookup.unit;
1879
+ if (!retUnit && ((_retUnitLookup$retMsg = retUnitLookup.retMsg) === null || _retUnitLookup$retMsg === void 0 ? void 0 : _retUnitLookup$retMsg.length) == 0) retObj.msg.push('Unable construct base unit string; tried ' + baseUnitString);else {
1880
+ try {
1881
+ retObj.magnitude = retUnit.convertFrom(fromVal, unit);
1882
+ } catch (e) {
1883
+ retObj.msg.push(e.toString());
1884
+ }
1885
+
1886
+ if (retObj.msg.length == 0) {
1887
+ retObj.unitToExp = unitToExp;
1888
+ retObj.fromUnitIsSpecial = unit.isSpecial_;
1889
+ }
1890
+ }
1891
+ }
1892
+
1893
+ return retObj;
1894
+ }
1820
1895
  /**
1821
1896
  * This method accepts a term and looks for units that include it as
1822
1897
  * a synonym - or that include the term in its name.
@@ -2419,8 +2494,8 @@ var Unit = /*#__PURE__*/function () {
2419
2494
  key: "convertFrom",
2420
2495
  value: function convertFrom(num, fromUnit) {
2421
2496
  var newNum = 0.0;
2422
- if (this.isArbitrary_) throw new Error("Attempt to convert arbitrary unit ".concat(this.name_));
2423
- if (fromUnit.isArbitrary_) throw new Error("Attempt to convert to arbitrary unit ".concat(fromUnit.name_)); // reject request if both units have dimensions that are not equal
2497
+ if (this.isArbitrary_) throw new Error("Attempt to convert to arbitrary unit \"".concat(this.csCode_, "\""));
2498
+ if (fromUnit.isArbitrary_) throw new Error("Attempt to convert arbitrary unit \"".concat(fromUnit.csCode_, "\"")); // reject request if both units have dimensions that are not equal
2424
2499
 
2425
2500
  if (fromUnit.dim_ && this.dim_ && !fromUnit.dim_.equals(this.dim_)) {
2426
2501
  // check first to see if a mole<->mass conversion is appropriate
@@ -2442,40 +2517,28 @@ var Unit = /*#__PURE__*/function () {
2442
2517
  }
2443
2518
 
2444
2519
  var fromCnv = fromUnit.cnv_;
2445
- var fromMag = fromUnit.magnitude_; // If the same conversion function is specified for both units, which
2446
- // includes neither unit having a conversion function, multiply the
2447
- // "from" unit's magnitude by the number passed in and then divide
2448
- // that result by this unit's magnitude. Do this for units with
2449
- // and without dimension vectors. PROBLEM with 2 non-commensurable
2450
- // units with no dimension vector or function, e.g., byte to mol
2451
-
2452
- if (fromCnv === this.cnv_) {
2453
- newNum = num * fromMag / this.magnitude_;
2454
- } // else use a function to get the number to be returned
2455
- else {
2456
- var x = 0.0;
2457
-
2458
- if (fromCnv != null) {
2459
- // turn num * fromUnit.magnitude into its ratio scale equivalent,
2460
- // e.g., convert Celsius to Kelvin
2461
- var fromFunc = _ucumFunctions.default.forName(fromCnv);
2520
+ var fromMag = fromUnit.magnitude_;
2521
+ var x;
2462
2522
 
2463
- x = fromFunc.cnvFrom(num * fromUnit.cnvPfx_) * fromMag; //x = fromFunc.cnvFrom(num * fromMag) * fromUnit.cnvPfx_;
2464
- } else {
2465
- x = num * fromMag;
2466
- }
2523
+ if (fromCnv != null) {
2524
+ // turn num * fromUnit.magnitude into its ratio scale equivalent,
2525
+ // e.g., convert Celsius to Kelvin
2526
+ var fromFunc = _ucumFunctions.default.forName(fromCnv);
2467
2527
 
2468
- if (this.cnv_ != null) {
2469
- // turn mag * origUnit on ratio scale into a non-ratio unit,
2470
- // e.g. convert Kelvin to Fahrenheit
2471
- var toFunc = _ucumFunctions.default.forName(this.cnv_);
2528
+ x = fromFunc.cnvFrom(num * fromUnit.cnvPfx_) * fromMag; //x = fromFunc.cnvFrom(num * fromMag) * fromUnit.cnvPfx_;
2529
+ } else {
2530
+ x = num * fromMag;
2531
+ }
2472
2532
 
2473
- newNum = toFunc.cnvTo(x / this.magnitude_) / this.cnvPfx_;
2474
- } else {
2475
- newNum = x / this.magnitude_;
2476
- }
2477
- } // end if either unit has a conversion function
2533
+ if (this.cnv_ != null) {
2534
+ // turn mag * origUnit on ratio scale into a non-ratio unit,
2535
+ // e.g. convert Kelvin to Fahrenheit
2536
+ var toFunc = _ucumFunctions.default.forName(this.cnv_);
2478
2537
 
2538
+ newNum = toFunc.cnvTo(x / this.magnitude_) / this.cnvPfx_;
2539
+ } else {
2540
+ newNum = x / this.magnitude_;
2541
+ }
2479
2542
 
2480
2543
  return newNum;
2481
2544
  } // end convertFrom
@@ -2690,6 +2753,7 @@ var Unit = /*#__PURE__*/function () {
2690
2753
  else if (unit2.cnv_ != null) {
2691
2754
  if (!retUnit.dim_ || retUnit.dim_.isZero()) {
2692
2755
  retUnit.cnvPfx_ = unit2.cnvPfx_ * retUnit.magnitude_;
2756
+ retUnit.magnitude_ = unit2.magnitude_;
2693
2757
  retUnit.cnv_ = unit2.cnv_;
2694
2758
  } else throw new Error("Attempt to multiply non-ratio unit ".concat(unit2.name_));
2695
2759
  } // end if unit2 has a conversion function
@@ -2723,7 +2787,9 @@ var Unit = /*#__PURE__*/function () {
2723
2787
  // if (!retUnit.isMole_)
2724
2788
  // retUnit.isMole_ = unit2.isMole_ ;
2725
2789
 
2726
- if (!retUnit.isArbitrary_) retUnit.isArbitrary_ = unit2.isArbitrary_;
2790
+ if (!retUnit.isArbitrary_) retUnit.isArbitrary_ = unit2.isArbitrary_; // Likewise for special units
2791
+
2792
+ if (!retUnit.isSpecial_) retUnit.isSpecial_ = unit2.isSpecial_;
2727
2793
  return retUnit;
2728
2794
  } // end multiplyThese
2729
2795
 
@@ -3280,6 +3346,7 @@ var UnitString = /*#__PURE__*/function () {
3280
3346
  if (intUtils_.isIntegerUnit(finalUnit) || typeof finalUnit === 'number') {
3281
3347
  finalUnit = new Unit({
3282
3348
  'csCode_': origString,
3349
+ 'ciCode_': origString,
3283
3350
  'magnitude_': finalUnit,
3284
3351
  'name_': origString
3285
3352
  });
@@ -4494,6 +4561,7 @@ var UnitString = /*#__PURE__*/function () {
4494
4561
  if (intUtils_.isIntegerUnit(finalUnit)) {
4495
4562
  finalUnit = new Unit({
4496
4563
  'csCode_': finalUnit,
4564
+ 'ciCode_': finalUnit,
4497
4565
  'magnitude_': Number(finalUnit),
4498
4566
  'name_': finalUnit
4499
4567
  });
@@ -4509,6 +4577,7 @@ var UnitString = /*#__PURE__*/function () {
4509
4577
  if (intUtils_.isIntegerUnit(nextUnit)) {
4510
4578
  nextUnit = new Unit({
4511
4579
  'csCode_': nextUnit,
4580
+ 'ciCode_': nextUnit,
4512
4581
  'magnitude_': Number(nextUnit),
4513
4582
  'name_': nextUnit
4514
4583
  });
@@ -4734,6 +4803,11 @@ var UnitTablesFactory = /*#__PURE__*/function () {
4734
4803
  */
4735
4804
 
4736
4805
  this.massDimIndex_ = 0;
4806
+ /**
4807
+ * Map of indices in the dimension vector to base unit symbols.
4808
+ */
4809
+
4810
+ this.dimVecIndexToBaseUnit_ = {};
4737
4811
  }
4738
4812
  /**
4739
4813
  * Provides the number of unit objects written to the tables, using the
@@ -4774,6 +4848,17 @@ var UnitTablesFactory = /*#__PURE__*/function () {
4774
4848
  } catch (err) {// do nothing - throws error if the property is null
4775
4849
  // and that's OK here.
4776
4850
  }
4851
+
4852
+ if (theUnit.isBase_) {
4853
+ var dimVec = theUnit.dim_.dimVec_;
4854
+ var nonZeroIndex;
4855
+
4856
+ for (var i = 0, len = dimVec.length; nonZeroIndex == undefined && i < len; ++i) {
4857
+ if (dimVec[i] != 0) nonZeroIndex = i;
4858
+ }
4859
+
4860
+ this.dimVecIndexToBaseUnit_[nonZeroIndex] = theUnit.csCode_;
4861
+ }
4777
4862
  } // end addUnit
4778
4863
 
4779
4864
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lhncbc/ucum-lhc",
3
- "version": "4.1.8",
3
+ "version": "4.2.0",
4
4
  "description": "Implements Unified Code for Units of Measure (UCUM) functions in a javascript library",
5
5
  "main": "source-cjs/ucumPkg.js",
6
6
  "homepage": "https://lhncbc.github.io/ucum-lhc/",
package/source/config.js CHANGED
@@ -24,6 +24,7 @@ export var Ucum = {
24
24
  */
25
25
  dimLen_: 7,
26
26
 
27
+
27
28
  /**
28
29
  * The characters used as valid operators in a UCUM unit expression,
29
30
  * where '.' is for multiplication and '/' is for division.
@@ -63,14 +64,14 @@ export var Ucum = {
63
64
  * displaying messages on a web site; should be blank when output is
64
65
  * to a file.
65
66
  */
66
- openEmphHTML_ : '<span class="emphSpan">',
67
+ openEmphHTML_ : ' <span class="emphSpan">',
67
68
 
68
69
  /**
69
70
  * Closing HTML used to emphasize portions of error messages. Used when
70
71
  * displaying messages on a web site; should be blank when output is
71
72
  * to a file.
72
73
  */
73
- closeEmphHTML_ : '</span>' ,
74
+ closeEmphHTML_ : '</span> ' ,
74
75
 
75
76
  /**
76
77
  * Message that is displayed when annotations are included in a unit
@@ -337,6 +337,76 @@ export class UcumLhcUtils {
337
337
  } // end convertUnitTo
338
338
 
339
339
 
340
+ /**
341
+ * Converts the given unit string into its base units, their exponents, and
342
+ * a magnitude, and returns that data.
343
+ * @param fromUnit the unit string to be converted to base units information
344
+ * @param fromVal the number of "from" units to be converted
345
+ * @returns an object with the properties:
346
+ * 'msg': an array of one or more messages, if the string is invalid or
347
+ * an error occurred, indicating the problem, or a suggestion of a
348
+ * substitution such as the substitution of 'G' for 'Gauss', or
349
+ * an empty array if no messages were generated. If this is not empty,
350
+ * no other information will be returned.
351
+ * 'magnitude': the new value when fromVal units of fromUnits is expressed in the base units.
352
+ * 'fromUnitIsSpecial': whether the input unit fromUnit is a "special unit"
353
+ * as defined in UCUM. This means there is some function applied to convert
354
+ * between fromUnit and the base units, so the returned magnitude is likely not
355
+ * useful as a scale factor for other conversions (i.e., it only has validity
356
+ * and usefulness for the input values that produced it).
357
+ * 'unitToExp': a map of base units in uStr to their exponent
358
+ */
359
+ convertToBaseUnits(fromUnit, fromVal) {
360
+ let inputUnitLookup = this.getSpecifiedUnit(fromUnit, 'validate');
361
+ let retObj = {};
362
+ let unit = inputUnitLookup.unit;
363
+ retObj.msg = inputUnitLookup.retMsg || [];
364
+ if (!unit) {
365
+ if (inputUnitLookup.retMsg?.length == 0)
366
+ retObj.msg.push('Could not find unit information for '+fromUnit);
367
+ }
368
+ else if (unit.isArbitrary_) {
369
+ retObj.msg.push('Arbitrary units cannot be converted to base units or other units.');
370
+ }
371
+ else if (retObj.msg.length == 0) {
372
+ let unitToExp = {};
373
+ let dimVec = unit.dim_?.dimVec_
374
+ let baseUnitString = '1';
375
+ if (dimVec) {
376
+ let dimVecIndexToBaseUnit = UnitTables.getInstance().dimVecIndexToBaseUnit_;
377
+ for (let i=0, len=dimVec.length; i<len; ++i) {
378
+ let exp = dimVec[i];
379
+ if (exp) {
380
+ unitToExp[dimVecIndexToBaseUnit[i]] = exp;
381
+ baseUnitString += '.' + dimVecIndexToBaseUnit[i] + exp;
382
+ }
383
+ }
384
+ }
385
+
386
+ // The unit might have a conversion function, which has to be applied; we
387
+ // cannot just assume unit_.magnitude_ is the magnitude in base units.
388
+ let retUnitLookup = this.getSpecifiedUnit(baseUnitString, 'validate');
389
+ // There should not be any error in retUnitLookup, unless there is a bug.
390
+ let retUnit = retUnitLookup.unit;
391
+ if (!retUnit && retUnitLookup.retMsg?.length == 0)
392
+ retObj.msg.push('Unable construct base unit string; tried '+baseUnitString);
393
+ else {
394
+ try {
395
+ retObj.magnitude = retUnit.convertFrom(fromVal, unit);
396
+ }
397
+ catch (e) {
398
+ retObj.msg.push(e.toString());
399
+ }
400
+ if (retObj.msg.length == 0) {
401
+ retObj.unitToExp = unitToExp;
402
+ retObj.fromUnitIsSpecial = unit.isSpecial_;
403
+ }
404
+ }
405
+ }
406
+ return retObj;
407
+ }
408
+
409
+
340
410
  /**
341
411
  * This method accepts a term and looks for units that include it as
342
412
  * a synonym - or that include the term in its name.
package/source/unit.js CHANGED
@@ -340,6 +340,7 @@ export class Unit {
340
340
  return match ;
341
341
  }// end of fullEquals
342
342
 
343
+
343
344
  /**
344
345
  * This returns the value of the property named by the parameter
345
346
  * passed in.
@@ -380,9 +381,9 @@ export class Unit {
380
381
  let newNum = 0.0 ;
381
382
 
382
383
  if (this.isArbitrary_)
383
- throw (new Error(`Attempt to convert arbitrary unit ${this.name_}`));
384
+ throw (new Error(`Attempt to convert to arbitrary unit "${this.csCode_}"`));
384
385
  if (fromUnit.isArbitrary_)
385
- throw (new Error(`Attempt to convert to arbitrary unit ${fromUnit.name_}`));
386
+ throw (new Error(`Attempt to convert arbitrary unit "${fromUnit.csCode_}"`));
386
387
 
387
388
  // reject request if both units have dimensions that are not equal
388
389
  if (fromUnit.dim_ && this.dim_ && !(fromUnit.dim_.equals(this.dim_))) {
@@ -410,39 +411,27 @@ export class Unit {
410
411
  let fromCnv = fromUnit.cnv_ ;
411
412
  let fromMag = fromUnit.magnitude_ ;
412
413
 
413
- // If the same conversion function is specified for both units, which
414
- // includes neither unit having a conversion function, multiply the
415
- // "from" unit's magnitude by the number passed in and then divide
416
- // that result by this unit's magnitude. Do this for units with
417
- // and without dimension vectors. PROBLEM with 2 non-commensurable
418
- // units with no dimension vector or function, e.g., byte to mol
419
- if (fromCnv === this.cnv_) {
420
- newNum = (num * fromMag) / this.magnitude_;
414
+ let x;
415
+ if (fromCnv != null) {
416
+ // turn num * fromUnit.magnitude into its ratio scale equivalent,
417
+ // e.g., convert Celsius to Kelvin
418
+ let fromFunc = funcs.forName(fromCnv);
419
+ x = fromFunc.cnvFrom(num * fromUnit.cnvPfx_) * fromMag;
420
+ //x = fromFunc.cnvFrom(num * fromMag) * fromUnit.cnvPfx_;
421
421
  }
422
- // else use a function to get the number to be returned
423
422
  else {
424
- let x = 0.0 ;
425
- if (fromCnv != null) {
426
- // turn num * fromUnit.magnitude into its ratio scale equivalent,
427
- // e.g., convert Celsius to Kelvin
428
- let fromFunc = funcs.forName(fromCnv);
429
- x = fromFunc.cnvFrom(num * fromUnit.cnvPfx_) * fromMag;
430
- //x = fromFunc.cnvFrom(num * fromMag) * fromUnit.cnvPfx_;
431
- }
432
- else {
433
- x = num * fromMag;
434
- }
423
+ x = num * fromMag;
424
+ }
435
425
 
436
- if (this.cnv_ != null) {
437
- // turn mag * origUnit on ratio scale into a non-ratio unit,
438
- // e.g. convert Kelvin to Fahrenheit
439
- let toFunc = funcs.forName(this.cnv_);
440
- newNum = toFunc.cnvTo(x / this.magnitude_) / this.cnvPfx_;
441
- }
442
- else {
443
- newNum = x / this.magnitude_;
444
- }
445
- } // end if either unit has a conversion function
426
+ if (this.cnv_ != null) {
427
+ // turn mag * origUnit on ratio scale into a non-ratio unit,
428
+ // e.g. convert Kelvin to Fahrenheit
429
+ let toFunc = funcs.forName(this.cnv_);
430
+ newNum = toFunc.cnvTo(x / this.magnitude_) / this.cnvPfx_;
431
+ }
432
+ else {
433
+ newNum = x / this.magnitude_;
434
+ }
446
435
 
447
436
  return newNum;
448
437
 
@@ -666,6 +655,7 @@ export class Unit {
666
655
  else if (unit2.cnv_ != null) {
667
656
  if (!retUnit.dim_ || retUnit.dim_.isZero()) {
668
657
  retUnit.cnvPfx_ = unit2.cnvPfx_ * retUnit.magnitude_;
658
+ retUnit.magnitude_ = unit2.magnitude_;
669
659
  retUnit.cnv_ = unit2.cnv_ ;
670
660
  }
671
661
  else
@@ -716,8 +706,12 @@ export class Unit {
716
706
  // via an arithmetic operation. Taint accordingly
717
707
  // if (!retUnit.isMole_)
718
708
  // retUnit.isMole_ = unit2.isMole_ ;
719
- if (!retUnit.isArbitrary_)
720
- retUnit.isArbitrary_ = unit2.isArbitrary_;
709
+ if (!retUnit.isArbitrary_)
710
+ retUnit.isArbitrary_ = unit2.isArbitrary_;
711
+
712
+ // Likewise for special units
713
+ if (!retUnit.isSpecial_)
714
+ retUnit.isSpecial_ = unit2.isSpecial_;
721
715
 
722
716
  return retUnit ;
723
717
 
@@ -218,6 +218,7 @@ export class UnitString {
218
218
  if (intUtils_.isIntegerUnit(finalUnit) || typeof finalUnit === 'number') {
219
219
  finalUnit = new Unit({
220
220
  'csCode_': origString,
221
+ 'ciCode_': origString,
221
222
  'magnitude_': finalUnit,
222
223
  'name_': origString
223
224
  });
@@ -1442,7 +1443,7 @@ export class UnitString {
1442
1443
 
1443
1444
  let finalUnit = uArray[0]['un'];
1444
1445
  if (intUtils_.isIntegerUnit(finalUnit)) {
1445
- finalUnit = new Unit({'csCode_' : finalUnit,
1446
+ finalUnit = new Unit({'csCode_' : finalUnit, 'ciCode_' : finalUnit,
1446
1447
  'magnitude_' : Number(finalUnit),
1447
1448
  'name_' : finalUnit}) ;
1448
1449
  }
@@ -1453,7 +1454,7 @@ export class UnitString {
1453
1454
  for (let u2 = 1; (u2 < uLen) && !endProcessing; u2++) {
1454
1455
  let nextUnit = uArray[u2]['un'];
1455
1456
  if (intUtils_.isIntegerUnit(nextUnit)) {
1456
- nextUnit = new Unit({'csCode_' : nextUnit ,
1457
+ nextUnit = new Unit({'csCode_' : nextUnit, 'ciCode_' : nextUnit,
1457
1458
  'magnitude_' : Number(nextUnit),
1458
1459
  'name_': nextUnit});
1459
1460
  }
@@ -97,6 +97,11 @@ class UnitTablesFactory {
97
97
  * @type integer
98
98
  */
99
99
  this.massDimIndex_ = 0;
100
+
101
+ /**
102
+ * Map of indices in the dimension vector to base unit symbols.
103
+ */
104
+ this.dimVecIndexToBaseUnit_ = {};
100
105
  }
101
106
 
102
107
 
@@ -138,6 +143,15 @@ class UnitTablesFactory {
138
143
  // and that's OK here.
139
144
  }
140
145
 
146
+ if (theUnit.isBase_) {
147
+ const dimVec = theUnit.dim_.dimVec_;
148
+ let nonZeroIndex;
149
+ for (let i=0, len=dimVec.length; nonZeroIndex==undefined && i<len; ++i) {
150
+ if (dimVec[i] != 0)
151
+ nonZeroIndex = i;
152
+ }
153
+ this.dimVecIndexToBaseUnit_[nonZeroIndex] = theUnit.csCode_;
154
+ }
141
155
  } // end addUnit
142
156
 
143
157
 
@@ -65,14 +65,14 @@ var Ucum = {
65
65
  * displaying messages on a web site; should be blank when output is
66
66
  * to a file.
67
67
  */
68
- openEmphHTML_: '<span class="emphSpan">',
68
+ openEmphHTML_: ' <span class="emphSpan">',
69
69
 
70
70
  /**
71
71
  * Closing HTML used to emphasize portions of error messages. Used when
72
72
  * displaying messages on a web site; should be blank when output is
73
73
  * to a file.
74
74
  */
75
- closeEmphHTML_: '</span>',
75
+ closeEmphHTML_: '</span> ',
76
76
 
77
77
  /**
78
78
  * Message that is displayed when annotations are included in a unit
@@ -1 +1 @@
1
- {"version":3,"sources":["../source/config.js"],"names":["Ucum","dimLen_","validOps_","codeSep_","valMsgStart_","valMsgEnd_","cnvMsgStart_","cnvMsgEnd_","openEmph_","closeEmph_","openEmphHTML_","closeEmphHTML_","bracesMsg_","needMoleWeightMsg_","csvCols_","inputKey_","specUnits_"],"mappings":";;;;;;;AAAA;;;;;;;AAQO,IAAIA,IAAI,GAAG;AAEhB;;;;;;AAMA;;AAEA;;;;;;AAMAC,EAAAA,OAAO,EAAE,CAhBO;;AAkBhB;;;;AAIAC,EAAAA,SAAS,EAAE,CAAC,GAAD,EAAM,GAAN,CAtBK;;AAyBhB;;;;AAIAC,EAAAA,QAAQ,EAAG,IA7BK;AA+BhB;AACAC,EAAAA,YAAY,EAAG,eAhCC;AAiChBC,EAAAA,UAAU,EAAG,GAjCG;AAkChBC,EAAAA,YAAY,EAAG,uBAlCC;AAmChBC,EAAAA,UAAU,EAAG,GAnCG;;AAsClB;;;;;AAKEC,EAAAA,SAAS,EAAG,KA3CI;;AA6ChB;;;;;AAKAC,EAAAA,UAAU,EAAG,KAlDG;;AAoDhB;;;;;AAKAC,EAAAA,aAAa,EAAG,yBAzDA;;AA2DhB;;;;;AAKAC,EAAAA,cAAc,EAAG,SAhED;;AAkEhB;;;;AAIAC,EAAAA,UAAU,EAAG,8DACA,6DADA,GAEA,iCAxEG;;AA0EhB;;;;;AAKAC,EAAAA,kBAAkB,EAAG,0DACA,uDADA,GAEA,8CAjFL;;AAmFhB;;;;AAIAC,EAAAA,QAAQ,EAAG;AACT,2BAAwB,SADf;AAET,sBAAmB,gBAFV;AAGT,sBAAmB,OAHV;AAIT,gBAAa,WAJJ;AAKT,cAAW,SALF;AAMT,gBAAa,WANJ;AAOT,gBAAa;AAPJ,GAvFK;;AAiGhB;;;AAGAC,EAAAA,SAAS,EAAG,qBApGI;;AAsGhB;;;;;;;AAOCC,EAAAA,UAAU,EAAG;AAAE,gBAAa,gBAAf;AACE,uBAAoB;AADtB;AA7GE,CAAX","sourcesContent":["/*\n * This defines the namespace for the UCUM classes and provides\n * a place for the definition of global variables and constants.\n *\n * The javascript for this UCUM implementation uses syntax as\n * defined by the ECMAScript 6 standard\n */\n\nexport var Ucum = {\n\n /**\n * Flag indicating whether or not we're using case sensitive labels\n * I don't think we need this. I think we're just going with\n * case sensitive, per Clem. Gunther's code has this flag, but I\n * am removing it, at least for now. lm, 6/2016\n */\n //caseSensitive_: true ,\n\n /**\n * The number of elements in a Dimension array. Currently this\n * is set as a configuration variable, but when we get to the point\n * of loading the unit definitions from a file, this value will be\n * set from that.\n */\n dimLen_: 7,\n\n /**\n * The characters used as valid operators in a UCUM unit expression,\n * where '.' is for multiplication and '/' is for division.\n */\n validOps_: ['.', '/'],\n\n\n /**\n * The string used to separate a unit code and unit name when they\n * are displayed together\n */\n codeSep_ : ': ',\n\n // Message text variations for validation methods and conversion methods\n valMsgStart_ : 'Did you mean ',\n valMsgEnd_ : '?' ,\n cnvMsgStart_ : 'We assumed you meant ',\n cnvMsgEnd_ : '.',\n\n\n/**\n * Default opening string used to emphasize portions of error messages.\n * Used when NOT displaying messages on a web site, i.e., for output\n * from the library methods or to a file.\n */\n openEmph_ : ' ->',\n\n /**\n * Default closing string used to emphasize portions of error messages.\n * Used when NOT displaying messages on a web site, i.e., for output\n * from the library methods or to a file.\n */\n closeEmph_ : '<- ' ,\n\n /**\n * Opening HTML used to emphasize portions of error messages. Used when\n * displaying messages on a web site; should be blank when output is\n * to a file.\n */\n openEmphHTML_ : '<span class=\"emphSpan\">',\n\n /**\n * Closing HTML used to emphasize portions of error messages. Used when\n * displaying messages on a web site; should be blank when output is\n * to a file.\n */\n closeEmphHTML_ : '</span>' ,\n\n /**\n * Message that is displayed when annotations are included in a unit\n * string, to let the user know how they are interpreted.\n */\n bracesMsg_ : 'FYI - annotations (text in curly braces {}) are ignored, ' +\n 'except that an annotation without a leading symbol implies ' +\n 'the default unit 1 (the unity).',\n\n /**\n * Message that is displayed or returned when a conversion is requested\n * for two units where (only) a mass<->moles conversion is appropriate\n * but no molecular weight was specified.\n */\n needMoleWeightMsg_ : 'Did you wish to convert between mass and moles? The ' +\n 'molecular weight of the substance represented by the ' +\n 'units is required to perform the conversion.',\n\n /**\n * Hash that matches unit column names to names used in the csv file\n * that is submitted to the data updater.\n */\n csvCols_ : {\n 'case-sensitive code' : 'csCode_',\n 'LOINC property' : 'loincProperty_',\n 'name (display)' : 'name_',\n 'synonyms' : 'synonyms_',\n 'source' : 'source_',\n 'category' : 'category_',\n 'Guidance' : 'guidance_'\n } ,\n\n /**\n * Name of the column in the csv file that serves as the key\n */\n inputKey_ : 'case-sensitive code' ,\n\n /**\n * Special codes that contain operators within brackets. The operator\n * within these codes causes them to parse incorrectly if they are preceded\n * by a prefix, because the parsing algorithm splits them up on the operator.\n * So we use this object to identify them and substitute placeholders to\n * avoid that.\n */\n specUnits_ : { 'B[10.nV]' : 'specialUnitOne',\n '[m/s2/Hz^(1/2)]' : 'specialUnitTwo'}\n} ;\n\n\n"],"file":"config.js"}
1
+ {"version":3,"sources":["../source/config.js"],"names":["Ucum","dimLen_","validOps_","codeSep_","valMsgStart_","valMsgEnd_","cnvMsgStart_","cnvMsgEnd_","openEmph_","closeEmph_","openEmphHTML_","closeEmphHTML_","bracesMsg_","needMoleWeightMsg_","csvCols_","inputKey_","specUnits_"],"mappings":";;;;;;;AAAA;;;;;;;AAQO,IAAIA,IAAI,GAAG;AAEhB;;;;;;AAMA;;AAEA;;;;;;AAMAC,EAAAA,OAAO,EAAE,CAhBO;;AAmBhB;;;;AAIAC,EAAAA,SAAS,EAAE,CAAC,GAAD,EAAM,GAAN,CAvBK;;AA0BhB;;;;AAIAC,EAAAA,QAAQ,EAAG,IA9BK;AAgChB;AACAC,EAAAA,YAAY,EAAG,eAjCC;AAkChBC,EAAAA,UAAU,EAAG,GAlCG;AAmChBC,EAAAA,YAAY,EAAG,uBAnCC;AAoChBC,EAAAA,UAAU,EAAG,GApCG;;AAuClB;;;;;AAKEC,EAAAA,SAAS,EAAG,KA5CI;;AA8ChB;;;;;AAKAC,EAAAA,UAAU,EAAG,KAnDG;;AAqDhB;;;;;AAKAC,EAAAA,aAAa,EAAG,0BA1DA;;AA4DhB;;;;;AAKAC,EAAAA,cAAc,EAAG,UAjED;;AAmEhB;;;;AAIAC,EAAAA,UAAU,EAAG,8DACA,6DADA,GAEA,iCAzEG;;AA2EhB;;;;;AAKAC,EAAAA,kBAAkB,EAAG,0DACA,uDADA,GAEA,8CAlFL;;AAoFhB;;;;AAIAC,EAAAA,QAAQ,EAAG;AACT,2BAAwB,SADf;AAET,sBAAmB,gBAFV;AAGT,sBAAmB,OAHV;AAIT,gBAAa,WAJJ;AAKT,cAAW,SALF;AAMT,gBAAa,WANJ;AAOT,gBAAa;AAPJ,GAxFK;;AAkGhB;;;AAGAC,EAAAA,SAAS,EAAG,qBArGI;;AAuGhB;;;;;;;AAOCC,EAAAA,UAAU,EAAG;AAAE,gBAAa,gBAAf;AACE,uBAAoB;AADtB;AA9GE,CAAX","sourcesContent":["/*\n * This defines the namespace for the UCUM classes and provides\n * a place for the definition of global variables and constants.\n *\n * The javascript for this UCUM implementation uses syntax as\n * defined by the ECMAScript 6 standard\n */\n\nexport var Ucum = {\n\n /**\n * Flag indicating whether or not we're using case sensitive labels\n * I don't think we need this. I think we're just going with\n * case sensitive, per Clem. Gunther's code has this flag, but I\n * am removing it, at least for now. lm, 6/2016\n */\n //caseSensitive_: true ,\n\n /**\n * The number of elements in a Dimension array. Currently this\n * is set as a configuration variable, but when we get to the point\n * of loading the unit definitions from a file, this value will be\n * set from that.\n */\n dimLen_: 7,\n\n\n /**\n * The characters used as valid operators in a UCUM unit expression,\n * where '.' is for multiplication and '/' is for division.\n */\n validOps_: ['.', '/'],\n\n\n /**\n * The string used to separate a unit code and unit name when they\n * are displayed together\n */\n codeSep_ : ': ',\n\n // Message text variations for validation methods and conversion methods\n valMsgStart_ : 'Did you mean ',\n valMsgEnd_ : '?' ,\n cnvMsgStart_ : 'We assumed you meant ',\n cnvMsgEnd_ : '.',\n\n\n/**\n * Default opening string used to emphasize portions of error messages.\n * Used when NOT displaying messages on a web site, i.e., for output\n * from the library methods or to a file.\n */\n openEmph_ : ' ->',\n\n /**\n * Default closing string used to emphasize portions of error messages.\n * Used when NOT displaying messages on a web site, i.e., for output\n * from the library methods or to a file.\n */\n closeEmph_ : '<- ' ,\n\n /**\n * Opening HTML used to emphasize portions of error messages. Used when\n * displaying messages on a web site; should be blank when output is\n * to a file.\n */\n openEmphHTML_ : ' <span class=\"emphSpan\">',\n\n /**\n * Closing HTML used to emphasize portions of error messages. Used when\n * displaying messages on a web site; should be blank when output is\n * to a file.\n */\n closeEmphHTML_ : '</span> ' ,\n\n /**\n * Message that is displayed when annotations are included in a unit\n * string, to let the user know how they are interpreted.\n */\n bracesMsg_ : 'FYI - annotations (text in curly braces {}) are ignored, ' +\n 'except that an annotation without a leading symbol implies ' +\n 'the default unit 1 (the unity).',\n\n /**\n * Message that is displayed or returned when a conversion is requested\n * for two units where (only) a mass<->moles conversion is appropriate\n * but no molecular weight was specified.\n */\n needMoleWeightMsg_ : 'Did you wish to convert between mass and moles? The ' +\n 'molecular weight of the substance represented by the ' +\n 'units is required to perform the conversion.',\n\n /**\n * Hash that matches unit column names to names used in the csv file\n * that is submitted to the data updater.\n */\n csvCols_ : {\n 'case-sensitive code' : 'csCode_',\n 'LOINC property' : 'loincProperty_',\n 'name (display)' : 'name_',\n 'synonyms' : 'synonyms_',\n 'source' : 'source_',\n 'category' : 'category_',\n 'Guidance' : 'guidance_'\n } ,\n\n /**\n * Name of the column in the csv file that serves as the key\n */\n inputKey_ : 'case-sensitive code' ,\n\n /**\n * Special codes that contain operators within brackets. The operator\n * within these codes causes them to parse incorrectly if they are preceded\n * by a prefix, because the parsing algorithm splits them up on the operator.\n * So we use this object to identify them and substitute placeholders to\n * avoid that.\n */\n specUnits_ : { 'B[10.nV]' : 'specialUnitOne',\n '[m/s2/Hz^(1/2)]' : 'specialUnitTwo'}\n} ;\n\n\n"],"file":"config.js"}
@@ -333,6 +333,76 @@ class UcumLhcUtils {
333
333
  return returnObj;
334
334
  } // end convertUnitTo
335
335
 
336
+ /**
337
+ * Converts the given unit string into its base units, their exponents, and
338
+ * a magnitude, and returns that data.
339
+ * @param fromUnit the unit string to be converted to base units information
340
+ * @param fromVal the number of "from" units to be converted
341
+ * @returns an object with the properties:
342
+ * 'msg': an array of one or more messages, if the string is invalid or
343
+ * an error occurred, indicating the problem, or a suggestion of a
344
+ * substitution such as the substitution of 'G' for 'Gauss', or
345
+ * an empty array if no messages were generated. If this is not empty,
346
+ * no other information will be returned.
347
+ * 'magnitude': the new value when fromVal units of fromUnits is expressed in the base units.
348
+ * 'fromUnitIsSpecial': whether the input unit fromUnit is a "special unit"
349
+ * as defined in UCUM. This means there is some function applied to convert
350
+ * between fromUnit and the base units, so the returned magnitude is likely not
351
+ * useful as a scale factor for other conversions (i.e., it only has validity
352
+ * and usefulness for the input values that produced it).
353
+ * 'unitToExp': a map of base units in uStr to their exponent
354
+ */
355
+
356
+
357
+ convertToBaseUnits(fromUnit, fromVal) {
358
+ let inputUnitLookup = this.getSpecifiedUnit(fromUnit, 'validate');
359
+ let retObj = {};
360
+ let unit = inputUnitLookup.unit;
361
+ retObj.msg = inputUnitLookup.retMsg || [];
362
+
363
+ if (!unit) {
364
+ if (inputUnitLookup.retMsg?.length == 0) retObj.msg.push('Could not find unit information for ' + fromUnit);
365
+ } else if (unit.isArbitrary_) {
366
+ retObj.msg.push('Arbitrary units cannot be converted to base units or other units.');
367
+ } else if (retObj.msg.length == 0) {
368
+ let unitToExp = {};
369
+ let dimVec = unit.dim_?.dimVec_;
370
+ let baseUnitString = '1';
371
+
372
+ if (dimVec) {
373
+ let dimVecIndexToBaseUnit = UnitTables.getInstance().dimVecIndexToBaseUnit_;
374
+
375
+ for (let i = 0, len = dimVec.length; i < len; ++i) {
376
+ let exp = dimVec[i];
377
+
378
+ if (exp) {
379
+ unitToExp[dimVecIndexToBaseUnit[i]] = exp;
380
+ baseUnitString += '.' + dimVecIndexToBaseUnit[i] + exp;
381
+ }
382
+ }
383
+ } // The unit might have a conversion function, which has to be applied; we
384
+ // cannot just assume unit_.magnitude_ is the magnitude in base units.
385
+
386
+
387
+ let retUnitLookup = this.getSpecifiedUnit(baseUnitString, 'validate'); // There should not be any error in retUnitLookup, unless there is a bug.
388
+
389
+ let retUnit = retUnitLookup.unit;
390
+ if (!retUnit && retUnitLookup.retMsg?.length == 0) retObj.msg.push('Unable construct base unit string; tried ' + baseUnitString);else {
391
+ try {
392
+ retObj.magnitude = retUnit.convertFrom(fromVal, unit);
393
+ } catch (e) {
394
+ retObj.msg.push(e.toString());
395
+ }
396
+
397
+ if (retObj.msg.length == 0) {
398
+ retObj.unitToExp = unitToExp;
399
+ retObj.fromUnitIsSpecial = unit.isSpecial_;
400
+ }
401
+ }
402
+ }
403
+
404
+ return retObj;
405
+ }
336
406
  /**
337
407
  * This method accepts a term and looks for units that include it as
338
408
  * a synonym - or that include the term in its name.