/* * Copyright 2010-2013 Institut Pasteur. * * This file is part of Icy. * * Icy is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Icy is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Icy. If not, see <http://www.gnu.org/licenses/>. */ package icy.math; import icy.util.StringUtil; import java.util.concurrent.TimeUnit; /** * Unit conversion utilities class. * * @author Thomas Provoost & Stephane Dallongeville */ public class UnitUtil { public static enum UnitPrefix { GIGA, MEGA, KILO, NONE, MILLI, MICRO, NANO, PICO; @Override public String toString() { switch (this) { case GIGA: return "G"; case KILO: return "k"; case MEGA: return "M"; case MILLI: return "m"; case MICRO: return "µ"; case NANO: return "n"; case PICO: return "p"; case NONE: return ""; default: return "x"; } } }; /** * Return the specified value as "bytes" string :<br> * 1024 --> "1 KB"<br> * 1048576 --> "1 MB"<br> * ...<br> */ public static String getBytesString(double value) { final double absValue = Math.abs(value); // GB if (absValue > 10737418240f) return Double.toString(MathUtil.round(value / 1073741824d, 1)) + " GB"; // MB else if (absValue > 10485760f) return Double.toString(MathUtil.round(value / 1048576d, 1)) + " MB"; // KB else if (absValue > 10240f) return Double.toString(MathUtil.round(value / 1024d, 1)) + " KB"; // B return Double.toString(MathUtil.round(value, 1)) + " B"; } /** * Get the best unit with the given value and {@link UnitPrefix}.<br> * By best unit we adapt the output unit so the value stay between 0.1 --> 100 range (for * dimension 1).<br> * Be careful, this method is supposed to be used with unit in <b>decimal</b> * system. For sexagesimal system, please use {@link #getBestTimeUnit(double)} or * {@link TimeUnit} methods.<br/> * <br/> * Example: <code>getBestUnit(0.01, UnitPrefix.MILLI, 1)</code> will return * <code>UnitPrefix.MICRO</code><br/> * * @param value * : value used to get the best unit. * @param currentUnit * : current unit of the value. * @param dimension * : current unit dimension. * @return Return the best unit * @see #getValueInUnit(double, UnitPrefix, UnitPrefix) */ public static UnitPrefix getBestUnit(double value, UnitPrefix currentUnit, int dimension) { // special case if (value == 0d) return currentUnit; int typeInd = currentUnit.ordinal(); double v = value; final int maxInd = UnitPrefix.values().length - 1; final double factor = Math.pow(1000d, dimension); final double midFactor = Math.pow(100d, dimension); while (((int) v == 0) && (typeInd < maxInd)) { v *= factor; typeInd++; } while (((int) (v / midFactor) != 0) && (typeInd > 0)) { v /= factor; typeInd--; } return UnitPrefix.values()[typeInd]; } /** * Get the best unit with the given value and {@link UnitPrefix}. By best unit we adapt the * output unit so the value stay between 0.1 --> 100 range (for dimension 1).<br> * Be careful, this method is supposed to be used with unit in <b>decimal</b> * system. For sexagesimal system, please use {@link #getBestTimeUnit(double)} or * {@link TimeUnit} methods.<br/> * Example: <code>getBestUnit(0.01, UnitPrefix.MILLI, 1)</code> will return * <code>UnitPrefix.MICRO</code><br/> * * @param value * : value used to get the best unit. * @param currentUnit * : current unit of the value. * @return Return the best unit * @see #getValueInUnit(double, UnitPrefix, UnitPrefix) */ public static UnitPrefix getBestUnit(double value, UnitPrefix currentUnit) { return getBestUnit(value, currentUnit, 1); } /** * Return the value from a specific unit to another unit.<br/> * Be careful, this method is supposed to be used with unit in <b>decimal</b> * system. For sexagesimal system, please use {@link #getBestTimeUnit(double)} or * {@link TimeUnit} methods.<br/> * <b>Example:</b><br/> * <ul> * <li>value = 0.01</li> * <li>currentUnit = {@link UnitPrefix#MILLI}</li> * <li>wantedUnit = {@link UnitPrefix#MICRO}</li> * <li>returns: 10</li> * </ul> * * @param value * : Original value. * @param currentUnit * : current unit * @param wantedUnit * : wanted unit * @param dimension * : unit dimension. * @return Return a double value in the <code>wantedUnit</code> unit. * @see #getBestUnit(double, UnitPrefix) */ public static double getValueInUnit(double value, UnitPrefix currentUnit, UnitPrefix wantedUnit, int dimension) { int currentOrdinal = currentUnit.ordinal(); int wantedOrdinal = wantedUnit.ordinal(); double result = value; final double factor = Math.pow(1000d, dimension); while (currentOrdinal < wantedOrdinal) { result *= factor; currentOrdinal++; } while (currentOrdinal > wantedOrdinal) { result /= factor; currentOrdinal--; } return result; } /** * Return the value from a specific unit to another unit.<br/> * Be careful, this method is supposed to be used with unit in <b>decimal</b> * system. For sexagesimal system, please use {@link #getBestTimeUnit(double)} or * {@link TimeUnit} methods.<br/> * <b>Example:</b><br/> * <ul> * <li>value = 0.01</li> * <li>currentUnit = {@link UnitPrefix#MILLI}</li> * <li>wantedUnit = {@link UnitPrefix#MICRO}</li> * <li>returns: 10</li> * </ul> * * @param value * : Original value. * @param currentUnit * : current unit * @param wantedUnit * : wanted unit * @return Return a double value in the <code>wantedUnit</code> unit. * @see #getBestUnit(double, UnitPrefix) */ public static double getValueInUnit(double value, UnitPrefix currentUnit, UnitPrefix wantedUnit) { return getValueInUnit(value, currentUnit, wantedUnit, 1); } /** * This method returns a string containing the value rounded to a specified * number of decimals and its best unit prefix. This method is supposed to * be used with meters only. * * @param value * : value to display * @param decimals * : number of decimals to keep * @param currentUnit * : current unit prefix (Ex: {@link UnitPrefix#MILLI}) */ public static String getBestUnitInMeters(double value, int decimals, UnitPrefix currentUnit) { UnitPrefix unitPxSize = getBestUnit(value, currentUnit); double distanceMeters = getValueInUnit(value, currentUnit, unitPxSize); return StringUtil.toString(distanceMeters, decimals) + unitPxSize + "m"; } /** * Return the best unit to display the value. The best unit is chosen * according to the precision. <br/> * <b>Example:</b> * <ul> * <li>62001 ms -> {@link TimeUnit#MILLISECONDS}</li> * <li>62000 ms -> {@link TimeUnit#SECONDS}</li> * <li>60000 ms -> {@link TimeUnit#MINUTES}</li> * </ul> * * @param valueInMs * : value in milliseconds. * @return Return a {@link TimeUnit} enumeration value. */ public static TimeUnit getBestTimeUnit(double valueInMs) { if (valueInMs % 1000 != 0) return TimeUnit.MILLISECONDS; if (valueInMs % 60000 != 0) return TimeUnit.SECONDS; if (valueInMs % 3600000 != 0) return TimeUnit.MINUTES; return TimeUnit.HOURS; } /** * @deprecated Use {@link #getBestTimeUnit(double)} instead. */ @Deprecated public static TimeUnit getBestUnit(double valueInMs) { return getBestTimeUnit(valueInMs); } /** * Display the time with a comma and a given precision. * * @param valueInMs * : value in milliseconds * @param precision * : number of decimals after comma * @return <b>Example:</b> "2.5 h", "1.543 min", "15 ms". */ public static String displayTimeAsStringWithComma(double valueInMs, int precision) { String toReturn = ""; if (valueInMs >= 360000d) { valueInMs /= 360000d; toReturn = StringUtil.toString(valueInMs, precision) + " h"; } else if (valueInMs >= 60000d) { valueInMs /= 60000d; toReturn = StringUtil.toString(valueInMs, precision) + " min"; } else if (valueInMs >= 1000d) { valueInMs /= 1000d; toReturn = StringUtil.toString(valueInMs, precision) + " sec"; } else { toReturn = StringUtil.toString(valueInMs, precision) + " ms"; } return toReturn; } /** * Display the time with all the units. * * @param valueInMs * : value in milliseconds * @param displayZero * : Even if a unit is not relevant (equals to zero), it will be displayed. * @return <b>Example:</b> "2h 3min 40sec 350ms". */ public static String displayTimeAsStringWithUnits(double valueInMs, boolean displayZero) { String toReturn = ""; if (valueInMs >= 3600000d) { toReturn += (int) (valueInMs / 3600000) + "h "; valueInMs %= 3600000; } else if (displayZero) { toReturn += "00h "; } if (valueInMs >= 60000d) { toReturn += (int) (valueInMs / 60000) + "min "; valueInMs %= 60000; } else if (displayZero) { toReturn += "00min "; } if (valueInMs >= 1000d) { toReturn += (int) (valueInMs / 1000d) + "sec "; valueInMs %= 1000; } else if (displayZero) { toReturn += "00sec "; } if (valueInMs != 0) toReturn += StringUtil.toString(valueInMs, 2) + "ms"; else if (displayZero) toReturn += "000ms"; return toReturn; } }