/*
* 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.i18n.ErrorKeys;
import org.geotools.resources.i18n.Errors;
/**
* Classe représentant des unités décalées par rapport à d'autres unités.
* Les valeurs exprimées selon les unités d'un objet OffsetUnit
* peuvent être converties vers le système d'unités {@link #unit} à l'aide
* de l'équation suivante:
*
*
* * Les objets* x{@link #unit} = {@link #offset} + xthis
*
OffsetUnit
permettent de faire des conversions entre
* différentes unités, par exemple des degrés Kelvin aux degrés Celsius. Cette
* classe n'ayant pas de constructeur publique, la seule façon d'obtenir des
* unités décalées est d'utiliser les méthodes {@link #getInstance} ou {@link #offset}.
*
* NOTE: Des erreurs d'arrondissements importants peuvent survenir lors des
* conversions d'unités utilisant des objets OffsetUnit
. La densité Sigma-T,
* utilisée en océanographie, en est un bon exemple. Cette densité Sigma-T est la densité de l'eau
* de mer auquel on a retranchée 1000 kg/m³. Par exemple une eau de mer de densité 1024 kg/m³
* a une densité Sigma-T de 24 kg/m³. Dans un nombre réel de type float
, un nombre
* de l'ordre de 24 peut être mémorisé avec environ 6 chiffres après la virgules. Mais en revanche,
* un nombre de l'ordre de 1024 ne peut être mémorisé qu'avec environ 3 ou 4 chiffres après la virgule.
* Les conversions des densités Sigma-T en densité ordinaires (qui consistent à ajouter 1000 kg/m³)
* risquent donc de se traduire par une perte de 2 ou 3 chiffres significatifs de cette densité. Une façon
* d'éviter ce problème est de convertir tous les tableaux de float
en double
* avant de convertir les unités.
OffsetUnit
.
* @param symbol Le symbole associé à cette unité.
* @param prefix Liste des préfix pouvant être placés devant le symbole
* symbol
, ou null
s'il n'y en a pas.
*/
private OffsetUnit(final double offset, final Unit unit, final String symbol, final PrefixSet prefix) {
super(symbol, prefix);
this.offset = offset;
this.unit = unit;
if (unit==null) {
throw new NullPointerException(Errors.format(ErrorKeys.NO_UNIT));
}
if (Double.isNaN(offset) || Double.isInfinite(offset)) {
throw new IllegalArgumentException(Errors.format(
ErrorKeys.NOT_A_NUMBER_$1,
new Double(offset)));
}
}
/**
* Crée une nouvelle unité de même dimension que l'unité spécifiée, mais dont les mesures
* seront décalées d'une constante. Les conversions d'unités se feront par l'équation suivante:
*
* unit
=
* xnew
+ offset
* unit
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.
*
* Par exemple on pourrait construire des unités de
* température en degrés celsius avec le code suivant:
*
* * * Ce code signifie que 0°C correspond à 273.15°K. * * @param offset Constante qu'il faudra additionner aux mesures * exprimées selon les nouvelles unités pour les convertir * dans les unités* Unit CELSIUS=OffsetUnit.getInstance(273.15, KELVIN); *
unit
.
* @param unit Unités par rapport à lesquelles
* décaler les nouvelles unités.
* @return Les unités créées, ou unit
si le
* paramètre offset
est égal à 0.
*
* @see BaseUnit#getInstance
* @see DerivedUnit#getInstance
* @see ScaledUnit#getInstance
*/
public static Unit getInstance(final double offset, final Unit unit) {
return getInstance(offset, unit, null, null);
}
/**
* Crée une nouvelle unité de même dimension que l'unité spécifiée, mais dont les mesures
* seront décalées d'une constante. Les conversions d'unités se feront par l'équation suivante:
*
* unit
=
* xnew
+ offset
* unit
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.
*
* Par exemple on pourrait construire des unités de
* température en degrés celsius avec le code suivant:
*
* * * Ce code signifie que 0°C correspond à 273.15°K. * * @param offset Constante qu'il faudra additionner aux mesures * exprimées selon les nouvelles unités pour les convertir * dans les unités* Unit CELSIUS=OffsetUnit.getInstance(273.15, KELVIN, "°C", null); *
unit
.
* @param unit Unités par rapport à lesquelles
* décaler les nouvelles 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 "+273.15 K"
* @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 offset
est égal à 0.
*
* @see BaseUnit#getInstance
* @see DerivedUnit#getInstance
* @see ScaledUnit#getInstance
*/
public static Unit getInstance(final double offset, final Unit unit, final String symbol, final PrefixSet prefix) {
if (offset==0) {
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) {
return new OffsetUnit(offset, unit, UnitFormat.DEFAULT.formatOffset(offset, unit, new StringBuffer()).toString(), null).internIgnoreSymbol();
} else {
return new OffsetUnit(offset, 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(offset, unit, symbol, prefix);
}
/**
* 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();
}
/**
* Crée une nouvelle unité proportionnelle à cette unité. Par exemple pour
* convertir en millicelsius des températures exprimées en degrés Celsius,
* il faut les multiplier par 1000. On peut exprimer cette relation par le
* code Unit mdegC=degC.scale(0.001)
.
*
* @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 #shift
*/
public Unit scale(final double amount) {
return (amount==1) ? this : getInstance(offset/amount, unit.scale(amount));
}
/**
* 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 #scale
*/
public Unit shift(final double offset) {
return (offset==0) ? this : getInstance(this.offset+offset, unit);
}
/**
* 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 KELVIN.convert(15, CELSIUS)
retournera 288.15
.
*
* @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)-offset;
}
/**
* 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
). 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; ithis
. 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 OffsetTransform) {
return OffsetTransform.getInstance(fromUnit, this, ((OffsetTransform) tr).offset+offset);
} else {
return CompoundTransform.getInstance(tr, OffsetTransform.getInstance(unit, this, offset));
}
} else {
return IdentityTransform.getInstance(fromUnit, this);
}
}
/**
* Convertit une mesure vers d'autre unités.
*
* @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+offset, 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; itoUnit
).
* @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; itoUnit
. 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=unit.getInverseTransform(toUnit);
if (tr instanceof OffsetTransform) {
return ScaledTransform.getInstance(this, toUnit, ((OffsetTransform) tr).offset-offset);
} else {
return CompoundTransform.getInstance(OffsetTransform.getInstance(this, unit, -offset), 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 OffsetUnit) {
final OffsetUnit that = (OffsetUnit) unit;
return Double.doubleToLongBits(this.offset)==Double.doubleToLongBits(that.offset) &&
this.unit.equalsIgnoreSymbol(that.unit);
} else {
return false;
}
}
/**
* Retourne un code
* pour cette unité.
*/
public int hashCode() {
final long code=Double.doubleToLongBits(offset);
return (int) code ^ (int) (code >>> 32) ^ unit.hashCode();
}
}