Java Code Examples for javax.measure.Unit#equals()

The following examples show how to use javax.measure.Unit#equals() . 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: QuantityType.java    From openhab-core with Eclipse Public License 2.0 6 votes vote down vote up
/**
 * Convert this QuantityType to a new {@link QuantityType} using the given target unit.
 *
 * @param targetUnit the unit to which this {@link QuantityType} will be converted to.
 * @return the new {@link QuantityType} in the given {@link Unit} or {@code null} in case of a
 */
@SuppressWarnings("unchecked")
public @Nullable QuantityType<T> toUnit(Unit<?> targetUnit) {
    if (!targetUnit.equals(getUnit())) {
        try {
            UnitConverter uc = getUnit().getConverterToAny(targetUnit);
            Quantity<?> result = Quantities.getQuantity(uc.convert(quantity.getValue()), targetUnit);

            return new QuantityType<T>(result.getValue(), (Unit<T>) targetUnit);
        } catch (UnconvertibleException | IncommensurableException e) {
            logger.debug("Unable to convert unit from {} to {}", getUnit(), targetUnit);
            return null;
        }
    }
    return this;
}
 
Example 2
Source File: UCUMFormat.java    From uom-systems with BSD 3-Clause "New" or "Revised" License 6 votes vote down vote up
@SuppressWarnings({ "unchecked", "rawtypes" })
private CharSequence symbolForKilogram(ComparableUnit unit) throws IOException {
    
    final Unit<?> systemUnit = unit.getSystemUnit();
    if (!systemUnit.equals(SI.KILOGRAM)) {
        return null;
    }

    final UnitConverter converter = 
            UCUMFormatHelper.toKnownPrefixConverterIfPossible(
                    unit.getConverterTo(systemUnit)
                    .concatenate(MultiplyConverter.ofPrefix(MetricPrefix.KILO)));
    
    final StringBuilder sb = new StringBuilder();
    final boolean printSeparator = true;
    
    // A special case because KILOGRAM is a BaseUnit instead of
    // a transformed unit, for compatibility with existing SI
    // unit system.
    format(SI.GRAM, sb);
    formatConverter(converter, printSeparator, sb, symbolMap);    
    
    return sb;
}
 
Example 3
Source File: UCUMFormat.java    From uom-systems with BSD 3-Clause "New" or "Revised" License 6 votes vote down vote up
@SuppressWarnings({ "unchecked", "rawtypes" })
private CharSequence symbolForNonSystemUnit(ComparableUnit unit) throws IOException {
    
    if (unit.isSystemUnit()) {
        return null;
    }
    
    final Unit<?> parentUnit = unit.getSystemUnit();
    final UnitConverter converter = unit.getConverterTo(parentUnit);
    final StringBuilder sb = new StringBuilder();
    final boolean printSeparator = !parentUnit.equals(ONE);
    
    format(parentUnit, sb);
    formatConverter(converter, printSeparator, sb, symbolMap);
    
    return sb;
}
 
Example 4
Source File: UCUMFormat.java    From uom-systems with BSD 3-Clause "New" or "Revised" License 6 votes vote down vote up
@SuppressWarnings({ "unchecked", "rawtypes" })
private CharSequence symbolForTransformedUnit(ComparableUnit unit) throws IOException {
    if (!(unit instanceof TransformedUnit)) {
        return null;    
    }
    final StringBuilder sb = new StringBuilder();
    final Unit<?> parentUnit = ((TransformedUnit) unit).getParentUnit();
    final UnitConverter converter = 
            UCUMFormatHelper.toKnownPrefixConverterIfPossible(unit.getConverterTo(parentUnit));
    final boolean printSeparator = !parentUnit.equals(ONE);
    if (printSeparator && converter instanceof MultiplyConverter) { // workaround for #166
    	format(parentUnit, sb);
    }
    formatConverter(converter, printSeparator, sb, symbolMap);

    return sb;
}
 
Example 5
Source File: UCUMFormat.java    From uom-systems with BSD 3-Clause "New" or "Revised" License 6 votes vote down vote up
@SuppressWarnings({ "unchecked", "rawtypes" })
private CharSequence symbolForKilogram(ComparableUnit unit) throws IOException {
    
    final Unit<?> systemUnit = unit.getSystemUnit();
    if (!systemUnit.equals(SI.KILOGRAM)) {
        return null;
    }

    final UnitConverter converter = 
            UCUMFormatHelper.toKnownPrefixConverterIfPossible(
                    unit.getConverterTo(systemUnit)
                    .concatenate(MultiplyConverter.ofPrefix(MetricPrefix.KILO)));
    
    final StringBuilder sb = new StringBuilder();
    final boolean printSeparator = true;
    
    // A special case because KILOGRAM is a BaseUnit instead of
    // a transformed unit, for compatibility with existing SI
    // unit system.
    format(SI.GRAM, sb);
    formatConverter(converter, printSeparator, sb, symbolMap);    
    
    return sb;
}
 
Example 6
Source File: QuantityType.java    From smarthome with Eclipse Public License 2.0 6 votes vote down vote up
/**
 * Convert this QuantityType to a new {@link QuantityType} using the given target unit.
 *
 * @param targetUnit the unit to which this {@link QuantityType} will be converted to.
 * @return the new {@link QuantityType} in the given {@link Unit} or {@code null} in case of a
 */
@SuppressWarnings("unchecked")
public @Nullable QuantityType<T> toUnit(Unit<?> targetUnit) {
    if (!targetUnit.equals(getUnit())) {
        try {
            UnitConverter uc = getUnit().getConverterToAny(targetUnit);
            Quantity<?> result = Quantities.getQuantity(uc.convert(quantity.getValue()), targetUnit);

            return new QuantityType<T>(result.getValue(), (Unit<T>) targetUnit);
        } catch (UnconvertibleException | IncommensurableException e) {
            logger.debug("Unable to convert unit from {} to {}", getUnit(), targetUnit);
            return null;
        }
    }
    return this;
}
 
Example 7
Source File: SampleDimension.java    From sis with Apache License 2.0 6 votes vote down vote up
/**
 * Returns the units of measurement for this sample dimension.
 * This unit applies to values obtained after the {@linkplain #getTransferFunction() transfer function}.
 * May be absent if not applicable.
 *
 * @return the units of measurement.
 * @throws IllegalStateException if this sample dimension use different units.
 *
 * @see #getMeasurementRange()
 */
public Optional<Unit<?>> getUnits() {
    Unit<?> unit = null;
    for (final Category category : converted().categories) {
        final NumberRange<?> r = category.range;
        if (r instanceof MeasurementRange<?>) {
            final Unit<?> c = ((MeasurementRange<?>) r).unit();
            if (c != null) {
                if (unit == null) {
                    unit = c;
                } else if (!unit.equals(c)) {
                    throw new IllegalStateException();
                }
            }
        }
    }
    return Optional.ofNullable(unit);
}
 
Example 8
Source File: ReferencingUtilities.java    From sis with Apache License 2.0 6 votes vote down vote up
/**
 * Returns the unit used for all axes in the given coordinate system.
 * If not all axes use the same unit, then this method returns {@code null}.
 *
 * <p>This method is used either when the coordinate system is expected to contain exactly one axis,
 * or for operations that support only one units for all axes, for example Well Know Text version 1
 * (WKT 1) formatting.</p>
 *
 * @param  cs  the coordinate system for which to get the unit, or {@code null}.
 * @return the unit for all axis in the given coordinate system, or {@code null}.
 *
 * @see org.apache.sis.internal.referencing.AxisDirections#getAngularUnit(CoordinateSystem, Unit)
 */
public static Unit<?> getUnit(final CoordinateSystem cs) {
    Unit<?> unit = null;
    if (cs != null) {
        for (int i=cs.getDimension(); --i>=0;) {
            final CoordinateSystemAxis axis = cs.getAxis(i);
            if (axis != null) {                                         // Paranoiac check.
                final Unit<?> candidate = axis.getUnit();
                if (candidate != null) {
                    if (unit == null) {
                        unit = candidate;
                    } else if (!unit.equals(candidate)) {
                        return null;
                    }
                }
            }
        }
    }
    return unit;
}
 
Example 9
Source File: DefaultPrimeMeridian.java    From sis with Apache License 2.0 6 votes vote down vote up
/**
 * Formats this prime meridian as a <cite>Well Known Text</cite> {@code PrimeMeridian[…]} element.
 *
 * @return {@code "PrimeMeridian"} (WKT 2) or {@code "PrimeM"} (WKT 1).
 *
 * @see <a href="http://docs.opengeospatial.org/is/12-063r5/12-063r5.html#53">WKT 2 specification §8.2.2</a>
 */
@Override
protected String formatTo(final Formatter formatter) {
    super.formatTo(formatter);
    final Convention  convention = formatter.getConvention();
    final boolean     isWKT1 = (convention.majorVersion() == 1);
    final Unit<Angle> contextualUnit = formatter.toContextualUnit(Units.DEGREE);
    Unit<Angle> unit = contextualUnit;
    if (!isWKT1) {
        unit = getAngularUnit();
        if (convention != Convention.INTERNAL) {
            unit = WKTUtilities.toFormattable(unit);
        }
    }
    formatter.append(getGreenwichLongitude(unit));
    if (isWKT1) {
        return WKTKeywords.PrimeM;
    }
    if (!convention.isSimplified() || !contextualUnit.equals(unit) || beConservative(formatter, contextualUnit)) {
        formatter.append(unit);
    }
    return formatter.shortOrLong(WKTKeywords.PrimeM, WKTKeywords.PrimeMeridian);
}
 
Example 10
Source File: DefaultVerticalCS.java    From sis with Apache License 2.0 6 votes vote down vote up
/**
 * Returns {@code VALID} if the given argument values are allowed for this coordinate system,
 * or an {@code INVALID_*} error code otherwise. This method is invoked at construction time.
 * The current implementation accepts only temporal directions (i.e. {@link AxisDirection#UP}
 * and {@link AxisDirection#DOWN}).
 */
@Override
final int validateAxis(final AxisDirection direction, Unit<?> unit) {
    if (!AxisDirection.UP.equals(AxisDirections.absolute(direction))) {
        return INVALID_DIRECTION;
    }
    unit = unit.getSystemUnit();
    if (unit.equals(Units.METRE)   ||       // Most usual case.
        unit.equals(Units.PASCAL)  ||       // Height or depth estimated by the atmospheric or ocean pressure.
        unit.equals(Units.SECOND)  ||       // Depth estimated by the time needed for an echo to travel.
        unit.equals(Units.UNITY))           // Sigma-level (percentage from sea surface to ocean floor).
    {
        return VALID;
    }
    return INVALID_UNIT;
}
 
Example 11
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 12
Source File: ItemUIRegistryImpl.java    From openhab-core with Eclipse Public License 2.0 5 votes vote down vote up
private @Nullable QuantityType<?> convertStateToWidgetUnit(QuantityType<?> quantityState, Widget w) {
    Unit<?> widgetUnit = UnitUtils.parseUnit(getFormatPattern(w.getLabel()));
    if (widgetUnit != null && !widgetUnit.equals(quantityState.getUnit())) {
        return quantityState.toUnit(widgetUnit);
    }

    return quantityState;
}
 
Example 13
Source File: Measure.java    From sis with Apache License 2.0 5 votes vote down vote up
/**
 * Implementation of {@link #getUOM()} as a static method for use by classes that define their own
 * {@code uom} attribute, instead of letting the {@code uom} attribute on the measurement value.
 * The main example is {@link org.apache.sis.referencing.cs.DefaultCoordinateSystemAxis}.
 *
 * @param  unit        the unit to format.
 * @param  asXPointer  {@code true} if the units shall be formatted as {@code xpointer}.
 * @param  inAxis      {@code true} for a unit used in Coordinate System Axis definition.
 * @return the string representation of the unit of measure.
 */
public static String getUOM(final Unit<?> unit, final boolean asXPointer, final boolean inAxis) {
    if (!asXPointer) {
        final Integer code = Units.getEpsgCode(unit, inAxis);
        if (code != null) {
            return DefinitionURI.PREFIX + ":uom:" + Constants.EPSG + "::" + code;
        }
    }
    if (unit == null || unit.equals(Units.UNITY)) {
        return "";
    }
    final StringBuilder link;
    final Context context = Context.current();
    /*
     * We have not yet found an ISO 19115-3 URL for units of measurement.
     * If we find one, we should use a block like below:
     *
     * if (Context.isFlagSet(context, Context.LEGACY_METADATA)) {
     *     link = ... new URL ...
     * } else {
     *     link = current code
     * }
     */
    link = Context.schema(context, "gmd", Schemas.METADATA_ROOT_LEGACY);
    link.append(Schemas.UOM_PATH).append("#xpointer(//*[@gml:id='");
    try {
        UCUM.format(unit, link);
    } catch (IOException e) {
        throw new UncheckedIOException(e);          // Should never happen since we wrote to a StringBuilder.
    }
    return link.append("'])").toString();
}
 
Example 14
Source File: CRSBuilder.java    From sis with Apache License 2.0 5 votes vote down vote up
/**
 * Returns {@code true} if the coordinate system may be one of the predefined CS. A returns value of {@code true}
 * is not a guarantee that the coordinate system in the netCDF file matches the predefined CS; it only tells that
 * this is reasonable chances to be the case based on a brief inspection of the first coordinate system axis.
 * If {@code true}, then {@link #isLongitudeFirst} will have been set to an indication of axis order.
 *
 * @param  expected  the expected unit of measurement of the first axis.
 *
 * @see #epsgCandidateCS(Unit)
 */
final boolean isPredefinedCS(final Unit<?> expected) {
    final Axis axis = getFirstAxis();
    final Unit<?> unit = axis.getUnit();
    if (unit == null || expected.equals(unit)) {
        isLongitudeFirst = AxisDirection.EAST.equals(axis.direction);
        if (isLongitudeFirst || AxisDirection.NORTH.equals(axis.direction)) {
            return true;
        }
    }
    return false;
}
 
Example 15
Source File: ItemUIRegistryImpl.java    From smarthome with Eclipse Public License 2.0 5 votes vote down vote up
private QuantityType<?> convertStateToWidgetUnit(QuantityType<?> quantityState, @NonNull Widget w) {
    Unit<?> widgetUnit = UnitUtils.parseUnit(getFormatPattern(w.getLabel()));
    if (widgetUnit != null && !widgetUnit.equals(quantityState.getUnit())) {
        return quantityState.toUnit(widgetUnit);
    }

    return quantityState;
}
 
Example 16
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 17
Source File: InterpolatedTransform.java    From sis with Apache License 2.0 4 votes vote down vote up
/**
 * Creates a transform for the given interpolation grid.
 * This {@code InterpolatedTransform} class works with coordinate values in <em>units of grid cell</em>
 * For example input coordinates (4,5) is the position of the center of the cell at grid index (4,5).
 * The output units are the same than the input units.
 *
 * <p>For converting geodetic coordinates, {@code InterpolatedTransform} instances need to be concatenated
 * with the following affine transforms:
 *
 * <ul>
 *   <li><cite>Normalization</cite> before {@code InterpolatedTransform}
 *     for converting the geodetic coordinates into grid coordinates.</li>
 *   <li><cite>Denormalization</cite> after {@code InterpolatedTransform}
 *     for converting grid coordinates into geodetic coordinates.</li>
 * </ul>
 *
 * After {@code InterpolatedTransform} construction,
 * the full conversion chain including the above affine transforms can be created by
 * <code>{@linkplain #getContextualParameters()}.{@linkplain ContextualParameters#completeTransform
 * completeTransform}(factory, this)}</code>.
 *
 * @param  <T>   dimension of the coordinate tuples and the translation unit.
 * @param  grid  the grid of datum shifts from source to target datum.
 * @throws NoninvertibleMatrixException if the conversion from geodetic coordinates
 *         to grid indices can not be inverted.
 *
 * @see #createGeodeticTransformation(MathTransformFactory, DatumShiftGrid)
 */
@SuppressWarnings( {"OverridableMethodCallDuringObjectConstruction", "fallthrough"})
protected <T extends Quantity<T>> InterpolatedTransform(final DatumShiftGrid<T,T> grid) throws NoninvertibleMatrixException {
    /*
     * Create the contextual parameters using the descriptor of the provider that created the datum shift grid.
     */
    super(grid.getParameterDescriptors(), grid);
    if (!grid.isCellValueRatio()) {
        throw new IllegalArgumentException(Resources.format(
                Resources.Keys.IllegalParameterValue_2, "isCellValueRatio", Boolean.FALSE));
    }
    final Unit<T> unit = grid.getTranslationUnit();
    if (unit != grid.getCoordinateUnit()) {
        throw new IllegalArgumentException(Resources.format(Resources.Keys.IllegalUnitFor_2, "translation", unit));
    }
    dimension = grid.getTranslationDimensions();
    /*
     * Set the normalization matrix to the conversion from source coordinates (e.g. seconds of angle)
     * to grid indices. This will allow us to invoke DatumShiftGrid.interpolateAtCell(x, y, vector)
     * directly in the transform(…) methods.
     */
    final MatrixSIS normalize = context.getMatrix(ContextualParameters.MatrixRole.NORMALIZATION);
    normalize.setMatrix(grid.getCoordinateToGrid().getMatrix());
    /*
     * NADCON and NTv2 datum shift grids expect geographic coordinates in seconds of angle, while
     * MathTransform instances created by DefaultMathTransformFactory.createParameterized(…) must
     * expect coordinates in standardized units (degrees of angle, metres, seconds, etc.).
     * We concatenate the unit conversion with above "coordinates to grid indices" conversion.
     */
    @SuppressWarnings("unchecked")
    final Unit<T> normalized = Units.isAngular(unit) ? (Unit<T>) Units.DEGREE : unit.getSystemUnit();
    if (!unit.equals(normalized)) {
        Number scale  = 1.0;
        Number offset = 0.0;
        final Number[] coefficients = Units.coefficients(normalized.getConverterTo(unit));
        switch (coefficients != null ? coefficients.length : -1) {
            case 2:  scale  = coefficients[1];                                      // Fall through
            case 1:  offset = coefficients[0];                                      // Fall through
            case 0:  break;
            default: throw new IllegalArgumentException(Resources.format(Resources.Keys.NonLinearUnitConversion_2, normalized, unit));
        }
        for (int j=0; j<dimension; j++) {
            normalize.convertBefore(j, scale, offset);
        }
    }
    /*
     * Denormalization is the inverse of all above conversions in the usual case (NADCON and NTv2) where the
     * source coordinate system is the same than the target coordinate system, for example with axis unit in
     * degrees. However we also use this InterpolatedTransform implementation for other operations, like the
     * one created by LocalizationGridBuilder. Those later operations may require a different denormalization
     * matrix. Consequently the call to `getParameterValues(…)` may overwrite the denormalization matrix as
     * a non-documented side effect.
     */
    Matrix denormalize = normalize.inverse();                   // Normal NACDON and NTv2 case.
    context.getMatrix(ContextualParameters.MatrixRole.DENORMALIZATION).setMatrix(denormalize);
    grid.getParameterValues(context);       // May overwrite `denormalize` (see above comment).
    inverse = createInverse();
}
 
Example 18
Source File: Formatter.java    From sis with Apache License 2.0 4 votes vote down vote up
/**
 * Appends a unit in a {@code Unit[…]} element or one of the specialized elements. Specialized elements are
 * {@code AngleUnit}, {@code LengthUnit}, {@code ScaleUnit}, {@code ParametricUnit} and {@code TimeUnit}.
 * By {@linkplain KeywordStyle#DEFAULT default}, specialized unit keywords are used with the
 * {@linkplain Convention#WKT2 WKT 2 convention}.
 *
 * <div class="note"><b>Example:</b>
 * {@code append(Units.KILOMETRE)} will append "{@code LengthUnit["km", 1000]}" to the WKT.</div>
 *
 * @param  unit  the unit to append to the WKT, or {@code null} if none.
 *
 * @see <a href="http://docs.opengeospatial.org/is/12-063r5/12-063r5.html#35">WKT 2 specification §7.4</a>
 */
public void append(final Unit<?> unit) {
    if (unit != null) {
        final boolean isSimplified = (longKeywords == 0) ? convention.isSimplified() : (longKeywords < 0);
        final boolean isWKT1 = convention.majorVersion() == 1;
        final Unit<?> base = unit.getSystemUnit();
        final String keyword;
        if (base.equals(Units.METRE)) {
            keyword = isSimplified ? WKTKeywords.Unit : WKTKeywords.LengthUnit;
        } else if (base.equals(Units.RADIAN)) {
            keyword = isSimplified ? WKTKeywords.Unit : WKTKeywords.AngleUnit;
        } else if (base.equals(Units.UNITY)) {
            keyword = isSimplified ? WKTKeywords.Unit : WKTKeywords.ScaleUnit;
        } else if (base.equals(Units.SECOND)) {
            keyword = WKTKeywords.TimeUnit;  // "Unit" alone is not allowed for time units according ISO 19162.
        } else {
            keyword = WKTKeywords.ParametricUnit;
        }
        openElement(false, keyword);
        setColor(ElementKind.UNIT);
        final int fromIndex = buffer.appendCodePoint(symbols.getOpeningQuote(0)).length();
        unitFormat.format(unit, buffer, dummy);
        closeQuote(fromIndex);
        resetColor();
        final double conversion = Units.toStandardUnit(unit);
        if (Double.isNaN(conversion) && Units.isAngular(unit)) {
            appendExact(Math.PI / 180);                 // Presume that we have sexagesimal degrees (see below).
        } else {
            appendExact(conversion);
        }
        /*
         * The EPSG code in UNIT elements is generally not recommended. But we make an exception for sexagesimal
         * units (EPSG:9108, 9110 and 9111) because they can not be represented by a simple scale factor in WKT.
         * Those units are identified by a conversion factor set to NaN since the conversion is non-linear.
         */
        if (convention == Convention.INTERNAL || Double.isNaN(conversion)) {
            final Integer code = Units.getEpsgCode(unit, getEnclosingElement(1) instanceof CoordinateSystemAxis);
            if (code != null) {
                openElement(false, isWKT1 ? WKTKeywords.Authority : WKTKeywords.Id);
                append(Constants.EPSG, null);
                if (isWKT1) {
                    append(code.toString(), null);
                } else {
                    append(code);
                }
                closeElement(false);
            }
        }
        closeElement(false);
        /*
         * ISO 19162 requires the conversion factor to be positive.
         * In addition, keywords other than "Unit" are not valid in WKt 1.
         */
        if (!(conversion > 0) || (keyword != WKTKeywords.Unit && isWKT1)) {
            setInvalidWKT(Unit.class, null);
        }
    }
}
 
Example 19
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;
    }
 
Example 20
Source File: DefaultPrimeMeridian.java    From sis with Apache License 2.0 2 votes vote down vote up
/**
 * Returns {@code true} if {@link #formatTo(Formatter)} should conservatively format the angular unit
 * even if it would be legal to omit it.
 *
 * <h4>Rational</h4>
 * According the ISO 19162 standard, it is legal to omit the {@code PrimeMeridian} angular unit when
 * that unit is the same than the unit of the axes of the enclosing {@code GeographicCRS}. However the
 * relationship between the CRS axes and the prime meridian is less obvious in WKT2 than it was in WKT1,
 * because the WKT2 {@code UNIT[…]} element is far from the {@code PRIMEM[…]} element while it was just
 * below it in WKT1.   Furthermore, the {@code PRIMEM[…]} unit is one source of incompatibility between
 * various WKT1 parsers (i.e. some popular libraries are not conform to OGC 01-009 and ISO 19162).
 * So we are safer to unconditionally format any unit other than degrees, even if we could legally
 * omit them.
 *
 * <p>However in order to keep the WKT slightly simpler in {@link Convention#WKT2_SIMPLIFIED} mode,
 * we make an exception to the above-cited safety if the {@code UNIT[…]} element is formatted right
 * below the {@code PRIMEM[…]} one, which happen if we are inside a base CRS.
 * See {@link #isElementOfBaseCRS(Formatter)} for more discussion.
 */
private static boolean beConservative(final Formatter formatter, final Unit<Angle> contextualUnit) {
    return !contextualUnit.equals(Units.DEGREE) && !isElementOfBaseCRS(formatter);
}