/* * Units - Temporary implementation for Geotools 2 * Copyright (C) 1998 University Corporation for Atmospheric Research (Unidata) * 1998 Bill Hibbard & al. (VisAD) * 1999 Pêches et Océans Canada * 2000 Institut de Recherche pour le Développement * 2002 Centre for Computational Geography * * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details (http://www.gnu.org/). * * * This package is inspired from the units package of VisAD. * Unidata and Visad's work is fully acknowledged here. * * THIS IS A TEMPORARY CLASS * * This is a placeholder for future Unit class. * This skeleton will be removed when the real classes from * JSR-108: Units specification will be publicly available. */ package org.geotools.units; // Divers import org.geotools.resources.XMath; import org.geotools.resources.i18n.ErrorKeys; import org.geotools.resources.i18n.Errors; import org.geotools.resources.units.Units; /** * Classe représentant des unités proportionnelles à d'autres unités. * Les valeurs exprimées selon les unités d'un objet ScaledUnit * peuvent être converties vers le système d'unités {@link #unit} à l'aide de * l'équation suivante: * *
 * x{@link #unit} = xthis * {@link #amount}
 * 
* * Les objets ScaledUnit permettent de faire des conversions * entre différentes unités, par exemple des pieds en mètres. Cette classe * n'ayant pas de constructeur publique, la seule façon d'obtenir des unités * proportionnelles est d'utiliser les méthodes {@link #getInstance} ou * {@link #scale}. * * @version 1.0 * @author Steven R. Emmerson * @author Bill Hibbard * @author Martin Desruisseaux * * @deprecated Replaced by the {@link javax.units.Unit} framework. */ /*public*/ final class ScaledUnit extends Unit { /** * Serial number for interoperability with different versions. */ private static final long serialVersionUID = -5387831470112872874L; /** * Inverse d'une petite valeur servant à éviter des erreurs d'arrondissements. * Cette valeur est définie arbitrairement à 2^24, soit environ 1.678E+7. */ private static final double INV_EPS = 16777216; /** * Le facteur par lequel multiplier les mesures exprimées * selon ces unités pour obtenir des mesures exprimées * selon les unités {@link #unit}. */ public final double amount; /** * Les unités vers lesquelles se feront les conversions. * Il ne peut s'agir que d'unités de bases (par exemple * "m" ou "s") ou dérivées (par exemple "m/s"). */ public final SimpleUnit unit; /** * Construit une unité proportionnelle à l'unité de base ou dérivée * spécifiée. new Scale(0.44704, unit) créé * une unité de milles/heure si unit représentait des * mètres/seconde. * * @param amount Le facteur proportionnel. * @param unit L'unité de base ou dérivée associée. * @param symbol Le symbole associé à cette unité. Ne doit pas être nul. * @param prefix Liste des préfix pouvant être placés devant le symbole * symbol, ou null s'il n'y en a pas. */ private ScaledUnit(final double amount, final SimpleUnit unit, final String symbol, final PrefixSet prefix) { super(symbol, prefix); this.amount = amount; this.unit = unit; if (unit==null) { throw new NullPointerException(Errors.format(ErrorKeys.NO_UNIT)); } if (Double.isNaN(amount) || Double.isInfinite(amount) || amount==0) { throw new IllegalArgumentException(Errors.format( ErrorKeys.NOT_DIFFERENT_THAN_ZERO_$1, new Double(amount))); } } /** * Crée une nouvelle unité de même dimension que l'unité spécifiée, * mais dont les données devront être multipliée par un facteur. * Les conversions d'unités se feront par l'équation suivante: * *
* xunit = * xnew * amount *
* * où xunit représente une quantité mesurée * selon les unités unit et xnew * représente une quantité mesurée selon les unités retournées par cette méthode. * * Les unités crées par cette méthode getInstance servent * à passer d'un système de d'unités à l'autre. Par exemple on pourrait * construire des unités de miles anglais avec le code suivant: * *
     *     Unit MILE=ScaledUnit.getInstance(1609, METRE);
     * 
* * Ce code signifie qu'il faudra compter 1609 mètres dans chaque mille anglais. * * @param amount Facteur par lequel il faudra multiplier les mesures * exprimées selon les nouvelles unités pour les convertir dans * les unités unit. * @param unit Unités proportionnelles aux unités à créer. Les nouvelles * unités créées représenteront amount de ces unités. * @return Les unités créées, ou unit si le * paramètre amount est égal à 1. * * @see BaseUnit#getInstance * @see DerivedUnit#getInstance * @see OffsetUnit#getInstance */ public static Unit getInstance(final double amount, final SimpleUnit unit) { return getInstance(amount, unit, null, null); } /** * Crée une nouvelle unité de même dimension que l'unité spécifiée, * mais dont les données devront être multipliée par un facteur. * Les conversions d'unités se feront par l'équation suivante: * *
* xunit = * xnew * amount *
* * où xunit représente une quantité mesurée * selon les unités unit et xnew * représente une quantité mesurée selon les unités retournées par cette méthode. * * Les unités crées par cette méthode getInstance servent * à passer d'un système de d'unités à l'autre. Par exemple on pourrait * construire des unités de miles anglais avec le code suivant: * *
     *     Unit MILE=ScaledUnit.getInstance(1609, METRE, "mile", null);
     * 
* * Ce code signifie qu'il faudra compter 1609 mètres dans chaque mille anglais. * * @param amount Facteur par lequel il faudra multiplier les mesures * exprimées selon les nouvelles unités pour les convertir dans * les unités unit. * @param unit Unités proportionnelles aux unités à créer. Les nouvelles * unités créées représenteront amount de ces unités. * @param symbol Le symbole qui représentera les unités créées. Si * nul, alors un symbole par défaut sera créé. Dans l'exemple * précédent, ce symbole par défaut serait "\u00D71609 m" * @param prefix Liste des préfix pouvant être placés devant le symbole * symbol, ou null s'il n'y en a pas. * @return Les unités créées, ou unit si le * paramètre amount est égal à 1. * * @see BaseUnit#getInstance * @see DerivedUnit#getInstance * @see OffsetUnit#getInstance */ public static Unit getInstance(double amount, final SimpleUnit unit, String symbol, final PrefixSet prefix) { /* * Si amount est presqu'une puissance de 10, arrondi * à la puissance de 10 la plus proche. Cette étape vise à réduire * certaines erreurs d'arrondissement. */ final double power = Math.rint(XMath.log10(amount)*INV_EPS)/INV_EPS; if (power==Math.rint(power)) { amount=XMath.pow10(power); } /* * Retourne les unités. */ if (amount==1) { if (unit==null) { throw new NullPointerException(Errors.format(ErrorKeys.NO_UNIT)); } if (symbol!=null) { // TODO: Que faire si le symbole spécifié // n'est pas le symbole de 'unit'? } return unit; } if (symbol==null) { symbol = UnitFormat.DEFAULT.formatScaled(amount, unit, new StringBuffer()).toString(); return new ScaledUnit(amount, unit, symbol, null).internIgnoreSymbol(); } else { return new ScaledUnit(amount, unit, symbol, prefix).intern(); } } /** * Renvoie une unité identique à celle-ci, mais * avec un nouveau symbole et de nouveaux préfix. * * @param symbol Nouveau symbole représentant cette unité. Si ce * paramètre est nul, un symbole par défaut sera créé. * @param prefix Liste des préfix autorisés pour le symbole. * @return La même unité, mais avec le nouveau symbole. Peut être * this, mais ne sera jamais null. */ public Unit rename(final String symbol, final PrefixSet prefix) { // CAST return getInstance(amount, unit, symbol, prefix); } /** * Retourne le nom de l'unité dans la langue de l'utilisateur. * Par exemple le symbole "cm" sera traduit par "centimètre" * dans la langue française. Si aucun nom n'est disponible * pour l'unité courante, retourne simplement son symbole. */ public String getLocalizedName() { PrefixSet prefix=unit.prefix; String unpref=unit.getUnprefixedSymbol(); if (prefix!=null && symbol.endsWith(unpref)) { final Prefix p=prefix.getPrefix(symbol.substring(0, symbol.length()-unpref.length())); if (p!=null) { return p.getLocalizedName()+Units.localize(unpref); } } return super.getLocalizedName(); } /** * Retourne la quantité que représente cette unité. Les quantités sont des chaînes de * caractères qui décrivent le paramètre physique mesuré, comme "mass" ou "speed". Si * aucune quantité n'est définie pour cette unité, retourne null. */ public String getQuantityName() { return unit.getQuantityName(); } /** * Élève cette unité à une puissance entière. * * @param power La puissance à laquelle élever cette unité. * @return Les unités résultant de l'élévation des unités * this à la puissance power. * * @see #multiply * @see #divide * @see #scale * @see #shift */ public Unit pow(final int power) { switch (power) { case 0: return DerivedUnit.DIMENSIONLESS; case 1: return this; default: return getInstance(Math.pow(amount, power), /*CAST*/ (SimpleUnit) unit.pow(power)); } } /** * Élève ces unités à une puissance fractionnaire. Cette méthode est utile entre * autre pour prendre la racine carré d'un nombre, ce qui revient à l'élever à la * puissance ½. * * @param power La puissance à laquelle élever cette unité. * @return Les unités résultant de l'élévation des unités * this à la puissance power. * @throws UnitException Si cette unité ne peut pas être élevée * à une puissance non-entière. */ public Unit pow(final double power) throws UnitException { final int integer=(int) power; if (integer==power) { return pow(integer); } return getInstance(Math.pow(amount, power), /*CAST*/ (SimpleUnit) unit.pow(power)); } /** * Multiplie cette unité par une autre unité. * * @param that L'unité par laquelle multiplier cette unité. * @return Le produit de this par that. * @throws UnitException Si l'unité that est de la * classe {@link OffsetUnit} ou d'une autre classe invalide. * * @see #pow * @see #divide * @see #scale * @see #shift */ public Unit multiply(final Unit that) throws UnitException { return that.inverseMultiply(this); } /** * Multiply a base unit by a scaled unit * (that*this). */ Unit inverseMultiply(final BaseUnit that) throws UnitException { final SimpleUnit unitThatThis = (SimpleUnit) unit.inverseMultiply(that); return (unitThatThis!=unit) ? getInstance(amount, unitThatThis) : this; } /** * Multiply a derived unit by a scaled unit * (that*this). */ Unit inverseMultiply(final DerivedUnit that) throws UnitException { final SimpleUnit unitThatThis = (SimpleUnit) unit.inverseMultiply(that); return (unitThatThis!=unit) ? getInstance(amount, unitThatThis) : this; } /** * Multiply a scaled unit by a scaled unit * (that*this). */ Unit inverseMultiply(final ScaledUnit that) throws UnitException { final double newAmount = that.amount*this.amount; final SimpleUnit unitThatThis = (SimpleUnit) that.unit.multiply(unit); return getInstance(newAmount, unitThatThis); } /** * Divise cette unité par une autre unité. * * @param that L'unité par laquelle diviser cette unité. * @return Le quotient de this par that. * @throws UnitException Si l'unité that est de la * classe {@link OffsetUnit} ou d'une autre classe invalide. * * @see #pow * @see #multiply * @see #scale * @see #shift */ public Unit divide(final Unit that) throws UnitException { return that.inverseDivide(this); } /** * Divise a base unit by a scaled unit * (that/this). */ Unit inverseDivide(final BaseUnit that) throws UnitException { final SimpleUnit unitThatThis = /*CAST*/ (SimpleUnit) unit.inverseDivide(that); return (unitThatThis!=unit) ? getInstance(amount, unitThatThis) : this; } /** * Divide a derived unit by a scaled unit * (that/this). */ Unit inverseDivide(final DerivedUnit that) throws UnitException { final SimpleUnit unitThatThis = /*CAST*/ (SimpleUnit) unit.inverseDivide(that); return (unitThatThis!=unit) ? getInstance(amount, unitThatThis) : this; } /** * Divide a scaled unit by a scaled unit * (that/this). */ Unit inverseDivide(final ScaledUnit that) throws UnitException { final double newAmount = that.amount/this.amount; final SimpleUnit unitThatThis = /*CAST*/ (SimpleUnit) that.unit.divide(unit); return getInstance(newAmount, unitThatThis); } /** * Crée une nouvelle unité proportionnelle à cette unité. Par exemple * pour convertir en kilomètres des mesures exprimées en mètres, il * faut les diviser par 1000. On peut exprimer cette relation par le * code Unit km=metre.scale(1000). * * @param amount Facteur par lequel il faudra diviser les valeurs * exprimées selon ces unités pour obtenir des valeurs * exprimées selon les nouvelles unités. * @return Les nouvelles unités. * * @see #pow * @see #multiply * @see #divide * @see #shift */ public Unit scale(final double amount) { return (amount==1) ? this : getInstance(this.amount*amount, unit); } /** * Crée une nouvelle unité décalée par rapport à cette unité. Par exemple * pour convertir des degrés Kelvin en degrés Celsius, il faut soustraire * 273.15 aux degrés Kelvin. On peut exprimer cette relation par le code * Unit celsius=kelvin.shift(273.15). * * @param offset Constante à soustraire aux valeurs exprimées selon ces * unités pour obtenir des valeurs exprimées selon les nouvelles * unités. * @return Les nouvelles unités. * * @see #pow * @see #multiply * @see #divide * @see #scale */ public Unit shift(final double offset) { return (offset==0) ? this : OffsetUnit.getInstance(offset, this); } /** * Indique si les unités this et that sont compatibles. * Si elles le sont, alors les méthodes convert ne lanceront jamais * d'exception pour ces unités. * * @param that Autre unités avec laquelle on veut * vérifier si ces unités sont compatibles. * @return true Si l'on garantie que les méthodes * convert ne lanceront pas d'exceptions. */ public boolean canConvert(final Unit that) { return unit.canConvert(that); } /** * Effectue la conversion d'une mesure exprimée selon d'autres unités. Par * exemple METRE.convert(1, FOOT) retournera 0.3048. * * @param value La valeur exprimée selon les autres unités (fromUnit). * @param fromUnit Les autres unités. * @return La valeur convertie selon ces unités (this). * @throws UnitException Si les unités ne sont pas compatibles. */ public double convert(final double value, final Unit fromUnit) throws UnitException { if (fromUnit==this) { return value; // sligh optimization } return unit.convert(value, fromUnit)/amount; } /** * Effectue sur-place la conversion de mesures exprimées selon d'autres * unités. Les valeurs converties remplaceront les anciennes valeurs. * * @param values En entré, les valeurs exprimées selon les autres unités * (fromUnit). En sortie, les valeurs exprimées selon ces * unités (this). * @param fromUnit Les autres unités. * @throws UnitException Si les unités ne sont pas compatibles. Dans ce * cas, aucun élément de values n'aura été modifié. */ public void convert(final double[] values, final Unit fromUnit) throws UnitException { if (!equalsIgnoreSymbol(fromUnit)) { unit.convert(values, fromUnit); for (int i=0; ifromUnit est de la classe {@link OffsetUnit}. * * @param values En entré, les valeurs exprimées selon les autres * unités (fromUnit). En sortie, les valeurs exprimées * selon ces unités (this). * @param fromUnit Les autres unités. * @throws UnitException Si les unités ne sont pas compatibles. Dans ce * cas, aucun élément de values n'aura été modifié. */ public void convert(final float[] values, final Unit fromUnit) throws UnitException { if (!equalsIgnoreSymbol(fromUnit)) { unit.convert(values, fromUnit); for (int i=0; ifromUnit * vers les unités this. Cette méthode ne * retourne jamais null. * @throws UnitException Si les unités ne sont pas compatibles. */ public UnitTransform getTransform(final Unit fromUnit) throws UnitException { if (!equalsIgnoreSymbol(fromUnit)) { final UnitTransform tr=unit.getTransform(fromUnit); if (tr instanceof ScaledTransform) { return ScaledTransform.getInstance(fromUnit, this, ((ScaledTransform) tr).amount*amount); } else { return CompoundTransform.getInstance(tr, ScaledTransform.getInstance(unit, this, amount)); } } else { return IdentityTransform.getInstance(fromUnit, this); } } /** * Convertit une mesure vers d'autre unités. Par exemple * METRE.inverseConvert(1, FOOT) retournera * 3.2808. Cette méthode est l'inverse de la méthode * {@link #convert(double,Unit)}. * * @param value La valeur exprimée selon ces unités (this). * @param toUnit Les autres unités. * @return La valeur convertie selon les autres unités (toUnit). * @throws UnitException Si les unités ne sont pas compatibles. */ protected double inverseConvert(final double value, final Unit toUnit) throws UnitException { if (toUnit==this) { return value; // sligh optimization } return unit.inverseConvert(value*amount, toUnit); } /** * Effectue sur-place la conversion de mesures vers d'autres unités. * Les valeurs converties remplaceront les anciennes valeurs. Cette * méthode est l'inverse de la méthode {@link #convert(double[],Unit)}. * * @param values En entré, les valeur exprimées selon ces unités * (this). En sortie, les valeurs exprimées * selon les autres unités (toUnit). * @param toUnit Les autres unités. * @throws UnitException Si les unités ne sont pas compatibles. Dans ce * cas, aucun élément de values n'aura été modifié. */ protected void inverseConvert(final double[] values, final Unit toUnit) throws UnitException { if (!equalsIgnoreSymbol(toUnit)) { if (unit.canConvert(toUnit)) { for (int i=0; ithis). En sortie, les valeurs exprimées * selon les autres unités (toUnit). * @param toUnit Les autres unités. * @throws UnitException Si les unités ne sont pas compatibles. Dans ce * cas, aucun élément de values n'aura été modifié. */ protected void inverseConvert(final float[] values, final Unit toUnit) throws UnitException { if (!equalsIgnoreSymbol(toUnit)) { if (unit.canConvert(toUnit)) { for (int i=0; ithis * vers les unités toUnit. Cette méthode * ne retourne jamais null. * @throws UnitException Si les unités ne sont pas compatibles. */ protected UnitTransform getInverseTransform(final Unit toUnit) throws UnitException { if (!equalsIgnoreSymbol(toUnit)) { final UnitTransform tr=InverseTransform.getInstance(unit, toUnit); if (tr instanceof ScaledTransform) { final double amount=((ScaledTransform) tr).amount/this.amount; return ScaledTransform.getInstance(this, toUnit, amount); } else { return CompoundTransform.getInstance(ScaledTransform.getInstance(this, unit, 1/amount), tr); } } else { return IdentityTransform.getInstance(this, toUnit); } } /** * Indique si deux unités sont égales, en ignorant leurs symboles. Le * champs {@link #symbol} de chacune des deux unités ne sera pas pris * en compte. */ public boolean equalsIgnoreSymbol(final Unit unit) { if (unit instanceof ScaledUnit) { final ScaledUnit that = (ScaledUnit) unit; return Double.doubleToLongBits(this.amount)==Double.doubleToLongBits(that.amount) && this.unit.equalsIgnoreSymbol(that.unit); } else { return false; } } /** * Retourne un code * pour cette unité. */ public int hashCode() { final long code = Double.doubleToLongBits(amount); return (int) code ^ (int) (code >>> 32) ^ unit.hashCode(); } }