Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Convert Quantity to most fitting Prefix #236

Closed
puigru opened this issue Feb 26, 2022 · 7 comments
Closed

Convert Quantity to most fitting Prefix #236

puigru opened this issue Feb 26, 2022 · 7 comments

Comments

@puigru
Copy link

puigru commented Feb 26, 2022

Is there a way to convert a Quantity to the most fitting Prefix?
e.g. given MetricPrefix: 1000 m to 1 km, 0.1 m to 1 cm

@keilw
Copy link
Member

keilw commented Feb 26, 2022

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.

@keilw keilw closed this as completed Feb 26, 2022
@puigru
Copy link
Author

puigru commented Feb 26, 2022

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?

@keilw
Copy link
Member

keilw commented Feb 26, 2022

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.

@puigru
Copy link
Author

puigru commented Feb 26, 2022

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.

@keilw
Copy link
Member

keilw commented Feb 26, 2022

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?

@puigru
Copy link
Author

puigru commented Feb 26, 2022

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.

@keilw
Copy link
Member

keilw commented Feb 26, 2022

I will transfer this to Indriya to explore possible functions like that.
It did not work for some strange reason, so referenced instead.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants