Search in sources :

Example 1 with VerticalDatumType

use of org.opengis.referencing.datum.VerticalDatumType in project sis by apache.

the class VerticalDatumTypes method guess.

/**
 * Guesses the type of a datum from its name, aliases or a given vertical axis. This is sometime needed
 * after XML unmarshalling or WKT parsing, since GML 3.2 and ISO 19162 do not contain any attribute for
 * the datum type.
 *
 * <p>This method uses heuristic rules and may be changed in any future SIS version.
 * If the type can not be determined, defaults to {@link VerticalDatumType#OTHER_SURFACE}.</p>
 *
 * @param  name     the name of the datum for which to guess a type, or {@code null} if unknown.
 * @param  aliases  the aliases of the datum for which to guess a type, or {@code null} if unknown.
 * @param  axis     the vertical axis for which to guess a type, or {@code null} if unknown.
 * @return a datum type, or {@link VerticalDatumType#OTHER_SURFACE} if none can be guessed.
 */
public static VerticalDatumType guess(final String name, final Collection<? extends GenericName> aliases, final CoordinateSystemAxis axis) {
    VerticalDatumType type = guess(name);
    if (type != null) {
        return type;
    }
    if (aliases != null) {
        for (final GenericName alias : aliases) {
            type = guess(alias.tip().toString());
            if (type != null) {
                return type;
            }
        }
    }
    if (axis != null) {
        final Unit<?> unit = axis.getUnit();
        if (Units.isLinear(unit)) {
            final String abbreviation = axis.getAbbreviation();
            if (abbreviation.length() == 1) {
                // Expected direction for accepting the type.
                AxisDirection dir = AxisDirection.UP;
                switch(abbreviation.charAt(0)) {
                    case 'h':
                        type = ELLIPSOIDAL;
                        break;
                    case 'H':
                        type = VerticalDatumType.GEOIDAL;
                        break;
                    case 'D':
                        type = VerticalDatumType.DEPTH;
                        dir = AxisDirection.DOWN;
                        break;
                    default:
                        return VerticalDatumType.OTHER_SURFACE;
                }
                if (dir.equals(axis.getDirection())) {
                    return type;
                }
            }
        } else if (Units.isPressure(unit)) {
            return VerticalDatumType.BAROMETRIC;
        }
    }
    return VerticalDatumType.OTHER_SURFACE;
}
Also used : GenericName(org.opengis.util.GenericName) VerticalDatumType(org.opengis.referencing.datum.VerticalDatumType) AxisDirection(org.opengis.referencing.cs.AxisDirection)

Example 2 with VerticalDatumType

use of org.opengis.referencing.datum.VerticalDatumType in project sis by apache.

the class Extents method getVerticalRange.

/**
 * Returns the union of chosen vertical ranges found in the given extent, or {@code null} if none.
 * This method gives preference to heights above the Mean Sea Level when possible.
 * Depths have negative height values: if the
 * {@linkplain org.apache.sis.referencing.cs.DefaultCoordinateSystemAxis#getDirection() axis direction}
 * is toward down, then this method reverses the sign of minimum and maximum values.
 *
 * <div class="section">Multi-occurrences</div>
 * If the given {@code Extent} object contains more than one vertical extent, then this method
 * performs a choice based on the vertical datum and the unit of measurement:
 *
 * <ul class="verbose">
 *   <li><p><b>Choice based on vertical datum</b><br>
 *   Only the extents associated (indirectly, through their CRS) to the same non-null {@link VerticalDatumType}
 *   will be taken in account. If all datum types are null, then this method conservatively uses only the first
 *   vertical extent. Otherwise the datum type used for filtering the vertical extents is:</p>
 *
 *   <ul>
 *     <li>{@link VerticalDatumType#GEOIDAL} or {@link VerticalDatumType#DEPTH DEPTH} if at least one extent
 *         uses those datum types. For this method, {@code DEPTH} is considered as equivalent to {@code GEOIDAL}
 *         except for the axis direction.</li>
 *     <li>Otherwise, the first non-null datum type found in iteration order.</li>
 *   </ul>
 *
 *   <div class="note"><b>Rational:</b> like {@linkplain #getGeographicBoundingBox(Extent) geographic bounding box},
 *   the vertical range is an approximative information; the range returned by this method does not carry any
 *   information about the vertical CRS and this method does not attempt to perform coordinate transformation.
 *   But this method is more useful if the returned ranges are close to a frequently used surface, like the
 *   Mean Sea Level. The same simplification is applied in the
 *   <a href="http://docs.opengeospatial.org/is/12-063r5/12-063r5.html#31">{@code VerticalExtent} element of
 *   Well Known Text (WKT) format</a>, which specifies that <cite>“Vertical extent is an approximate description
 *   of location; heights are relative to an unspecified mean sea level.”</cite></div></li>
 *
 *   <li><p><b>Choice based on units of measurement</b><br>
 *   If, after the choice based on the vertical datum described above, there is still more than one vertical
 *   extent to consider, then the next criterion checks for the units of measurement.</p>
 *   <ul>
 *     <li>If no range specify a unit of measurement, return the first range and ignore all others.</li>
 *     <li>Otherwise take the first range having a unit of measurement. Then:<ul>
 *       <li>All other ranges having an incompatible unit of measurement will be ignored.</li>
 *       <li>All other ranges having a compatible unit of measurement will be converted to
 *           the unit of the first retained range, and their union will be computed.</li>
 *     </ul></li>
 *   </ul>
 *
 *   <div class="note"><b>Example:</b>
 *   Heights or depths are often measured using some pressure units, for example hectopascals (hPa).
 *   An {@code Extent} could contain two vertical elements: one with the height measurements in hPa,
 *   and the other element with heights transformed to metres using an empirical formula.
 *   In such case this method will select the first vertical element on the assumption that it is
 *   the "main" one that the metadata producer intended to show. Next, this method will search for
 *   other vertical elements using pressure unit. In our example there is none, but if such elements
 *   were found, this method would compute their union.</div></li>
 * </ul>
 *
 * @param  extent  the extent to convert to a vertical measurement range, or {@code null}.
 * @return a vertical measurement range created from the given extent, or {@code null} if none.
 *
 * @since 0.4
 */
public static MeasurementRange<Double> getVerticalRange(final Extent extent) {
    MeasurementRange<Double> range = null;
    VerticalDatumType selectedType = null;
    if (extent != null) {
        for (final VerticalExtent element : extent.getVerticalElements()) {
            double min = element.getMinimumValue();
            double max = element.getMaximumValue();
            final VerticalCRS crs = element.getVerticalCRS();
            VerticalDatumType type = null;
            Unit<?> unit = null;
            if (crs != null) {
                final VerticalDatum datum = crs.getDatum();
                if (datum != null) {
                    type = datum.getVerticalDatumType();
                    if (VerticalDatumType.DEPTH.equals(type)) {
                        type = VerticalDatumType.GEOIDAL;
                    }
                }
                final CoordinateSystemAxis axis = crs.getCoordinateSystem().getAxis(0);
                unit = axis.getUnit();
                if (AxisDirection.DOWN.equals(axis.getDirection())) {
                    final double tmp = min;
                    min = -max;
                    max = -tmp;
                }
            }
            if (range != null) {
                /*
                     * If the new range does not specify any datum type or unit, then we do not know how to
                     * convert the values before to perform the union operation. Conservatively do nothing.
                     */
                if (type == null || unit == null) {
                    continue;
                }
                /*
                     * If the new range is not measured relative to the same kind of surface than the previous range,
                     * then we do not know how to combine those ranges. Do nothing, unless the new range is a Mean Sea
                     * Level Height in which case we forget all previous ranges and use the new one instead.
                     */
                if (!type.equals(selectedType)) {
                    if (!type.equals(VerticalDatumType.GEOIDAL)) {
                        continue;
                    }
                } else if (selectedType != null) {
                    /*
                         * If previous range did not specify any unit, then unconditionally replace it by
                         * the new range since it provides more information. If both ranges specify units,
                         * then we will compute the union if we can, or ignore the new range otherwise.
                         */
                    final Unit<?> previous = range.unit();
                    if (previous != null) {
                        if (previous.isCompatible(unit)) {
                            range = (MeasurementRange<Double>) range.union(MeasurementRange.create(min, true, max, true, unit));
                        }
                        continue;
                    }
                }
            }
            range = MeasurementRange.create(min, true, max, true, unit);
            selectedType = type;
        }
    }
    return range;
}
Also used : MeasurementRange(org.apache.sis.measure.MeasurementRange) VerticalDatumType(org.opengis.referencing.datum.VerticalDatumType) VerticalExtent(org.opengis.metadata.extent.VerticalExtent) VerticalCRS(org.opengis.referencing.crs.VerticalCRS) CoordinateSystemAxis(org.opengis.referencing.cs.CoordinateSystemAxis) VerticalDatum(org.opengis.referencing.datum.VerticalDatum) Unit(javax.measure.Unit)

Example 3 with VerticalDatumType

use of org.opengis.referencing.datum.VerticalDatumType in project sis by apache.

the class DefaultVerticalDatum method type.

/**
 * Returns the type of this datum, or infers the type from the datum name if no type were specified.
 * The later case occurs after unmarshalling, since GML 3.2 does not contain any attribute for the datum type.
 * It may also happen if the datum were created using reflection.
 *
 * <p>This method uses heuristic rules and may be changed in any future SIS version. If the type can not be
 * determined, default on the ellipsoidal type since it will usually implies no additional calculation.</p>
 *
 * <p>No synchronization needed; this is not a problem if this value is computed twice.
 * This method returns only existing immutable instances.</p>
 *
 * @see #getVerticalDatumType()
 * @see #getTypeElement()
 */
private VerticalDatumType type() {
    VerticalDatumType t = type;
    if (t == null) {
        final ReferenceIdentifier name = super.getName();
        type = t = VerticalDatumTypes.guess(name != null ? name.getCode() : null, super.getAlias(), null);
    }
    return t;
}
Also used : ReferenceIdentifier(org.opengis.referencing.ReferenceIdentifier) VerticalDatumType(org.opengis.referencing.datum.VerticalDatumType)

Example 4 with VerticalDatumType

use of org.opengis.referencing.datum.VerticalDatumType in project sis by apache.

the class CommonCRSTest method testVertical.

/**
 * Verifies the vertical datum enumeration.
 */
@Test
public void testVertical() {
    for (final CommonCRS.Vertical e : CommonCRS.Vertical.values()) {
        final VerticalDatumType datumType;
        final String axisName, datumName;
        switch(e) {
            case NAVD88:
                axisName = AxisNames.GRAVITY_RELATED_HEIGHT;
                datumName = "North American Vertical Datum 1988";
                datumType = VerticalDatumType.GEOIDAL;
                break;
            case BAROMETRIC:
                axisName = "Barometric altitude";
                datumName = "Constant pressure surface";
                datumType = VerticalDatumType.BAROMETRIC;
                break;
            case MEAN_SEA_LEVEL:
                axisName = AxisNames.GRAVITY_RELATED_HEIGHT;
                datumName = "Mean Sea Level";
                datumType = VerticalDatumType.GEOIDAL;
                break;
            case DEPTH:
                axisName = AxisNames.DEPTH;
                datumName = "Mean Sea Level";
                datumType = VerticalDatumType.GEOIDAL;
                break;
            case ELLIPSOIDAL:
                axisName = AxisNames.ELLIPSOIDAL_HEIGHT;
                datumName = "Ellipsoid";
                datumType = VerticalDatumTypes.ELLIPSOIDAL;
                break;
            case OTHER_SURFACE:
                axisName = "Height";
                datumName = "Other surface";
                datumType = VerticalDatumType.OTHER_SURFACE;
                break;
            default:
                throw new AssertionError(e);
        }
        final String name = e.name();
        final VerticalDatum datum = e.datum();
        final VerticalCRS crs = e.crs();
        if (e.isEPSG) {
            /*
                 * BAROMETRIC, ELLIPSOIDAL and OTHER_SURFACE uses an axis named "Height", which is not
                 * a valid axis name according ISO 19111. We skip the validation test for those enums.
                 */
            Validators.validate(crs);
        }
        // Datum before CRS creation.
        assertSame(name, datum, e.datum());
        // Datum after CRS creation.
        assertSame(name, crs.getDatum(), e.datum());
        assertEquals(name, datumName, datum.getName().getCode());
        assertEquals(name, datumType, datum.getVerticalDatumType());
        assertEquals(name, axisName, crs.getCoordinateSystem().getAxis(0).getName().getCode());
    }
}
Also used : VerticalDatumType(org.opengis.referencing.datum.VerticalDatumType) VerticalCRS(org.opengis.referencing.crs.VerticalCRS) VerticalDatum(org.opengis.referencing.datum.VerticalDatum) Test(org.junit.Test)

Aggregations

VerticalDatumType (org.opengis.referencing.datum.VerticalDatumType)4 VerticalCRS (org.opengis.referencing.crs.VerticalCRS)2 VerticalDatum (org.opengis.referencing.datum.VerticalDatum)2 Unit (javax.measure.Unit)1 MeasurementRange (org.apache.sis.measure.MeasurementRange)1 Test (org.junit.Test)1 VerticalExtent (org.opengis.metadata.extent.VerticalExtent)1 ReferenceIdentifier (org.opengis.referencing.ReferenceIdentifier)1 AxisDirection (org.opengis.referencing.cs.AxisDirection)1 CoordinateSystemAxis (org.opengis.referencing.cs.CoordinateSystemAxis)1 GenericName (org.opengis.util.GenericName)1