This is an automated email from the ASF dual-hosted git repository.
desruisseaux pushed a commit to branch geoapi-4.0
in repository https://gitbox.apache.org/repos/asf/sis.git
The following commit(s) were added to refs/heads/geoapi-4.0 by this push:
new bb54c72 Add support of decibel unit (dB). This is used in some netCDF
files.
bb54c72 is described below
commit bb54c72ee99d0767dbf767ce3096cf1710d81383
Author: Martin Desruisseaux <[email protected]>
AuthorDate: Wed Jan 16 23:26:04 2019 +0100
Add support of decibel unit (dB). This is used in some netCDF files.
---
.../org/apache/sis/internal/converter/Column.java | 2 +-
.../org/apache/sis/measure/AbstractConverter.java | 58 ++++++
.../org/apache/sis/measure/LinearConverter.java | 10 -
.../java/org/apache/sis/measure/PowerOf10.java | 208 +++++++++++++++++++++
.../apache/sis/measure/SexagesimalConverter.java | 51 -----
.../java/org/apache/sis/measure/UnitRegistry.java | 2 +-
.../main/java/org/apache/sis/measure/Units.java | 17 +-
.../org/apache/sis/measure/UnitNames.properties | 2 +
.../org/apache/sis/measure/UnitNames_fr.properties | 1 +
.../org/apache/sis/measure/UnitFormatTest.java | 1 +
.../java/org/apache/sis/measure/UnitsTest.java | 22 +++
11 files changed, 309 insertions(+), 65 deletions(-)
diff --git
a/core/sis-utility/src/main/java/org/apache/sis/internal/converter/Column.java
b/core/sis-utility/src/main/java/org/apache/sis/internal/converter/Column.java
index eff74f7..20e92c8 100644
---
a/core/sis-utility/src/main/java/org/apache/sis/internal/converter/Column.java
+++
b/core/sis-utility/src/main/java/org/apache/sis/internal/converter/Column.java
@@ -76,7 +76,7 @@ final class Column extends TableColumn<Class<?>> implements
Serializable {
}
/**
- * Resources to the singleton instance on deserialization.
+ * Returns the singleton instance on deserialization.
*/
private Object readResolve() throws ObjectStreamException {
return target ? TARGET : SOURCE;
diff --git
a/core/sis-utility/src/main/java/org/apache/sis/measure/AbstractConverter.java
b/core/sis-utility/src/main/java/org/apache/sis/measure/AbstractConverter.java
index b823239..4649573 100644
---
a/core/sis-utility/src/main/java/org/apache/sis/measure/AbstractConverter.java
+++
b/core/sis-utility/src/main/java/org/apache/sis/measure/AbstractConverter.java
@@ -16,8 +16,11 @@
*/
package org.apache.sis.measure;
+import java.util.List;
+import java.util.Collections;
import java.io.Serializable;
import javax.measure.UnitConverter;
+import org.apache.sis.util.ArgumentChecks;
import org.apache.sis.math.DecimalFunctions;
@@ -42,6 +45,26 @@ abstract class AbstractConverter implements UnitConverter,
Serializable {
}
/**
+ * Returns {@code true} if {@link #convert(double)} returns given values
unchanged.
+ * The default implementation returns {@code false} for convenience of
non-linear conversions.
+ * Subclasses should override if their conversions may be identity.
+ */
+ @Override
+ public boolean isIdentity() {
+ return false;
+ }
+
+ /**
+ * Indicates if this converter is linear in JSR-363 sense (not the usual
mathematical sense).
+ * The default implementation returns {@code false} for convenience of
non-linear conversions.
+ * Subclasses should override if their conversions may be identity.
+ */
+ @Override
+ public boolean isLinear() {
+ return false;
+ }
+
+ /**
* If the conversion can be represented by a polynomial equation, returns
the coefficients of that equation.
* Otherwise returns {@code null}.
*/
@@ -50,6 +73,16 @@ abstract class AbstractConverter implements UnitConverter,
Serializable {
}
/**
+ * Performs a unit conversion on the given number. The default
implementation delegates to the version working
+ * on {@code double} primitive type, so it may not provide the accuracy
normally required by this method contract.
+ * Linear conversions should override this method.
+ */
+ @Override
+ public Number convert(final Number value) {
+ return convert(value.doubleValue());
+ }
+
+ /**
* Returns the derivative of the conversion function at the given value,
or {@code NaN} if unknown.
*/
public abstract double derivative(double value);
@@ -96,4 +129,29 @@ abstract class AbstractConverter implements UnitConverter,
Serializable {
static boolean epsilonEquals(final double expected, final double actual) {
return Math.abs(expected - actual) <= Math.scalb(Math.ulp(expected),
4);
}
+
+ /**
+ * Concatenates this converter with another converter. The resulting
converter is equivalent to first converting
+ * by the specified converter (right converter), and then converting by
this converter (left converter).
+ *
+ * <p>The default implementation is okay, but subclasses should override
if they can detect optimizations.</p>
+ */
+ @Override
+ public UnitConverter concatenate(final UnitConverter converter) {
+ ArgumentChecks.ensureNonNull("converter", converter);
+ if (equals(converter.inverse())) {
+ return LinearConverter.IDENTITY;
+ }
+ return new ConcatenatedConverter(converter, this);
+ }
+
+ /**
+ * Returns the steps of fundamental converters making up this converter.
The default implementation returns
+ * only {@code this} on the assumption that this conversion is not a
concatenation of other converters.
+ * Subclasses should override if this assumption does not hold.
+ */
+ @Override
+ public List<UnitConverter> getConversionSteps() {
+ return Collections.singletonList(this);
+ }
}
diff --git
a/core/sis-utility/src/main/java/org/apache/sis/measure/LinearConverter.java
b/core/sis-utility/src/main/java/org/apache/sis/measure/LinearConverter.java
index e5b6863..5c02295 100644
--- a/core/sis-utility/src/main/java/org/apache/sis/measure/LinearConverter.java
+++ b/core/sis-utility/src/main/java/org/apache/sis/measure/LinearConverter.java
@@ -16,8 +16,6 @@
*/
package org.apache.sis.measure;
-import java.util.List;
-import java.util.Collections;
import java.math.BigDecimal;
import java.math.BigInteger;
import javax.measure.UnitConverter;
@@ -391,14 +389,6 @@ final class LinearConverter extends AbstractConverter
implements LenientComparab
}
/**
- * Returns the steps of fundamental converters making up this converter,
which is only {@code this}.
- */
- @Override
- public List<LinearConverter> getConversionSteps() {
- return Collections.singletonList(this);
- }
-
- /**
* Returns a hash code value for this unit converter.
*/
@Override
diff --git
a/core/sis-utility/src/main/java/org/apache/sis/measure/PowerOf10.java
b/core/sis-utility/src/main/java/org/apache/sis/measure/PowerOf10.java
new file mode 100644
index 0000000..82debb9
--- /dev/null
+++ b/core/sis-utility/src/main/java/org/apache/sis/measure/PowerOf10.java
@@ -0,0 +1,208 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sis.measure;
+
+import java.io.ObjectStreamException;
+import javax.measure.UnitConverter;
+import org.apache.sis.math.MathFunctions;
+
+
+/**
+ * Conversions from units represented by a logarithm in base 10.
+ *
+ * @author Martin Desruisseaux (Geomatys)
+ * @version 1.0
+ * @since 1.0
+ * @module
+ */
+final class PowerOf10 extends AbstractConverter {
+ /**
+ * For cross-version compatibility.
+ */
+ private static final long serialVersionUID = 7960860506196831772L;
+
+ /**
+ * Value of {@code Math.log(10)}.
+ */
+ private static final double LN_10 = 2.302585092994046;
+
+ /**
+ * The singleton instance. Can be used for conversion from neper units to
{@link Units#UNITY}.
+ */
+ private static final UnitConverter INSTANCE = new PowerOf10();
+
+ /**
+ * Returns the converter from bel unit (B) to dimensionless unit. ISO
80000-3:2006 defines
+ * 1 B = ln(10)/2 Np (neper) and 1 Np = 1 (dimensionless), keeping in mind
that neper is a
+ * logarithmic scale using the natural logarithm. The ln(10) factor is for
converting from
+ * base ℯ to base 10.
+ *
+ * <p>The method of expressing a ratio as a level in decibels depends on
whether the measured property
+ * is a power quantity or a root-power quantity (amplitude of a field).
This is because power is often
+ * proportional to the square of the amplitude. The /2 in above equation
is for taking the square root
+ * of a power, since B is used for power and Np is used for root-power.</p>
+ *
+ * <p>The bel represents the logarithm of a ratio between two power
quantities of 10:1.
+ * Two signals whose levels differ by <var>x</var> bels have a power ratio
of 10^x and
+ * an amplitude (field quantity) ratio of 10^(x/2).</p>
+ *
+ * @see <a href="https://en.wikipedia.org/wiki/Decibel#Definition">Decibel
on Wikipedia</a>
+ */
+ static UnitConverter belToOne() {
+ /*
+ * We do not put LN_10 in numerator because convert(x) uses log10(x)
instead of ln(x),
+ * do the multiplication by ln(10) will be implicitly done.
+ */
+ return new ConcatenatedConverter(LinearConverter.scale(1, 2),
INSTANCE);
+ }
+
+ /**
+ * Creates the singleton instance.
+ */
+ private PowerOf10() {
+ }
+
+ /**
+ * Returns the singleton instance on deserialization.
+ */
+ private Object readResolve() throws ObjectStreamException {
+ return INSTANCE;
+ }
+
+ /**
+ * Returns the inverse of this converter.
+ */
+ @Override public UnitConverter inverse() {
+ return Logarithm.INSTANCE;
+ }
+
+ /**
+ * Applies the unit conversion on the given value.
+ */
+ @Override
+ public double convert(final double value) {
+ return MathFunctions.pow10(value);
+ }
+
+ /**
+ * Returns the derivative of this conversion at the given value.
+ */
+ @Override
+ public double derivative(final double value) {
+ return LN_10 * MathFunctions.pow10(value);
+ }
+
+ /**
+ * Compares this converter with the given object for equality.
+ */
+ @Override
+ public boolean equals(final Object other) {
+ return (other instanceof PowerOf10);
+ }
+
+ /**
+ * Returns a hash code value for this unit converter.
+ */
+ @Override
+ public int hashCode() {
+ return (int) serialVersionUID;
+ }
+
+ /**
+ * Returns a string representation of this converter for debugging purpose.
+ */
+ @Override
+ public String toString() {
+ return "y = 10^x";
+ }
+
+ /**
+ * Inverse of {@link PowerOf10}.
+ */
+ private static final class Logarithm extends AbstractConverter {
+ /**
+ * For cross-version compatibility.
+ */
+ private static final long serialVersionUID = -7089883299592861677L;
+
+ /**
+ * The singleton instance.
+ */
+ static final UnitConverter INSTANCE = new Logarithm();
+
+ /**
+ * Creates the singleton instance.
+ */
+ private Logarithm() {
+ }
+
+ /**
+ * Returns the singleton instance on deserialization.
+ */
+ private Object readResolve() throws ObjectStreamException {
+ return INSTANCE;
+ }
+
+ /**
+ * Returns the inverse of this converter.
+ */
+ @Override
+ public UnitConverter inverse() {
+ return PowerOf10.INSTANCE;
+ }
+
+ /**
+ * Applies the unit conversion on the given value.
+ */
+ @Override
+ public double convert(final double value) {
+ return Math.log10(value);
+ }
+
+ /**
+ * Returns the derivative of this conversion at the given value.
+ */
+ @Override
+ public double derivative(final double value) {
+ return 1 / (value * LN_10);
+ }
+
+ /**
+ * Returns a hash code value for this unit converter.
+ */
+ @Override
+ public int hashCode() {
+ return (int) serialVersionUID;
+ }
+
+ /**
+ * Compares this converter with the given object for equality.
+ */
+ @Override
+ public boolean equals(final Object other) {
+ return (other instanceof Logarithm);
+ }
+
+ /**
+ * Returns a string representation of this converter for debugging
purpose.
+ */
+ @Override
+ public String toString() {
+ return "y = log10(x)";
+ }
+ }
+}
diff --git
a/core/sis-utility/src/main/java/org/apache/sis/measure/SexagesimalConverter.java
b/core/sis-utility/src/main/java/org/apache/sis/measure/SexagesimalConverter.java
index 857db03..850f243 100644
---
a/core/sis-utility/src/main/java/org/apache/sis/measure/SexagesimalConverter.java
+++
b/core/sis-utility/src/main/java/org/apache/sis/measure/SexagesimalConverter.java
@@ -16,12 +16,9 @@
*/
package org.apache.sis.measure;
-import java.util.List;
-import java.util.Collections;
import javax.measure.Unit;
import javax.measure.quantity.Angle;
import javax.measure.UnitConverter;
-import org.apache.sis.util.ArgumentChecks;
import org.apache.sis.util.resources.Errors;
import org.apache.sis.util.resources.Vocabulary;
import org.apache.sis.internal.util.Numerics;
@@ -158,31 +155,6 @@ class SexagesimalConverter extends AbstractConverter {
}
/**
- * Returns {@code false} since this converter is not an identity function.
- */
- @Override
- public boolean isIdentity() {
- return false;
- }
-
- /**
- * Returns {@code false} since the conversion is non-linear.
- */
- @Override
- public boolean isLinear() {
- return false;
- }
-
- /**
- * Returns a collection containing only {@code this} since this conversion
is not
- * a concatenation of other converters.
- */
- @Override
- public List<? extends UnitConverter> getConversionSteps() {
- return Collections.singletonList(this);
- }
-
- /**
* Returns the inverse of this converter.
*/
@Override
@@ -208,16 +180,6 @@ class SexagesimalConverter extends AbstractConverter {
}
/**
- * Performs a conversion from fractional degrees to sexagesimal degrees.
- * This method delegates to the version working on {@code double}
primitive type,
- * so it may not provide the accuracy normally required by this method
contract.
- */
- @Override
- public final Number convert(final Number value) {
- return convert(value.doubleValue());
- }
-
- /**
* Considers this converter as non-derivable. Actually it would be
possible to provide a derivative value
* for input values other than the discontinuities points, but for now we
presume that it is less dangerous
* to return NaN every time, so the user can not miss that this function
is not derivable everywhere.
@@ -228,19 +190,6 @@ class SexagesimalConverter extends AbstractConverter {
}
/**
- * Concatenates this converter with another converter. The resulting
converter is equivalent to first converting
- * by the specified converter (right converter), and then converting by
this converter (left converter).
- */
- @Override
- public UnitConverter concatenate(final UnitConverter converter) {
- ArgumentChecks.ensureNonNull("converter", converter);
- if (equals(converter.inverse())) {
- return LinearConverter.IDENTITY;
- }
- return new ConcatenatedConverter(converter, this);
- }
-
- /**
* Compares this converter with the specified object.
*/
@Override
diff --git
a/core/sis-utility/src/main/java/org/apache/sis/measure/UnitRegistry.java
b/core/sis-utility/src/main/java/org/apache/sis/measure/UnitRegistry.java
index fb12216..c475f7d 100644
--- a/core/sis-utility/src/main/java/org/apache/sis/measure/UnitRegistry.java
+++ b/core/sis-utility/src/main/java/org/apache/sis/measure/UnitRegistry.java
@@ -50,7 +50,7 @@ final class UnitRegistry implements SystemOfUnits,
Serializable {
/**
* A bitmask specifying that the unit symbol can be combined with a SI
prefix.
* This is usually combined only with {@link #SI}, not {@link #ACCEPTED}
except
- * the litre unit (cL, mL, etc).
+ * the litre unit (cL, mL, etc) and bel (for creating the decibel unit).
*/
static final byte PREFIXABLE = 1;
diff --git a/core/sis-utility/src/main/java/org/apache/sis/measure/Units.java
b/core/sis-utility/src/main/java/org/apache/sis/measure/Units.java
index fa76b6d..3443fbb 100644
--- a/core/sis-utility/src/main/java/org/apache/sis/measure/Units.java
+++ b/core/sis-utility/src/main/java/org/apache/sis/measure/Units.java
@@ -74,7 +74,7 @@ import static org.apache.sis.measure.UnitRegistry.PREFIXABLE;
* </table>
*
* @author Martin Desruisseaux (IRD, Geomatys)
- * @version 0.8
+ * @version 1.0
* @since 0.3
* @module
*/
@@ -1026,6 +1026,13 @@ public final class Units extends Static {
public static final Unit<Dimensionless> PPM;
/**
+ * Sub-division of logarithm of ratio of the measured quantity to a
reference quantity (dB).
+ *
+ * @since 1.0
+ */
+ public static final Unit<Dimensionless> DECIBEL;
+
+ /**
* Salinity measured using PSS-78. While this is a dimensionless
measurement, the {@code "psu"} symbol
* is sometime added to PSS-78 measurement. However this is officially
discouraged.
*
@@ -1218,12 +1225,15 @@ public final class Units extends Static {
* All Unit<Dimensionless>.
*/
final SystemUnit<Salinity> sal;
+ ConventionalUnit<Dimensionless> bel;
SIGMA = add(Dimensionless.class, Scalar.Dimensionless::new,
dimensionless, "sigma", OTHER, (short) 0);
PIXEL = add(Dimensionless.class, Scalar.Dimensionless::new,
dimensionless, "px", OTHER, (short) 0);
sal = add(Salinity.class, null,
dimensionless, null, OTHER, (short) 0);
PSU = add(sal, milli,
"psu", OTHER, (short) 0);
PERCENT = add(one, centi,
"%", OTHER, (short) 0);
PPM = add(one, micro,
"ppm", OTHER, (short) 9202);
+ bel = add(one, PowerOf10.belToOne(), "B", (byte) (ACCEPTED |
PREFIXABLE), (short) 0);
+ DECIBEL = add(bel, Prefixes.converter('d'), "dB", ACCEPTED, (short) 0);
UNITY = UnitRegistry.init(one); // Must be last in order to take
precedence over all other units associated to UnitDimension.NONE.
UnitRegistry.alias(UNITY, Short.valueOf((short) 9203));
@@ -1264,6 +1274,9 @@ public final class Units extends Static {
* Invoked by {@code Units} static class initializer for registering SI
conventional units.
* This method shall be invoked in a single thread by the {@code Units}
class initializer only.
*
+ * <p>The {@code target} argument should be an instance of {@link
SystemUnit}.
+ * The only exception is for creating the {@link DECIBEL} unit base on the
bel conventional unit.</p>
+ *
* <p>If the {@code target} unit holds a list of {@linkplain
SystemUnit#related() related units}
* (i.e. conventional units that can not be computed easily by appending a
SI prefix), then the new
* conventional unit is added to that list of related units. For example
"foot" is related to "metre"
@@ -1271,7 +1284,7 @@ public final class Units extends Static {
* because this relationship can be inferred automatically without the
need of a {@code related} table.
* The unrecorded units are all SI units related to {@code target} by a
scale factor without offset.</p>
*/
- private static <Q extends Quantity<Q>> ConventionalUnit<Q>
add(SystemUnit<Q> target, UnitConverter toTarget, String symbol, byte scope,
short epsg) {
+ private static <Q extends Quantity<Q>> ConventionalUnit<Q>
add(AbstractUnit<Q> target, UnitConverter toTarget, String symbol, byte scope,
short epsg) {
final ConventionalUnit<Q> unit = UnitRegistry.init(new
ConventionalUnit<>(target, toTarget, symbol, scope, epsg));
final ConventionalUnit<Q>[] related = target.related();
if (related != null && (unit.scope != UnitRegistry.SI ||
!toTarget.isLinear())) {
diff --git
a/core/sis-utility/src/main/resources/org/apache/sis/measure/UnitNames.properties
b/core/sis-utility/src/main/resources/org/apache/sis/measure/UnitNames.properties
index 11c36bf..a196184 100644
---
a/core/sis-utility/src/main/resources/org/apache/sis/measure/UnitNames.properties
+++
b/core/sis-utility/src/main/resources/org/apache/sis/measure/UnitNames.properties
@@ -3,10 +3,12 @@
A=ampere
a=year
atm=atmosphere
+B=bel
C=coulomb
cd=candela
cm=centimetre
d=day
+dB=decibel
dbar=decibar
F=farad
ft=foot
diff --git
a/core/sis-utility/src/main/resources/org/apache/sis/measure/UnitNames_fr.properties
b/core/sis-utility/src/main/resources/org/apache/sis/measure/UnitNames_fr.properties
index 708a734..f691c8a 100644
---
a/core/sis-utility/src/main/resources/org/apache/sis/measure/UnitNames_fr.properties
+++
b/core/sis-utility/src/main/resources/org/apache/sis/measure/UnitNames_fr.properties
@@ -5,6 +5,7 @@ a=ann
atm=atmosph�re
cm=centim�tre
d=jour
+dB=d�cibel
ft=pied
ftCla=pied de Clarke
ftUS=pied am�ricain
diff --git
a/core/sis-utility/src/test/java/org/apache/sis/measure/UnitFormatTest.java
b/core/sis-utility/src/test/java/org/apache/sis/measure/UnitFormatTest.java
index 6ed271e..af2b8e7 100644
--- a/core/sis-utility/src/test/java/org/apache/sis/measure/UnitFormatTest.java
+++ b/core/sis-utility/src/test/java/org/apache/sis/measure/UnitFormatTest.java
@@ -126,6 +126,7 @@ public final strictfp class UnitFormatTest extends TestCase
{
verify(declared, "PPM", "", "ppm",
"parts per million", Units.PPM);
verify(declared, "PSU", "", "psu",
"practical salinity unit", Units.PSU);
verify(declared, "PIXEL", "", "px",
"pixel", Units.PIXEL);
+ verify(declared, "DECIBEL", "", "dB",
"decibel", Units.DECIBEL);
assertTrue("Missing units in test:" + declared, declared.isEmpty());
}
diff --git
a/core/sis-utility/src/test/java/org/apache/sis/measure/UnitsTest.java
b/core/sis-utility/src/test/java/org/apache/sis/measure/UnitsTest.java
index 0f4e632..cde077a 100644
--- a/core/sis-utility/src/test/java/org/apache/sis/measure/UnitsTest.java
+++ b/core/sis-utility/src/test/java/org/apache/sis/measure/UnitsTest.java
@@ -210,6 +210,27 @@ public final strictfp class UnitsTest extends TestCase {
}
/**
+ * Tests the conversion factor of {@link Units#DECIBEL}.
+ *
+ * @throws IncommensurableException if the conversion can not be applied.
+ *
+ * @see <a
href="https://en.wikipedia.org/wiki/Decibel#Conversions">Decibel on
Wikipedia</a>
+ */
+ @Test
+ public void testDecibelConversionFactor() throws IncommensurableException {
+ final Unit<?> bel = Units.valueOf("B");
+ assertEquals(10, bel.getConverterToAny(DECIBEL).convert(1),
STRICT);
+ assertEquals(0.1, DECIBEL.getConverterToAny(bel).convert(1),
STRICT);
+ assertEquals(3.16228, bel.getConverterToAny(UNITY).convert(1),
5E-6);
+ assertEquals(1.12202, DECIBEL.getConverterToAny(UNITY).convert(1),
5E-6);
+ /*
+ * Reverse of last two lines above.
+ */
+ assertEquals(1, UNITY.getConverterToAny(bel) .convert(3.16228),
2E-5);
+ assertEquals(1, UNITY.getConverterToAny(DECIBEL).convert(1.12202),
2E-5);
+ }
+
+ /**
* Tests getting a unit for a given quantity type.
*/
@Test
@@ -298,6 +319,7 @@ public final strictfp class UnitsTest extends TestCase {
assertSame(CELSIUS, valueOf("degree Celsius"));
assertSame(CELSIUS, valueOf("degree_Celcius"));
assertSame(PASCAL, valueOf("Pa"));
+ assertSame(DECIBEL, valueOf("dB"));
}
/**