Closed puigru closed 2 years ago
Converting not really, but isEquivalentTo()
(since 2.1) returns true
if you compare 1000 m
and 1km
.
I don't really see a need for a special convert()
method, but using isEquivalentTo()
you could easily write some kind of loop or Lambda to find such a fitting target quantity. I hope this helps, otherwise feel free to reopen or create another ticket.
Well, I meant it for formatting purposes, isEquivalentTo()
is not very useful in that case.
One may want to display 4500 g
as 4.5 kg
, for example. Is there a way to accomplish this with this library?
Ok so if it's about formatting (potentially with confersion) then please refer to indriya#189. We start exploring these ideas with implementations of UnitFormat
or QuantityFormat
. If something like isEquivalentTo() later turns out to be very generic we might propose changes to the API but for now if you're interested in this kind of feature, happy about help or PRs for indriya#189. I won't reopen this but please share your ideas in the RI ticket or create another one if you think a MultiFormat
is too far from what you'd like to archive.
I wrote a small piece of code to better illustrate what I'm trying to do:
public static Quantity<?> unsafeConvert(Quantity<?> quantity, Unit<?> unit) {
Number value = quantity.getValue();
UnitConverter converter;
try {
converter = quantity.getUnit().getConverterToAny(unit);
} catch (IncommensurableException e) {
throw new RuntimeException(e);
}
return Quantities.getQuantity(converter.convert(value), unit);
}
public static Quantity<?> simplifyUnits(Quantity<?> quantity) {
Prefix[] large = new Prefix[] { KILO, MEGA, GIGA, TERA, PETA, EXA, ZETTA, YOTTA };
Prefix[] small = new Prefix[] { MILLI, MICRO, NANO, PICO, FEMTO, ATTO, ZEPTO, YOCTO };
Unit<?> baseUnit = quantity.getUnit().getSystemUnit();
if (baseUnit == Units.KILOGRAM) baseUnit = Units.GRAM; // special case for kg
quantity = unsafeConvert(quantity, baseUnit);
double value = quantity.getValue().doubleValue();
int log = (int)Math.floor(Math.log(value) / Math.log(1000));
if (log == 0) return quantity;
Prefix prefix;
if (log < 0) {
log = Math.min(-log, small.length);
prefix = small[log-1];
} else {
log = Math.min(log, large.length);
prefix = large[log-1];
}
Unit<?> targetUnit = baseUnit.prefix(prefix);
return unsafeConvert(quantity, targetUnit);
}
public static void main(String[] args) {
double[] test = new double[] { 42*Math.pow(10, -6), 0.1, 1, 4500, Math.pow(10, 6) };
for (double t : test) {
Quantity<?> result = simplifyUnits(Quantities.getQuantity(t, Units.GRAM));
System.out.println(result);
}
}
Outputs:
42 µg
100 mg
1 g
4.5 kg
1 Mg
Perhaps a solution would be to integrate something like this inside a custom QuantityFormatter as you suggest. I was just wondering if there was a built-in way before rolling my own, as this seems like something that would come up often.
Edit: Ironed out some kinks in the code.
I think I only half-guess how these values are related (not quite MultiFormat
because there it would have to be "1000mg, 1g" ;-) but maybe the function
package of Indriya could offer a similar function in places like QuantityStreams
or another class.
Or uom-lib-common
in https://github.com/unitsofmeasurement/uom-lib. @andi-huber What do you think about it?
It's just a few random numbers to show how you could simplify the value to use more precise units instead of showing a very large (or very small) value. Similarly to how your computer doesn't tell you a file weighs 44500 bytes but may instead say 44.5 kb, you may want the Quantity object to print the latter.
I will transfer this to Indriya to explore possible functions like that. It did not work for some strange reason, so referenced instead.
Is there a way to convert a Quantity to the most fitting Prefix? e.g. given MetricPrefix:
1000 m
to1 km
,0.1 m
to1 cm