Search in sources :

Example 31 with CoordinateSystemAxis

use of org.opengis.referencing.cs.CoordinateSystemAxis in project sis by apache.

the class Envelopes method transform.

/**
 * Transforms an envelope using the given coordinate operation.
 * The transformation is only approximative: the returned envelope may be bigger than the
 * smallest possible bounding box, but should not be smaller in most cases.
 *
 * <p>This method can handle the case where the envelope contains the North or South pole,
 * or when it cross the ±180° longitude.</p>
 *
 * <div class="note"><b>Note:</b>
 * If the envelope CRS is non-null, then the caller should ensure that the operation source CRS
 * is the same than the envelope CRS. In case of mismatch, this method transforms the envelope
 * to the operation source CRS before to apply the operation. This extra step may cause a lost
 * of accuracy. In order to prevent this method from performing such pre-transformation (if not desired),
 * callers can ensure that the envelope CRS is {@code null} before to call this method.</div>
 *
 * @param  operation  the operation to use.
 * @param  envelope   envelope to transform, or {@code null}. This envelope will not be modified.
 * @return the transformed envelope, or {@code null} if {@code envelope} was null.
 * @throws TransformException if a transform failed.
 *
 * @see #transform(MathTransform, Envelope)
 *
 * @since 0.5
 */
@SuppressWarnings("null")
public static GeneralEnvelope transform(final CoordinateOperation operation, Envelope envelope) throws TransformException {
    ensureNonNull("operation", operation);
    if (envelope == null) {
        return null;
    }
    boolean isOperationComplete = true;
    final CoordinateReferenceSystem sourceCRS = operation.getSourceCRS();
    if (sourceCRS != null) {
        final CoordinateReferenceSystem crs = envelope.getCoordinateReferenceSystem();
        if (crs != null && !Utilities.equalsIgnoreMetadata(crs, sourceCRS)) {
            /*
                 * Argument-check: the envelope CRS seems inconsistent with the given operation.
                 * However we need to push the check a little bit further, since 3D-GeographicCRS
                 * are considered not equal to CompoundCRS[2D-GeographicCRS + ellipsoidal height].
                 * Checking for identity MathTransform is a more powerfull (but more costly) check.
                 * Since we have the MathTransform, perform an opportunist envelope transform if it
                 * happen to be required.
                 */
            final MathTransform mt;
            try {
                mt = CoordinateOperations.factory().createOperation(crs, sourceCRS).getMathTransform();
            } catch (FactoryException e) {
                throw new TransformException(Errors.format(Errors.Keys.CanNotTransformEnvelope), e);
            }
            if (!mt.isIdentity()) {
                isOperationComplete = false;
                envelope = transform(mt, envelope);
            }
        }
    }
    MathTransform mt = operation.getMathTransform();
    final double[] centerPt = new double[mt.getTargetDimensions()];
    final GeneralEnvelope transformed = transform(mt, envelope, centerPt);
    /*
         * If the source envelope crosses the expected range of valid coordinates, also projects
         * the range bounds as a safety. Example: if the source envelope goes from 150 to 200°E,
         * some map projections will interpret 200° as if it was -160°, and consequently produce
         * an envelope which do not include the 180°W extremum. We will add those extremum points
         * explicitly as a safety. It may leads to bigger than necessary target envelope, but the
         * contract is to include at least the source envelope, not to return the smallest one.
         */
    if (sourceCRS != null) {
        final CoordinateSystem cs = sourceCRS.getCoordinateSystem();
        if (cs != null) {
            // Should never be null, but check as a paranoiac safety.
            DirectPosition sourcePt = null;
            DirectPosition targetPt = null;
            final int dimension = cs.getDimension();
            for (int i = 0; i < dimension; i++) {
                final CoordinateSystemAxis axis = cs.getAxis(i);
                if (axis == null) {
                    // Should never be null, but check as a paranoiac safety.
                    continue;
                }
                final double min = envelope.getMinimum(i);
                final double max = envelope.getMaximum(i);
                final double v1 = axis.getMinimumValue();
                final double v2 = axis.getMaximumValue();
                final boolean b1 = (v1 > min && v1 < max);
                final boolean b2 = (v2 > min && v2 < max);
                if (!b1 && !b2) {
                    continue;
                }
                if (sourcePt == null) {
                    sourcePt = new GeneralDirectPosition(dimension);
                    for (int j = 0; j < dimension; j++) {
                        sourcePt.setOrdinate(j, envelope.getMedian(j));
                    }
                }
                if (b1) {
                    sourcePt.setOrdinate(i, v1);
                    transformed.add(targetPt = mt.transform(sourcePt, targetPt));
                }
                if (b2) {
                    sourcePt.setOrdinate(i, v2);
                    transformed.add(targetPt = mt.transform(sourcePt, targetPt));
                }
                sourcePt.setOrdinate(i, envelope.getMedian(i));
            }
        }
    }
    /*
         * Now takes the target CRS in account...
         */
    final CoordinateReferenceSystem targetCRS = operation.getTargetCRS();
    if (targetCRS == null) {
        return transformed;
    }
    transformed.setCoordinateReferenceSystem(targetCRS);
    final CoordinateSystem targetCS = targetCRS.getCoordinateSystem();
    if (targetCS == null) {
        // It should be an error, but we keep this method tolerant.
        return transformed;
    }
    /*
         * Checks for singularity points. For example the south pole is a singularity point in
         * geographic CRS because is is located at the maximal value allowed by one particular
         * axis, namely latitude. This point is not a singularity in the stereographic projection,
         * because axes extends toward infinity in all directions (mathematically) and because the
         * South pole has nothing special apart being the origin (0,0).
         *
         * Algorithm:
         *
         * 1) Inspect the target axis, looking if there is any bounds. If bounds are found, get
         *    the coordinates of singularity points and project them from target to source CRS.
         *
         *    Example: If the transformed envelope above is (80 … 85°S, 10 … 50°W), and if the
         *             latitude in the target CRS is bounded at 90°S, then project (90°S, 30°W)
         *             to the source CRS. Note that the longitude is set to the the center of
         *             the envelope longitude range (more on this below).
         *
         * 2) If the singularity point computed above is inside the source envelope, add that
         *    point to the target (transformed) envelope.
         *
         * 3) If step #2 added the point, iterate over all other axes. If an other bounded axis
         *    is found and that axis is of kind "WRAPAROUND", test for inclusion the same point
         *    than the point tested at step #1, except for the ordinate of the axis found in this
         *    step. That ordinate is set to the minimal and maximal values of that axis.
         *
         *    Example: If the above steps found that the point (90°S, 30°W) need to be included,
         *             then this step #3 will also test the points (90°S, 180°W) and (90°S, 180°E).
         *
         * NOTE: we test (-180°, centerY), (180°, centerY), (centerX, -90°) and (centerX, 90°)
         * at step #1 before to test (-180°, -90°), (180°, -90°), (-180°, 90°) and (180°, 90°)
         * at step #3 because the later may not be supported by every projections. For example
         * if the target envelope is located between 20°N and 40°N, then a Mercator projection
         * may fail to transform the (-180°, 90°) coordinate while the (-180°, 30°) coordinate
         * is a valid point.
         */
    TransformException warning = null;
    AbstractEnvelope generalEnvelope = null;
    DirectPosition sourcePt = null;
    DirectPosition targetPt = null;
    // A bitmask for each dimension.
    long includedMinValue = 0;
    long includedMaxValue = 0;
    long isWrapAroundAxis = 0;
    long dimensionBitMask = 1;
    final int dimension = targetCS.getDimension();
    poles: for (int i = 0; i < dimension; i++, dimensionBitMask <<= 1) {
        final CoordinateSystemAxis axis = targetCS.getAxis(i);
        if (axis == null) {
            // Should never be null, but check as a paranoiac safety.
            continue;
        }
        // Tells if we are testing the minimal or maximal value.
        boolean testMax = false;
        do {
            final double extremum = testMax ? axis.getMaximumValue() : axis.getMinimumValue();
            if (Double.isInfinite(extremum) || Double.isNaN(extremum)) {
                /*
                     * The axis is unbounded. It should always be the case when the target CRS is
                     * a map projection, in which case this loop will finish soon and this method
                     * will do nothing more (no object instantiated, no MathTransform inversed...)
                     */
                continue;
            }
            if (targetPt == null) {
                try {
                    mt = mt.inverse();
                } catch (NoninvertibleTransformException exception) {
                    /*
                         * If the transform is non invertible, this method can't do anything. This
                         * is not a fatal error because the envelope has already be transformed by
                         * the caller. We lost the check for singularity points performed by this
                         * method, but it make no difference in the common case where the source
                         * envelope didn't contains any of those points.
                         *
                         * Note that this exception is normal if target dimension is smaller than
                         * source dimension, since the math transform can not reconstituate the
                         * lost dimensions. So we don't log any warning in this case.
                         */
                    if (dimension >= mt.getSourceDimensions()) {
                        warning = exception;
                    }
                    break poles;
                }
                targetPt = new GeneralDirectPosition(mt.getSourceDimensions());
                for (int j = 0; j < dimension; j++) {
                    targetPt.setOrdinate(j, centerPt[j]);
                }
                // TODO: avoid the hack below if we provide a contains(DirectPosition)
                // method in the GeoAPI org.opengis.geometry.Envelope interface.
                generalEnvelope = AbstractEnvelope.castOrCopy(envelope);
            }
            targetPt.setOrdinate(i, extremum);
            try {
                sourcePt = mt.transform(targetPt, sourcePt);
            } catch (TransformException exception) {
                /*
                     * This exception may be normal. For example if may occur when projecting
                     * the latitude extremums with a cylindrical Mercator projection.  Do not
                     * log any message (unless logging level is fine) and try the other points.
                     */
                if (warning == null) {
                    warning = exception;
                } else {
                    warning.addSuppressed(exception);
                }
                continue;
            }
            if (generalEnvelope.contains(sourcePt)) {
                transformed.add(targetPt);
                if (testMax)
                    includedMaxValue |= dimensionBitMask;
                else
                    includedMinValue |= dimensionBitMask;
            }
        } while ((testMax = !testMax) == true);
        /*
             * Keep trace of axes of kind WRAPAROUND, except if the two extremum values of that
             * axis have been included in the envelope  (in which case the next step after this
             * loop doesn't need to be executed for that axis).
             */
        if ((includedMinValue & includedMaxValue & dimensionBitMask) == 0 && CoordinateOperations.isWrapAround(axis)) {
            isWrapAroundAxis |= dimensionBitMask;
        }
        // Restore 'targetPt' to its initial state, which is equals to 'centerPt'.
        if (targetPt != null) {
            targetPt.setOrdinate(i, centerPt[i]);
        }
    }
    /*
         * Step #3 described in the above "Algorithm" section: iterate over all dimensions
         * of type "WRAPAROUND" for which minimal or maximal axis values have not yet been
         * included in the envelope. The set of axes is specified by a bitmask computed in
         * the above loop.  We examine only the points that have not already been included
         * in the envelope.
         */
    final long includedBoundsValue = (includedMinValue | includedMaxValue);
    if (includedBoundsValue != 0) {
        while (isWrapAroundAxis != 0) {
            final int wrapAroundDimension = Long.numberOfTrailingZeros(isWrapAroundAxis);
            dimensionBitMask = 1 << wrapAroundDimension;
            // Clear now the bit, for the next iteration.
            isWrapAroundAxis &= ~dimensionBitMask;
            final CoordinateSystemAxis wrapAroundAxis = targetCS.getAxis(wrapAroundDimension);
            final double min = wrapAroundAxis.getMinimumValue();
            final double max = wrapAroundAxis.getMaximumValue();
            /*
                 * Iterate over all axes for which a singularity point has been previously found,
                 * excluding the "wrap around axis" currently under consideration.
                 */
            for (long am = (includedBoundsValue & ~dimensionBitMask), bm; am != 0; am &= ~bm) {
                bm = Long.lowestOneBit(am);
                final int axisIndex = Long.numberOfTrailingZeros(bm);
                final CoordinateSystemAxis axis = targetCS.getAxis(axisIndex);
                /*
                     * switch (c) {
                     *   case 0: targetPt = (..., singularityMin, ..., wrapAroundMin, ...)
                     *   case 1: targetPt = (..., singularityMin, ..., wrapAroundMax, ...)
                     *   case 2: targetPt = (..., singularityMax, ..., wrapAroundMin, ...)
                     *   case 3: targetPt = (..., singularityMax, ..., wrapAroundMax, ...)
                     * }
                     */
                for (int c = 0; c < 4; c++) {
                    /*
                         * Set the ordinate value along the axis having the singularity point
                         * (cases c=0 and c=2).  If the envelope did not included that point,
                         * then skip completely this case and the next one, i.e. skip c={0,1}
                         * or skip c={2,3}.
                         */
                    double value = max;
                    if ((c & 1) == 0) {
                        // 'true' if we are testing "wrapAroundMin".
                        if (((c == 0 ? includedMinValue : includedMaxValue) & bm) == 0) {
                            // Skip also the case for "wrapAroundMax".
                            c++;
                            continue;
                        }
                        targetPt.setOrdinate(axisIndex, (c == 0) ? axis.getMinimumValue() : axis.getMaximumValue());
                        value = min;
                    }
                    targetPt.setOrdinate(wrapAroundDimension, value);
                    try {
                        sourcePt = mt.transform(targetPt, sourcePt);
                    } catch (TransformException exception) {
                        if (warning == null) {
                            warning = exception;
                        } else {
                            warning.addSuppressed(exception);
                        }
                        continue;
                    }
                    if (generalEnvelope.contains(sourcePt)) {
                        transformed.add(targetPt);
                    }
                }
                targetPt.setOrdinate(axisIndex, centerPt[axisIndex]);
            }
            targetPt.setOrdinate(wrapAroundDimension, centerPt[wrapAroundDimension]);
        }
    }
    /*
         * At this point we finished envelope transformation. Verify if some ordinates need to be "wrapped around"
         * as a result of the coordinate operation.  This is usually the longitude axis where the source CRS uses
         * the [-180 … +180]° range and the target CRS uses the [0 … 360]° range, or the converse. We do not wrap
         * around if the source and target axes use the same range (e.g. the longitude stay [-180 … +180]°) in order
         * to reduce the risk of discontinuities. If the user really wants unconditional wrap around, (s)he can call
         * GeneralEnvelope.normalize().
         */
    final Set<Integer> wrapAroundChanges;
    if (isOperationComplete && operation instanceof AbstractCoordinateOperation) {
        wrapAroundChanges = ((AbstractCoordinateOperation) operation).getWrapAroundChanges();
    } else {
        wrapAroundChanges = CoordinateOperations.wrapAroundChanges(sourceCRS, targetCS);
    }
    transformed.normalize(targetCS, 0, wrapAroundChanges.size(), wrapAroundChanges.iterator());
    if (warning != null) {
        recoverableException(Envelopes.class, warning);
    }
    return transformed;
}
Also used : DirectPosition(org.opengis.geometry.DirectPosition) AbstractMathTransform(org.apache.sis.referencing.operation.transform.AbstractMathTransform) MathTransform(org.opengis.referencing.operation.MathTransform) FactoryException(org.opengis.util.FactoryException) CoordinateSystem(org.opengis.referencing.cs.CoordinateSystem) NoninvertibleTransformException(org.opengis.referencing.operation.NoninvertibleTransformException) TransformException(org.opengis.referencing.operation.TransformException) CoordinateSystemAxis(org.opengis.referencing.cs.CoordinateSystemAxis) NoninvertibleTransformException(org.opengis.referencing.operation.NoninvertibleTransformException) CoordinateReferenceSystem(org.opengis.referencing.crs.CoordinateReferenceSystem) AbstractCoordinateOperation(org.apache.sis.referencing.operation.AbstractCoordinateOperation)

Example 32 with CoordinateSystemAxis

use of org.opengis.referencing.cs.CoordinateSystemAxis in project sis by apache.

the class Formatter method append.

/**
 * 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)) {
            // "Unit" alone is not allowed for time units according ISO 19162.
            keyword = WKTKeywords.TimeUnit;
        } 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)) {
            // Presume that we have sexagesimal degrees (see below).
            appendExact(Math.PI / 180);
        } 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);
        }
    }
}
Also used : CoordinateSystemAxis(org.opengis.referencing.cs.CoordinateSystemAxis) InternationalString(org.opengis.util.InternationalString)

Example 33 with CoordinateSystemAxis

use of org.opengis.referencing.cs.CoordinateSystemAxis in project sis by apache.

the class VerticalInfo method complete.

/**
 * Completes the extent with a new CRS using the units specified at construction time.
 * The CRS created by this method is implementation-dependent. The only guarantees are:
 *
 * <ul>
 *   <li>datum type is {@link VerticalDatumType#GEOIDAL},</li>
 *   <li>increasing height values are up, and</li>
 *   <li>axis unit of measurement is the given linear unit.</li>
 * </ul>
 *
 * If this method can not propose a suitable CRS, then it returns {@code this}.
 */
final VerticalInfo complete(final CRSFactory crsFactory, final CSFactory csFactory) throws FactoryException {
    if (next != null) {
        next = next.complete(crsFactory, csFactory);
    }
    if (compatibleCRS == null) {
        return this;
    }
    final Object name;
    final String abbreviation;
    CoordinateSystemAxis axis = compatibleCRS.getCoordinateSystem().getAxis(0);
    final boolean isUP = AxisDirection.UP.equals(axis.getDirection());
    if (isUP) {
        name = axis.getName();
        abbreviation = axis.getAbbreviation();
    } else {
        name = AxisNames.GRAVITY_RELATED_HEIGHT;
        abbreviation = "H";
    }
    axis = csFactory.createCoordinateSystemAxis(properties(name), abbreviation, AxisDirection.UP, unit);
    /*
         * Naming policy (based on usage of names in the EPSG database):
         *
         *   - We can reuse the old axis name if (and only if) the direction is the same, because the axis
         *     names are constrained by the ISO 19111 specification in a way that do not include the units
         *     of measurement. Examples: "Gravity-related height", "Depth".
         *
         *   - We can not reuse the previous Coordinate System name, because it often contains the axis
         *     abbreviation and unit. Examples: "Vertical CS. Axis: height (H). Orientation: up. UoM: m.".
         *     Since we are lazy, we will reuse the axis name instead, which is more neutral.
         *
         *   - We generally can reuse the CRS name because those names tend to refer to the datum (which is
         *     unchanged) rather than the coordinate system. Examples: "Low Water depth", "NGF Lallemand height",
         *     "JGD2011 (vertical) height". However we make an exception if the direction is down, because in such
         *     cases the previous name may contain terms like "depth", which are not appropriate for our new CRS.
         */
    final VerticalCS cs = csFactory.createVerticalCS(properties(axis.getName()), axis);
    extent.setVerticalCRS(crsFactory.createVerticalCRS(properties((isUP ? compatibleCRS : axis).getName()), compatibleCRS.getDatum(), cs));
    return next;
}
Also used : VerticalCS(org.opengis.referencing.cs.VerticalCS) CoordinateSystemAxis(org.opengis.referencing.cs.CoordinateSystemAxis) IdentifiedObject(org.opengis.referencing.IdentifiedObject)

Example 34 with CoordinateSystemAxis

use of org.opengis.referencing.cs.CoordinateSystemAxis in project sis by apache.

the class CRS method getVerticalComponent.

/**
 * Returns the first vertical coordinate reference system found in the given CRS, or {@code null} if there is none.
 * If the given CRS is already an instance of {@code VerticalCRS}, then this method returns it as-is.
 * Otherwise if the given CRS is compound, then this method searches for the first vertical component
 * in the order of the {@linkplain #getSingleComponents(CoordinateReferenceSystem) single components list}.
 *
 * <div class="section">Height in a three-dimensional geographic CRS</div>
 * In ISO 19111 model, ellipsoidal heights are indissociable from geographic CRS because such heights
 * without their (<var>latitude</var>, <var>longitude</var>) locations make little sense. Consequently
 * a standard-conformant library should return {@code null} when asked for the {@code VerticalCRS}
 * component of a geographic CRS. This is what {@code getVerticalComponent(…)} does when the
 * {@code allowCreateEllipsoidal} argument is {@code false}.
 *
 * <p>However in some exceptional cases, handling ellipsoidal heights like any other kind of heights
 * may simplify the task. For example when computing <em>difference</em> between heights above the
 * same datum, the impact of ignoring locations may be smaller (but not necessarily canceled).
 * Orphan {@code VerticalCRS} may also be useful for information purpose like labeling a plot axis.
 * If the caller feels confident that ellipsoidal heights are safe for his task, he can set the
 * {@code allowCreateEllipsoidal} argument to {@code true}. In such case, this {@code getVerticalComponent(…)}
 * method will create a temporary {@code VerticalCRS} from the first three-dimensional {@code GeographicCRS}
 * <em>in last resort</em>, only if it can not find an existing {@code VerticalCRS} instance.
 * <strong>Note that this is not a valid CRS according ISO 19111</strong> — use with care.</p>
 *
 * @param  crs  the coordinate reference system, or {@code null}.
 * @param  allowCreateEllipsoidal {@code true} for allowing the creation of orphan CRS for ellipsoidal heights.
 *         The recommended value is {@code false}.
 * @return the first vertical CRS, or {@code null} if none.
 *
 * @see #compound(CoordinateReferenceSystem...)
 *
 * @category information
 */
public static VerticalCRS getVerticalComponent(final CoordinateReferenceSystem crs, final boolean allowCreateEllipsoidal) {
    if (crs instanceof VerticalCRS) {
        return (VerticalCRS) crs;
    }
    if (crs instanceof CompoundCRS) {
        final CompoundCRS cp = (CompoundCRS) crs;
        boolean a = false;
        do {
            // Executed at most twice.
            for (final CoordinateReferenceSystem c : cp.getComponents()) {
                final VerticalCRS candidate = getVerticalComponent(c, a);
                if (candidate != null) {
                    return candidate;
                }
            }
        } while ((a = !a) == allowCreateEllipsoidal);
    }
    if (allowCreateEllipsoidal && horizontalCode(crs) == 3) {
        final CoordinateSystem cs = crs.getCoordinateSystem();
        final int i = AxisDirections.indexOfColinear(cs, AxisDirection.UP);
        if (i >= 0) {
            final CoordinateSystemAxis axis = cs.getAxis(i);
            VerticalCRS c = CommonCRS.Vertical.ELLIPSOIDAL.crs();
            if (!c.getCoordinateSystem().getAxis(0).equals(axis)) {
                final Map<String, ?> properties = IdentifiedObjects.getProperties(c);
                c = new DefaultVerticalCRS(properties, c.getDatum(), new DefaultVerticalCS(properties, axis));
            }
            return c;
        }
    }
    return null;
}
Also used : DefaultVerticalCRS(org.apache.sis.referencing.crs.DefaultVerticalCRS) CoordinateSystem(org.opengis.referencing.cs.CoordinateSystem) DefaultVerticalCRS(org.apache.sis.referencing.crs.DefaultVerticalCRS) VerticalCRS(org.opengis.referencing.crs.VerticalCRS) CompoundCRS(org.opengis.referencing.crs.CompoundCRS) DefaultCompoundCRS(org.apache.sis.referencing.crs.DefaultCompoundCRS) CoordinateSystemAxis(org.opengis.referencing.cs.CoordinateSystemAxis) CoordinateReferenceSystem(org.opengis.referencing.crs.CoordinateReferenceSystem) DefaultVerticalCS(org.apache.sis.referencing.cs.DefaultVerticalCS)

Example 35 with CoordinateSystemAxis

use of org.opengis.referencing.cs.CoordinateSystemAxis in project sis by apache.

the class ReferencingFunctions method getAxis.

/**
 * Returns the axis name and units for the specified dimension in a coordinate reference system or coordinate system.
 * This method returns a short axis name as used in Well Known Text (WKT) format, for example <cite>"Latitude"</cite>
 * instead of <cite>"Geodetic latitude"</cite>.
 *
 * @param  codeOrPath  the code allocated by an authority, or the path to a file.
 * @param  dimension   the dimension (1, 2, …).
 * @return the name of the requested axis.
 */
@Override
public String getAxis(final String codeOrPath, final int dimension) {
    final CacheKey<String> key = new CacheKey<>(String.class, codeOrPath, dimension, null);
    String name = key.peek();
    if (name == null) {
        final Cache.Handler<String> handler = key.lock();
        try {
            name = handler.peek();
            if (name == null) {
                final IdentifiedObject object;
                try {
                    object = getIdentifiedObject(codeOrPath, null);
                } catch (Exception exception) {
                    return getLocalizedMessage(exception);
                }
                CoordinateSystem cs = null;
                final CoordinateSystemAxis axis;
                if (object instanceof CoordinateSystemAxis) {
                    axis = (CoordinateSystemAxis) object;
                } else {
                    if (object instanceof CoordinateReferenceSystem) {
                        cs = ((CoordinateReferenceSystem) object).getCoordinateSystem();
                    } else if (object instanceof CoordinateSystem) {
                        cs = (CoordinateSystem) object;
                    } else {
                        final Class<?> actual;
                        if (object instanceof AbstractIdentifiedObject) {
                            actual = ((AbstractIdentifiedObject) object).getInterface();
                        } else {
                            actual = Classes.getClass(object);
                        }
                        return Errors.getResources(getJavaLocale()).getString(Errors.Keys.UnexpectedTypeForReference_3, codeOrPath, CoordinateReferenceSystem.class, actual);
                    }
                    if (dimension >= 1 && dimension <= cs.getDimension()) {
                        axis = cs.getAxis(dimension - 1);
                    } else {
                        return Errors.getResources(getJavaLocale()).getString(Errors.Keys.IndexOutOfBounds_1, dimension);
                    }
                }
                final String unit = axis.getUnit().toString();
                name = Transliterator.DEFAULT.toShortAxisName(cs, axis.getDirection(), axis.getName().getCode());
                if (unit != null && !unit.isEmpty()) {
                    name = name + " (" + unit + ')';
                }
            }
        } finally {
            handler.putAndUnlock(name);
        }
    }
    return name;
}
Also used : AbstractIdentifiedObject(org.apache.sis.referencing.AbstractIdentifiedObject) CoordinateSystem(org.opengis.referencing.cs.CoordinateSystem) CoordinateSystemAxis(org.opengis.referencing.cs.CoordinateSystemAxis) InternationalString(org.opengis.util.InternationalString) CoordinateReferenceSystem(org.opengis.referencing.crs.CoordinateReferenceSystem) IdentifiedObject(org.opengis.referencing.IdentifiedObject) AbstractIdentifiedObject(org.apache.sis.referencing.AbstractIdentifiedObject) DataStoreException(org.apache.sis.storage.DataStoreException) IllegalArgumentException(com.sun.star.lang.IllegalArgumentException) FactoryException(org.opengis.util.FactoryException) Cache(org.apache.sis.util.collection.Cache)

Aggregations

CoordinateSystemAxis (org.opengis.referencing.cs.CoordinateSystemAxis)50 CoordinateSystem (org.opengis.referencing.cs.CoordinateSystem)15 Test (org.junit.Test)14 CoordinateReferenceSystem (org.opengis.referencing.crs.CoordinateReferenceSystem)9 EllipsoidalCS (org.opengis.referencing.cs.EllipsoidalCS)5 Unit (javax.measure.Unit)4 VerticalCRS (org.opengis.referencing.crs.VerticalCRS)4 AxisDirection (org.opengis.referencing.cs.AxisDirection)4 FactoryException (org.opengis.util.FactoryException)4 IdentifiedObject (org.opengis.referencing.IdentifiedObject)3 VerticalCS (org.opengis.referencing.cs.VerticalCS)3 InternationalString (org.opengis.util.InternationalString)3 DefaultVerticalCRS (org.apache.sis.referencing.crs.DefaultVerticalCRS)2 DefaultCoordinateSystemAxis (org.apache.sis.referencing.cs.DefaultCoordinateSystemAxis)2 DefaultVerticalCS (org.apache.sis.referencing.cs.DefaultVerticalCS)2 AbstractCoordinateOperation (org.apache.sis.referencing.operation.AbstractCoordinateOperation)2 DependsOnMethod (org.apache.sis.test.DependsOnMethod)2 Identifier (org.opengis.metadata.Identifier)2 VerticalExtent (org.opengis.metadata.extent.VerticalExtent)2 GeodeticCRS (org.opengis.referencing.crs.GeodeticCRS)2