Java Code Examples for javax.measure.UnitConverter#convert()

The following examples show how to use javax.measure.UnitConverter#convert() . You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example 1
Source File: Units.java    From sis with Apache License 2.0 6 votes vote down vote up
/**
 * Returns the coefficients of the given converter expressed as a polynomial equation.
 * This method returns the first of the following choices that apply:
 *
 * <ul>
 *   <li>If the given converter {@linkplain UnitConverter#isIdentity() is identity}, returns an empty array.</li>
 *   <li>If the given converter shifts the values without scaling them (for example the conversion from Kelvin to
 *       Celsius degrees), returns an array of length 1 containing only the offset.</li>
 *   <li>If the given converter scales the values (optionally in addition to shifting them), returns an array of
 *       length 2 containing the offset and scale factor, in that order.</li>
 * </ul>
 *
 * This method returns {@code null} if it can not get the polynomial equation coefficients from the given converter.
 *
 * @param  converter  the converter from which to get the coefficients of the polynomial equation, or {@code null}.
 * @return the polynomial equation coefficients (may be any length, including zero), or {@code null} if the given
 *         converter is {@code null} or if this method can not get the coefficients.
 *
 * @since 0.8
 */
@SuppressWarnings("fallthrough")
public static Number[] coefficients(final UnitConverter converter) {
    if (converter != null) {
        if (converter instanceof AbstractConverter) {
            return ((AbstractConverter) converter).coefficients();
        }
        if (converter.isIdentity()) {
            return new Number[0];
        }
        if (converter.isLinear()) {
            final double offset = converter.convert(0);  // Should be zero as per JSR-363 specification, but we are paranoiac.
            final double scale  = converter.convert(1) - offset;
            final Number[] c = new Number[(scale != 1) ? 2 : (offset != 0) ? 1 : 0];
            switch (c.length) {
                case 2: c[1] = scale;       // Fall through
                case 1: c[0] = offset;
                case 0: break;
            }
            return c;
        }
    }
    return null;
}
 
Example 2
Source File: UnitsTest.java    From uom-systems with BSD 3-Clause "New" or "Revised" License 5 votes vote down vote up
/**
 * Test method for {@link javax.measure.Unit#getConverterTo}.
 */
@Test
public void testConverterToSI() {
	Double factor = 10.0;
	UnitConverter converter = one.getConverterTo(one);
	Double result = converter.convert(factor.doubleValue());
	assertEquals(result, factor);
	logger.log(Level.FINER, result.toString());
}
 
Example 3
Source File: Axis.java    From sis with Apache License 2.0 5 votes vote down vote up
/**
 * Returns {@code true} if coordinates in this axis seem to map cell corner instead than cell center.
 * A {@code false} value does not necessarily means that the axis maps cell center; it can be unknown.
 * This method assumes a geographic CRS.
 *
 * <p>From CF-Convention: <cite>"If bounds are not provided, an application might reasonably assume the
 * grid points to be at the centers of the cells, but we do not require that in this standard."</cite>
 * We nevertheless tries to guess by checking if the "cell center" convention would result in coordinates
 * outside the range of longitude or latitude values.</p>
 */
final boolean isCellCorner() throws IOException, DataStoreException {
    double min;
    boolean wraparound;
    switch (abbreviation) {
        case 'λ': min = Longitude.MIN_VALUE; wraparound = true;  break;
        case 'φ': min =  Latitude.MIN_VALUE; wraparound = false; break;
        default: return false;
    }
    final Vector data = read();
    final int size = data.size();
    if (size != 0) {
        Unit<?> unit = getUnit();
        if (unit == null) {
            unit = Units.DEGREE;
        }
        try {
            final UnitConverter uc = unit.getConverterToAny(Units.DEGREE);
            if (wraparound && uc.convert(data.doubleValue(size - 1)) > Longitude.MAX_VALUE) {
                min = 0;            // Replace [-180 … +180]° longitude range by [0 … 360]°.
            }
            return uc.convert(data.doubleValue(0)) == min;
        } catch (IncommensurableException e) {
            warning(e, Errors.Keys.InconsistentUnitsForCS_1, unit);
        }
    }
    return false;
}
 
Example 4
Source File: MetadataReader.java    From sis with Apache License 2.0 5 votes vote down vote up
/**
 * Fills one dimension of the geographic bounding box or vertical extent.
 * The extent values are written in the given {@code extent} array.
 *
 * @param  dim         the dimension for which to get the extent.
 * @param  targetUnit  the destination unit of the extent.
 * @param  positive    the direction considered positive, or {@code null} if the unit symbol is not expected to contain a direction.
 * @param  extent      where to store the minimum and maximum values.
 * @param  index       index where to store the minimum value in {@code extent}. The maximum value is stored at {@code index+1}.
 * @return {@code true} if a minimum or a maximum value has been found.
 */
private boolean fillExtent(final AttributeNames.Dimension dim, final Unit<?> targetUnit, final AxisDirection positive,
                           final double[] extent, final int index)
{
    double min = numericValue(dim.MINIMUM);
    double max = numericValue(dim.MAXIMUM);
    boolean hasExtent = !Double.isNaN(min) || !Double.isNaN(max);
    if (hasExtent) {
        final String symbol = stringValue(dim.UNITS);
        if (symbol != null) {
            try {
                final UnitConverter c = Units.valueOf(symbol).getConverterToAny(targetUnit);
                min = c.convert(min);
                max = c.convert(max);
            } catch (ParserException | IncommensurableException e) {
                warning(e);
            }
            boolean reverse = false;
            if (positive != null) {
                reverse = AxisDirections.opposite(positive).equals(Axis.direction(symbol));
            } else if (dim.POSITIVE != null) {
                // For now, only the vertical axis have a "positive" attribute.
                reverse = CF.POSITIVE_DOWN.equals(stringValue(dim.POSITIVE));
            }
            if (reverse) {
                final double tmp = min;
                min = -max;
                max = -tmp;
            }
        }
    }
    extent[index  ] = min;
    extent[index+1] = max;
    return hasExtent;
}
 
Example 5
Source File: Verifier.java    From sis with Apache License 2.0 5 votes vote down vote up
/**
 * Converts the information about an "value out of range" error. The range in the error message will be formatted
 * in the unit given by the user, which is not necessarily the same than the unit of the parameter descriptor.
 *
 * @param converter  the conversion from user unit to descriptor unit, or {@code null} if none.
 *        This method uses the inverse of that conversion for converting the given minimum and maximum values.
 */
private void convertRange(UnitConverter converter) {
    if (converter != null && !internal && errorKey == Errors.Keys.ValueOutOfRange_4) {
        converter = converter.inverse();
        Object minimumValue = arguments[1];
        Object maximumValue = arguments[2];
        minimumValue = (minimumValue != null) ? converter.convert(((Number) minimumValue).doubleValue()) : "−∞";
        maximumValue = (maximumValue != null) ? converter.convert(((Number) maximumValue).doubleValue()) :  "∞";
        arguments[1] = minimumValue;
        arguments[2] = maximumValue;
    }
}
 
Example 6
Source File: DerivedScalar.java    From sis with Apache License 2.0 5 votes vote down vote up
/**
 * Creates a new scalar for the given value.
 *
 * @param toSystem  converter from {@code unit} to the system unit.
 */
DerivedScalar(final double value, final Unit<Q> unit, final Unit<Q> systemUnit, final UnitConverter toSystem) {
    super(toSystem.convert(value), systemUnit);
    derivedValue = value;
    derivedUnit  = unit;
    fromSystem   = toSystem.inverse();
}
 
Example 7
Source File: AbstractConverter.java    From sis with Apache License 2.0 5 votes vote down vote up
/**
 * Delegates to {@link #derivative(double)} if the given converter is an Apache SIS implementation,
 * or use a fallback otherwise.
 */
static double derivative(final UnitConverter converter, final double value) {
    if (converter != null) {
        if (converter instanceof AbstractConverter) {
            return ((AbstractConverter) converter).derivative(value);
        } else if (converter.isLinear()) {
            return converter.convert(1) - converter.convert(0);
        }
    }
    return Double.NaN;
}
 
Example 8
Source File: AbstractConverter.java    From sis with Apache License 2.0 5 votes vote down vote up
/**
 * Returns the scale factor of the given converter if the conversion is linear, or NaN otherwise.
 */
static double scale(final UnitConverter converter) {
    if (converter != null && converter.isLinear() && converter.convert(0) == 0) {
        // Above check for converter(0) is a paranoiac check since
        // JSR-363 said that a "linear" converter has no offset.
        return converter.convert(1);
    }
    return Double.NaN;
}
 
Example 9
Source File: LinearConverter.java    From sis with Apache License 2.0 5 votes vote down vote up
/**
 * Raises the given converter to the given power. This method assumes that the given converter
 * {@linkplain #isLinear() is linear} (this is not verified) and takes only the scale factor;
 * the offset (if any) is ignored.
 *
 * <p>It is caller's responsibility to skip this method call when {@code n} = 1.
 * This method does not perform this check because it is usually already done (indirectly) by the caller.</p>
 *
 * @param  converter  the converter to raise to the given power.
 * @param  n          the exponent.
 * @param  root       {@code true} for raising to 1/n instead of n.
 * @return the converter raised to the given power.
 */
static LinearConverter pow(final UnitConverter converter, final int n, final boolean root) {
    double numerator, denominator;
    if (converter instanceof LinearConverter) {
        final LinearConverter lc = (LinearConverter) converter;
        numerator   = lc.scale;
        denominator = lc.divisor;
    } else {
        // Subtraction by convert(0) is a paranoiac safety.
        numerator   = converter.convert(1d) - converter.convert(0d);
        denominator = 1;
    }
    if (root) {
        switch (n) {
            case 1:  break;
            case 2:  numerator   = Math.sqrt(numerator);
                     denominator = Math.sqrt(denominator);
                     break;
            case 3:  numerator   = Math.cbrt(numerator);
                     denominator = Math.cbrt(denominator);
                     break;
            default: final double r = 1.0 / n;
                     numerator   = Math.pow(numerator,   r);
                     denominator = Math.pow(denominator, r);
                     break;
        }
    } else {
        numerator   = (numerator   == 10) ? MathFunctions.pow10(n) : Math.pow(numerator,   n);
        denominator = (denominator == 10) ? MathFunctions.pow10(n) : Math.pow(denominator, n);
    }
    return scale(numerator, denominator);
}
 
Example 10
Source File: MeasurementRange.java    From sis with Apache License 2.0 5 votes vote down vote up
/**
 * Casts this range to the specified type and converts to the specified unit.
 * This method is invoked on the {@code other} instance in expressions like
 * {@code this.operation(other)}.
 *
 * @param  type  the class to cast to. Must be one of {@link Byte}, {@link Short},
 *               {@link Integer}, {@link Long}, {@link Float} or {@link Double}.
 * @param  targetUnit the target unit, or {@code null} for no change.
 * @return the casted range, or {@code this}.
 * @throws IncommensurableException if the given target unit is not compatible with the unit of this range.
 */
@SuppressWarnings("unchecked")
private <N extends Number & Comparable<? super N>> MeasurementRange<N>
        convertAndCast(final Class<N> type, Unit<?> targetUnit) throws IncommensurableException
{
    if (targetUnit == null || targetUnit.equals(unit)) {
        if (elementType == type) {
            return (MeasurementRange<N>) this;
        }
        targetUnit = unit;
    } else if (unit != null) {
        final UnitConverter converter = unit.getConverterToAny(targetUnit);
        if (!converter.isIdentity()) {
            boolean minInc = isMinIncluded;
            boolean maxInc = isMaxIncluded;
            double minimum = converter.convert(getMinDouble());
            double maximum = converter.convert(getMaxDouble());
            if (minimum > maximum) {
                final double  td = minimum; minimum = maximum; maximum = td;
                final boolean tb = minInc;  minInc  = maxInc;  maxInc  = tb;
            }
            if (Numbers.isInteger(type)) {
                minInc &= (minimum == (minimum = Math.floor(minimum)));
                maxInc &= (maximum == (maximum = Math.ceil (maximum)));
            }
            return new MeasurementRange<>(type,
                    Numbers.cast(minimum, type), minInc,
                    Numbers.cast(maximum, type), maxInc, targetUnit);
        }
    }
    return new MeasurementRange<>(type, this, targetUnit);
}
 
Example 11
Source File: WaterTankUnitTest.java    From tutorials with MIT License 5 votes vote down vote up
@Test
public void givenMeters_whenConvertToKilometer_ThenConverted() {
    double distanceInMeters = 50.0;
    UnitConverter metreToKilometre = METRE.getConverterTo(MetricPrefix.KILO(METRE));
    double distanceInKilometers = metreToKilometre.convert(distanceInMeters);
    assertEquals(0.05, distanceInKilometers, 0.00f);
}
 
Example 12
Source File: Normalizer.java    From sis with Apache License 2.0 4 votes vote down vote up
/**
 * Returns a new axis with the same properties (except identifiers) than given axis,
 * but with normalized axis direction and unit of measurement.
 *
 * @param  axis     the axis to normalize.
 * @param  changes  the change to apply on axis direction and units.
 * @return an axis using normalized direction and units, or {@code axis} if there is no change.
 */
static CoordinateSystemAxis normalize(final CoordinateSystemAxis axis, final AxisFilter changes) {
    final Unit<?>       unit      = axis.getUnit();
    final AxisDirection direction = axis.getDirection();
    final Unit<?>       newUnit   = changes.getUnitReplacement(axis, unit);
    final AxisDirection newDir    = changes.getDirectionReplacement(axis, direction);
    /*
     * Reuse some properties (name, remarks, etc.) from the existing axis. If the direction changed,
     * then the axis name may need change too (e.g. "Westing" → "Easting"). The new axis name may be
     * set to "Unnamed", but the caller will hopefully be able to replace the returned instance by
     * an instance from the EPSG database with appropriate name.
     */
    final boolean sameDirection = newDir.equals(direction);
    if (sameDirection && newUnit.equals(unit)) {
        return axis;
    }
    final String abbreviation = axis.getAbbreviation();
    final String newAbbr = sameDirection ? abbreviation :
            AxisDirections.suggestAbbreviation(axis.getName().getCode(), newDir, newUnit);
    final Map<String,Object> properties = new HashMap<>(8);
    if (newAbbr.equals(abbreviation)) {
        properties.putAll(IdentifiedObjects.getProperties(axis, EXCLUDES));
    } else {
        properties.put(NAME_KEY, UNNAMED);
    }
    /*
     * Convert the axis range and build the new axis. The axis range will be converted only if
     * the axis direction is the same or the opposite, otherwise we do not know what should be
     * the new values. In the particular case of opposite axis direction, we need to reverse the
     * sign of minimum and maximum values.
     */
    if (sameDirection || newDir.equals(AxisDirections.opposite(direction))) {
        final UnitConverter c;
        try {
            c = unit.getConverterToAny(newUnit);
        } catch (IncommensurableException e) {
            // Use IllegalStateException because the public API is an AbstractCS member method.
            throw new IllegalStateException(Resources.format(Resources.Keys.IllegalUnitFor_2, "axis", unit), e);
        }
        double minimum = c.convert(axis.getMinimumValue());
        double maximum = c.convert(axis.getMaximumValue());
        if (!sameDirection) {
            final double tmp = minimum;
            minimum = -maximum;
            maximum = -tmp;
        }
        properties.put(DefaultCoordinateSystemAxis.MINIMUM_VALUE_KEY, minimum);
        properties.put(DefaultCoordinateSystemAxis.MAXIMUM_VALUE_KEY, maximum);
        properties.put(DefaultCoordinateSystemAxis.RANGE_MEANING_KEY, axis.getRangeMeaning());
    }
    return new DefaultCoordinateSystemAxis(properties, newAbbr, newDir, newUnit);
}
 
Example 13
Source File: LinearConverter.java    From sis with Apache License 2.0 4 votes vote down vote up
/**
 * 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).  In the
 * following equations, the 1 subscript is for the specified converter and the 2 subscript is for this converter:
 *
 * {@preformat math
 *    t = (x⋅scale₁ + offset₁) ∕ divisor₁
 *    y = (t⋅scale₂ + offset₂) ∕ divisor₂
 * }
 *
 * We rewrite as:
 *
 * {@preformat math
 *    y = (x⋅scale₁⋅scale₂ + offset₁⋅scale₂ + divisor₁⋅offset₂) ∕ (divisor₁⋅divisor₂)
 * }
 */
@Override
public UnitConverter concatenate(final UnitConverter converter) {
    ArgumentChecks.ensureNonNull("converter", converter);
    if (converter.isIdentity()) {
        return this;
    }
    if (isIdentity()) {
        return converter;
    }
    double otherScale, otherOffset, otherDivisor;
    if (converter instanceof LinearConverter) {
        final LinearConverter lc = (LinearConverter) converter;
        otherScale   = lc.scale;
        otherOffset  = lc.offset;
        otherDivisor = lc.divisor;
    } else if (converter.isLinear()) {
        /*
         * Fallback for foreigner implementations. Note that 'otherOffset' should be restricted to zero
         * according JSR-363 definition of 'isLinear()', but let be safe; maybe we are not the only one
         * to have a different interpretation about the meaning of "linear".
         */
        otherOffset  = converter.convert(0.0);
        otherScale   = converter.convert(1.0) - otherOffset;
        otherDivisor = 1;
    } else {
        return new ConcatenatedConverter(converter, this);
    }
    otherScale   *= scale;
    otherOffset   = otherOffset * scale + otherDivisor * offset;
    otherDivisor *= divisor;
    /*
     * Following loop is a little bit similar to simplifying a fraction, but checking only for the
     * powers of 10 since unit conversions are often such values. Algorithm is not very efficient,
     * but the loop should not be executed often.
     */
    if (otherScale != 0 || otherOffset != 0 || otherDivisor != 0) {
        double cf, f = 1;
        do {
            cf = f;
            f *= 10;
        } while (otherScale % f == 0 && otherOffset % f == 0 && otherDivisor % f == 0);
        otherScale   /= cf;
        otherOffset  /= cf;
        otherDivisor /= cf;
    }
    if (otherOffset == 0 && otherScale == otherDivisor) {
        return IdentityConverter.INSTANCE;
    }
    return new LinearConverter(otherScale, otherOffset, otherDivisor);
}
 
Example 14
Source File: DefaultParameterValue.java    From sis with Apache License 2.0 3 votes vote down vote up
/**
 * Returns an ordered sequence of numeric values in the specified unit of measure.
 * This convenience method applies unit conversions on the fly as needed.
 *
 * <p>The default implementation invokes {@link #doubleValueList()} and {@link #getUnit()},
 * then converts the values to the given unit of measurement.</p>
 *
 * @param  unit  the unit of measure for the value to be returned.
 * @return the sequence of values represented by this parameter after conversion to type
 *         {@code double} and conversion to {@code unit}.
 * @throws IllegalArgumentException if the specified unit is invalid for this parameter.
 * @throws InvalidParameterTypeException if the value is not an array of {@code double}s.
 * @throws IllegalStateException if the value is not defined and there is no default value.
 *
 * @see #getUnit()
 * @see #setValue(double[],Unit)
 * @see #doubleValue(Unit)
 * @see Parameters#doubleValueList(ParameterDescriptor)
 */
@Override
public double[] doubleValueList(final Unit<?> unit) throws IllegalArgumentException, IllegalStateException {
    final UnitConverter converter = getConverterTo(unit);
    final double[] values = doubleValueList();
    for (int i=0; i<values.length; i++) {
        values[i] = converter.convert(values[i]);
    }
    return values;
}
 
Example 15
Source File: CoordinateSystems.java    From sis with Apache License 2.0 2 votes vote down vote up
/**
     * Returns the EPSG code of a coordinate system using the units and directions of given axes.
     * This method ignores axis metadata (names, abbreviation, identifiers, remarks, <i>etc.</i>).
     * The axis minimum and maximum values are checked only if the
     * {@linkplain CoordinateSystemAxis#getRangeMeaning() range meaning} is "wraparound".
     * If no suitable coordinate system is known to Apache SIS, then this method returns {@code null}.
     *
     * <p>Current implementation uses a hard-coded list of known coordinate systems;
     * it does not yet scan the EPSG database (this may change in future Apache SIS version).
     * The current list of known coordinate systems is given below.</p>
     *
     * <table>
     *   <caption>Known coordinate systems (CS)</caption>
     *   <tr><th>EPSG</th> <th>CS type</th> <th colspan="3">Axis directions</th> <th>Horizontal unit</th></tr>
     *   <tr><td>6424</td> <td>Ellipsoidal</td> <td>east</td>  <td>north</td> <td></td>   <td>degree</td></tr>
     *   <tr><td>6422</td> <td>Ellipsoidal</td> <td>north</td> <td>east</td>  <td></td>   <td>degree</td></tr>
     *   <tr><td>6425</td> <td>Ellipsoidal</td> <td>east</td>  <td>north</td> <td></td>   <td>grads</td></tr>
     *   <tr><td>6403</td> <td>Ellipsoidal</td> <td>north</td> <td>east</td>  <td></td>   <td>grads</td></tr>
     *   <tr><td>6429</td> <td>Ellipsoidal</td> <td>east</td>  <td>north</td> <td></td>   <td>radian</td></tr>
     *   <tr><td>6428</td> <td>Ellipsoidal</td> <td>north</td> <td>east</td>  <td></td>   <td>radian</td></tr>
     *   <tr><td>6426</td> <td>Ellipsoidal</td> <td>east</td>  <td>north</td> <td>up</td> <td>degree</td></tr>
     *   <tr><td>6423</td> <td>Ellipsoidal</td> <td>north</td> <td>east</td>  <td>up</td> <td>degree</td></tr>
     *   <tr><td>6427</td> <td>Ellipsoidal</td> <td>east</td>  <td>north</td> <td>up</td> <td>grads</td></tr>
     *   <tr><td>6421</td> <td>Ellipsoidal</td> <td>north</td> <td>east</td>  <td>up</td> <td>grads</td></tr>
     *   <tr><td>6431</td> <td>Ellipsoidal</td> <td>east</td>  <td>north</td> <td>up</td> <td>radian</td></tr>
     *   <tr><td>6430</td> <td>Ellipsoidal</td> <td>north</td> <td>east</td>  <td>up</td> <td>radian</td></tr>
     *   <tr><td>4400</td> <td>Cartesian</td>   <td>east</td>  <td>north</td> <td></td>   <td>metre</td></tr>
     *   <tr><td>4500</td> <td>Cartesian</td>   <td>north</td> <td>east</td>  <td></td>   <td>metre</td></tr>
     *   <tr><td>4491</td> <td>Cartesian</td>   <td>west</td>  <td>north</td> <td></td>   <td>metre</td></tr>
     *   <tr><td>4501</td> <td>Cartesian</td>   <td>north</td> <td>west</td>  <td></td>   <td>metre</td></tr>
     *   <tr><td>6503</td> <td>Cartesian</td>   <td>west</td>  <td>south</td> <td></td>   <td>metre</td></tr>
     *   <tr><td>6501</td> <td>Cartesian</td>   <td>south</td> <td>west</td>  <td></td>   <td>metre</td></tr>
     *   <tr><td>1039</td> <td>Cartesian</td>   <td>east</td>  <td>north</td> <td></td>   <td>foot</td></tr>
     *   <tr><td>1029</td> <td>Cartesian</td>   <td>north</td> <td>east</td>  <td></td>   <td>foot</td></tr>
     *   <tr><td>4403</td> <td>Cartesian</td>   <td>east</td>  <td>north</td> <td></td>   <td>Clarke’s foot</td></tr>
     *   <tr><td>4502</td> <td>Cartesian</td>   <td>north</td> <td>east</td>  <td></td>   <td>Clarke’s foot</td></tr>
     *   <tr><td>4497</td> <td>Cartesian</td>   <td>east</td>  <td>north</td> <td></td>   <td>US survey foot</td></tr>
     * </table>
     *
     * @param  type  the type of coordinate system for which an EPSG code is desired, as a GeoAPI interface.
     * @param  axes  axes for which a coordinate system EPSG code is desired.
     * @return EPSG codes for a coordinate system using the given axes (ignoring metadata), or {@code null} if unknown
     *         to this method. Note that a null value does not mean that a more  extensive search in the EPSG database
     *         would not find a matching coordinate system.
     *
     * @see org.apache.sis.referencing.factory.GeodeticAuthorityFactory#createCoordinateSystem(String)
     *
     * @since 1.0
     */
    @SuppressWarnings("fallthrough")
    public static Integer getEpsgCode(final Class<? extends CoordinateSystem> type, final CoordinateSystemAxis... axes) {
        ArgumentChecks.ensureNonNull("type", type);
        ArgumentChecks.ensureNonNull("axes", axes);
forDim: switch (axes.length) {
            case 3: {
                if (!Units.METRE.equals(axes[2].getUnit())) break;      // Restriction in our hard-coded list of codes.
                // Fall through
            }
            case 2: {
                final Unit<?> unit = axes[0].getUnit();
                if (unit != null && unit.equals(axes[1].getUnit())) {
                    final boolean isAngular = Units.isAngular(unit);
                    if ((isAngular && type.isAssignableFrom(EllipsoidalCS.class)) ||
                         Units.isLinear(unit) && type.isAssignableFrom(CartesianCS.class))
                    {
                        /*
                         * Current implementation defines EPSG codes for EllipsoidalCS and CartesianCS only.
                         * Those two coordinate system types can be differentiated by the unit of the two first axes.
                         * If a future implementation supports more CS types, above condition will need to be updated.
                         */
                        final AxisDirection[] directions = new AxisDirection[axes.length];
                        for (int i=0; i<directions.length; i++) {
                            final CoordinateSystemAxis axis = axes[i];
                            ArgumentChecks.ensureNonNullElement("axes", i, axis);
                            directions[i] = axis.getDirection();
                            if (isAngular && RangeMeaning.WRAPAROUND.equals(axis.getRangeMeaning())) try {
                                final UnitConverter uc = unit.getConverterToAny(Units.DEGREE);
                                final double min = uc.convert(axis.getMinimumValue());
                                final double max = uc.convert(axis.getMaximumValue());
                                if ((min > Double.NEGATIVE_INFINITY && Math.abs(min - Longitude.MIN_VALUE) > Formulas.ANGULAR_TOLERANCE) ||
                                    (max < Double.POSITIVE_INFINITY && Math.abs(max - Longitude.MAX_VALUE) > Formulas.ANGULAR_TOLERANCE))
                                {
                                    break forDim;
                                }
                            } catch (IncommensurableException e) {      // Should never happen since we checked that units are angular.
                                Logging.unexpectedException(Logging.getLogger(Modules.REFERENCING), CoordinateSystems.class, "getEpsgCode", e);
                                break forDim;
                            }
                        }
                        return getEpsgCode(unit, directions);
                    }
                }
            }
        }
        return null;
    }