Search in sources :

Example 46 with FactoryException

use of org.opengis.util.FactoryException in project sis by apache.

the class TransformSeparatorTest method testLinearTransform.

/**
 * Tests separation of a linear transform.
 *
 * @throws FactoryException if an error occurred while creating a new transform.
 */
@Test
public void testLinearTransform() throws FactoryException {
    Matrix matrix = Matrices.create(4, 4, new double[] { // Some random values.
    2, // Some random values.
    0, // Some random values.
    0, // Some random values.
    7, 0, 5, 0, 6, 1, 0, 3, 8, 0, 0, 0, 1 });
    final TransformSeparator s = new TransformSeparator(MathTransforms.linear(matrix));
    /*
         * Trivial case: no dimension specified, we should get the transform unchanged.
         */
    assertSame("transform", s.transform, s.separate());
    assertArrayEquals("sourceDimensions", new int[] { 0, 1, 2 }, s.getSourceDimensions());
    assertArrayEquals("targetDimensions", new int[] { 0, 1, 2 }, s.getTargetDimensions());
    /*
         * Filter only target dimensions. This is the easiest non-trivial case since we just
         * need to drop some rows. There is no analysis to perform on the matrix values.
         */
    matrix = Matrices.create(3, 4, new double[] { 2, 0, 0, 7, 1, 0, 3, 8, 0, 0, 0, 1 });
    s.clear();
    s.addTargetDimensions(0, 2);
    assertMatrixEquals("transform", matrix, ((LinearTransform) s.separate()).getMatrix(), STRICT);
    assertArrayEquals("sourceDimensions", new int[] { 0, 1, 2 }, s.getSourceDimensions());
    assertArrayEquals("targetDimensions", new int[] { 0, 2 }, s.getTargetDimensions());
    /*
         * Filter only source dimensions. Do not specify any target dimensions for now.
         * TransformSeparator needs to examine the matrix values and drop all target dimensions
         * that depend on an excluded source dimensions.
         */
    matrix = Matrices.create(2, 3, new double[] { 5, 0, 6, 0, 0, 1 });
    s.clear();
    s.addSourceDimensions(1, 2);
    assertMatrixEquals("transform", matrix, ((LinearTransform) s.separate()).getMatrix(), STRICT);
    assertArrayEquals("sourceDimensions", new int[] { 1, 2 }, s.getSourceDimensions());
    assertArrayEquals("targetDimensions", new int[] { 1 }, s.getTargetDimensions());
    /*
         * Filter both source and target dimensions. Source dimensions 0 and 2 allow the target dimensions 0 and 2
         * (target dimension 1 is discarded because it depends on source dimension 1).  Then the target dimensions
         * are filtered for retaining only dimension 0.
         */
    matrix = Matrices.create(2, 3, new double[] { 2, 0, 7, 0, 0, 1 });
    s.clear();
    s.addSourceDimensions(0, 2);
    s.addTargetDimensions(0);
    assertMatrixEquals("transform", matrix, ((LinearTransform) s.separate()).getMatrix(), STRICT);
    assertArrayEquals("sourceDimensions", new int[] { 0, 2 }, s.getSourceDimensions());
    assertArrayEquals("targetDimensions", new int[] { 0 }, s.getTargetDimensions());
    /*
         * Try again, but with the addition of a target dimension that TransformSeparator can not keep.
         * It shall cause an exception to be thrown.
         */
    s.addTargetDimensions(1);
    try {
        s.separate();
        fail("Should not have been able to separate that transform.");
    } catch (FactoryException e) {
        // This is the expected exception.
        assertNotNull(e.getMessage());
    }
}
Also used : Matrix(org.opengis.referencing.operation.Matrix) FactoryException(org.opengis.util.FactoryException) Test(org.junit.Test)

Example 47 with FactoryException

use of org.opengis.util.FactoryException in project sis by apache.

the class Store method unmarshal.

/**
 * Unmarshal the object, if not already done. Note that {@link #object} may still be null
 * if an exception has been thrown at this invocation time or in previous invocation.
 *
 * @throws DataStoreException if an error occurred during the unmarshalling process.
 */
private void unmarshal() throws DataStoreException {
    final StreamSource s = source;
    final Closeable in = input(s);
    // Cleared first in case of error.
    source = null;
    if (in != null)
        try {
            try {
                object = XML.unmarshal(s, properties());
            } finally {
                in.close();
            }
        } catch (JAXBException | IOException e) {
            throw new DataStoreException(Errors.format(Errors.Keys.CanNotRead_1, getDisplayName()), e);
        }
    if (object instanceof CoordinateReferenceSystem)
        try {
            final DefinitionVerifier v = DefinitionVerifier.withAuthority((CoordinateReferenceSystem) object, null, false);
            if (v != null) {
                log(v.warning(false));
            }
        } catch (FactoryException e) {
            listeners.warning(null, e);
        }
}
Also used : DataStoreException(org.apache.sis.storage.DataStoreException) FactoryException(org.opengis.util.FactoryException) StreamSource(javax.xml.transform.stream.StreamSource) Closeable(java.io.Closeable) CoordinateReferenceSystem(org.opengis.referencing.crs.CoordinateReferenceSystem) DefinitionVerifier(org.apache.sis.internal.referencing.DefinitionVerifier)

Example 48 with FactoryException

use of org.opengis.util.FactoryException 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 49 with FactoryException

use of org.opengis.util.FactoryException in project sis by apache.

the class Envelopes method transform.

/**
 * Transforms the given envelope to the specified CRS. If any argument is null, or if the
 * {@linkplain GeneralEnvelope#getCoordinateReferenceSystem() envelope CRS} is null or the
 * same instance than the given target CRS, then the given envelope is returned unchanged.
 * Otherwise a new transformed envelope is returned.
 *
 * <div class="section">Performance tip</div>
 * If there is many envelopes to transform with the same source and target CRS, then it is more efficient
 * to get the {@link CoordinateOperation} or {@link MathTransform} instance once and invoke one of the
 * others {@code transform(…)} methods.
 *
 * @param  envelope   the envelope to transform (may be {@code null}).
 * @param  targetCRS  the target CRS (may be {@code null}).
 * @return a new transformed envelope, or directly {@code envelope} if no change was required.
 * @throws TransformException if a transformation was required and failed.
 *
 * @since 0.5
 */
public static Envelope transform(Envelope envelope, final CoordinateReferenceSystem targetCRS) throws TransformException {
    if (envelope != null && targetCRS != null) {
        final CoordinateReferenceSystem sourceCRS = envelope.getCoordinateReferenceSystem();
        if (sourceCRS != targetCRS) {
            if (sourceCRS == null) {
                // Slight optimization: just copy the given Envelope.
                envelope = new GeneralEnvelope(envelope);
                ((GeneralEnvelope) envelope).setCoordinateReferenceSystem(targetCRS);
            } else {
                final CoordinateOperation operation;
                try {
                    operation = CoordinateOperations.factory().createOperation(sourceCRS, targetCRS);
                } catch (FactoryException exception) {
                    throw new TransformException(Errors.format(Errors.Keys.CanNotTransformEnvelope), exception);
                }
                envelope = transform(operation, envelope);
            }
            assert Utilities.deepEquals(targetCRS, envelope.getCoordinateReferenceSystem(), ComparisonMode.DEBUG);
        }
    }
    return envelope;
}
Also used : FactoryException(org.opengis.util.FactoryException) NoninvertibleTransformException(org.opengis.referencing.operation.NoninvertibleTransformException) TransformException(org.opengis.referencing.operation.TransformException) AbstractCoordinateOperation(org.apache.sis.referencing.operation.AbstractCoordinateOperation) CoordinateOperation(org.opengis.referencing.operation.CoordinateOperation) CoordinateReferenceSystem(org.opengis.referencing.crs.CoordinateReferenceSystem)

Example 50 with FactoryException

use of org.opengis.util.FactoryException in project sis by apache.

the class LocationFormat method format.

/**
 * Writes a textual representation of the given location in the given stream or buffer.
 *
 * <div class="warning"><b>Upcoming API change — generalization</b><br>
 * in a future SIS version, the type of {@code location} parameter may be generalized
 * to the {@code org.opengis.referencing.gazetteer.Location} interface.
 * This change is pending GeoAPI revision.</div>
 *
 * @param  location    the location to format.
 * @param  toAppendTo  where to format the location.
 * @throws IOException if an error occurred while writing to the given appendable.
 */
@Override
@SuppressWarnings({ "fallthrough", "null" })
public void format(final AbstractLocation location, final Appendable toAppendTo) throws IOException {
    ArgumentChecks.ensureNonNull("location", location);
    final Locale locale = getLocale(Locale.Category.DISPLAY);
    final Vocabulary vocabulary = Vocabulary.getResources(locale);
    final TableAppender table = new TableAppender(toAppendTo, "│ ", columnSeparator, " │");
    table.setMultiLinesCells(true);
    /*
         * Location type.
         */
    table.appendHorizontalSeparator();
    final AbstractLocationType type = location.type();
    if (type != null) {
        append(table, vocabulary, Vocabulary.Keys.LocationType, toString(type.getName(), locale));
    }
    /*
         * Geographic identifier and alternative identifiers, if any.
         */
    append(table, vocabulary, Vocabulary.Keys.GeographicIdentifier, toString(location.getGeographicIdentifier(), locale));
    final Collection<? extends InternationalString> alt = location.getAlternativeGeographicIdentifiers();
    if (alt != null && !alt.isEmpty()) {
        boolean isFirst = true;
        vocabulary.appendLabel(Vocabulary.Keys.AlternativeIdentifiers, table);
        nextColumn(table);
        for (final InternationalString id : alt) {
            if (!isFirst) {
                isFirst = false;
                table.append(lineSeparator);
            }
            table.append(id);
        }
        table.nextLine();
    }
    /*
         * Extents (temporal and geographic). If an envelope exists and the CRS is not geographic,
         * then the envelope bounds will be appended on the same lines than the geographic bounds.
         * But before writing the bounding box and/or the envelope, check if they are redundant.
         * We may also need to change axis order (but not unit) of the envelope in order to match
         * the axis order of the geographic bounding box.
         */
    final Extent extent = new DefaultExtent(null, location.getGeographicExtent(), null, location.getTemporalExtent());
    final Range<Date> time = Extents.getTimeRange(extent);
    if (time != null) {
        append(table, vocabulary, Vocabulary.Keys.StartDate, toString(time.getMinValue()));
        append(table, vocabulary, Vocabulary.Keys.EndDate, toString(time.getMaxValue()));
    }
    GeographicBoundingBox bbox = Extents.getGeographicBoundingBox(extent);
    Envelope envelope = location.getEnvelope();
    DirectPosition position = position(location.getPosition());
    // Position in geographic CRS.
    DirectPosition geopos = null;
    // Envelope Coordinate Reference System.
    CoordinateReferenceSystem crs = null;
    // CRS in conventional (x,y) axis order.
    CoordinateReferenceSystem normCRS = null;
    // If failed to transform envelope.
    Exception warning = null;
    try {
        if (envelope != null) {
            normCRS = normalize(crs = envelope.getCoordinateReferenceSystem());
            if (normCRS != crs) {
                // Should only change order and sign.
                envelope = Envelopes.transform(envelope, normCRS);
            }
        }
        if (position != null) {
            /*
                 * If only one of the envelope or the position objects specify a CRS, assume that the other object
                 * use the same CRS. If both the envelope and the position objects specify a CRS, the envelope CRS
                 * will have precedence and the "representative position" will be projected to that CRS.
                 */
            final CoordinateReferenceSystem posCRS = position.getCoordinateReferenceSystem();
            if (normCRS == null) {
                normCRS = normalize(crs = posCRS);
                if (normCRS != crs) {
                    // Should only change order and sign.
                    envelope = Envelopes.transform(envelope, normCRS);
                }
            }
            if (bbox != null) {
                // Compute geographic position only if there is a geographic bounding box.
                GeographicCRS geogCRS = ReferencingUtilities.toNormalizedGeographicCRS(posCRS);
                if (geogCRS != null) {
                    geopos = transform(position, posCRS, geogCRS);
                }
            }
            position = transform(position, posCRS, normCRS);
        }
    } catch (FactoryException | TransformException e) {
        envelope = null;
        position = null;
        warning = e;
    }
    /*
         * At this point we got the final geographic bounding box and/or envelope to write.
         * Since we will write the projected and geographic coordinates side-by-side in the same cells,
         * we need to format them in advance so we can compute their width for internal right-alignment.
         * We do the alignment ourselves instead than using TableAppender.setCellAlignment(ALIGN_RIGHT)
         * because we do not want (projected geographic) tuple to appear far on the right side if other
         * cells have long texts.
         */
    if (bbox != null || envelope != null) {
        final CoordinateSystem cs = (crs != null) ? crs.getCoordinateSystem() : null;
        String[] geographic = null;
        String[] projected = null;
        String[] unitSymbol = null;
        AngleFormat geogFormat = null;
        NumberFormat projFormat = null;
        UnitFormat unitFormat = null;
        int maxGeogLength = 0;
        int maxProjLength = 0;
        int maxUnitLength = 0;
        boolean showProj = false;
        if (bbox != null || geopos != null) {
            geogFormat = (AngleFormat) getFormat(Angle.class);
            geographic = new String[BOUND_KEY.length];
            Arrays.fill(geographic, "");
        }
        if (envelope != null || position != null) {
            projFormat = (NumberFormat) getFormat(Number.class);
            unitFormat = (UnitFormat) getFormat(Unit.class);
            projected = new String[BOUND_KEY.length];
            unitSymbol = new String[BOUND_KEY.length];
            Arrays.fill(projected, "");
            Arrays.fill(unitSymbol, "");
        }
        for (int i = 0; i < BOUND_KEY.length; i++) {
            RoundingMode rounding = RoundingMode.FLOOR;
            double g = Double.NaN;
            double p = Double.NaN;
            int dimension = 0;
            switch(i) {
                case 0:
                    if (bbox != null)
                        g = bbox.getWestBoundLongitude();
                    if (envelope != null)
                        p = envelope.getMinimum(0);
                    break;
                case 2:
                    if (bbox != null)
                        g = bbox.getEastBoundLongitude();
                    if (envelope != null)
                        p = envelope.getMaximum(0);
                    rounding = RoundingMode.CEILING;
                    break;
                case 3:
                    if (bbox != null)
                        g = bbox.getSouthBoundLatitude();
                    if (envelope != null)
                        p = envelope.getMinimum(1);
                    dimension = 1;
                    break;
                case 5:
                    if (bbox != null)
                        g = bbox.getNorthBoundLatitude();
                    if (envelope != null)
                        p = envelope.getMaximum(1);
                    rounding = RoundingMode.CEILING;
                    dimension = 1;
                    break;
                // Fall through
                case 4:
                    dimension = 1;
                case 1:
                    if (geopos != null)
                        g = geopos.getOrdinate(dimension);
                    if (position != null)
                        p = position.getOrdinate(dimension);
                    rounding = RoundingMode.HALF_EVEN;
                    break;
            }
            if (!Double.isNaN(p)) {
                showProj |= (g != p);
                if (cs != null) {
                    final Unit<?> unit = cs.getAxis(dimension).getUnit();
                    if (unit != null) {
                        final int length = (unitSymbol[i] = unitFormat.format(unit)).length();
                        if (length > maxUnitLength) {
                            maxUnitLength = length;
                        }
                    }
                }
                try {
                    projFormat.setRoundingMode(rounding);
                } catch (UnsupportedOperationException e) {
                // Ignore.
                }
                final int length = (projected[i] = projFormat.format(p)).length();
                if (length > maxProjLength) {
                    maxProjLength = length;
                }
            }
            if (!Double.isNaN(g)) {
                geogFormat.setRoundingMode(rounding);
                final Angle angle = (dimension == 0) ? new Longitude(g) : new Latitude(g);
                final int length = (geographic[i] = geogFormat.format(angle)).length();
                if (length > maxGeogLength) {
                    maxGeogLength = length;
                }
            }
        }
        if (!showProj) {
            // All projected coordinates are identical to geographic ones.
            projected = null;
            unitSymbol = null;
            maxProjLength = 0;
            maxUnitLength = 0;
        } else if (maxProjLength != 0) {
            if (maxUnitLength != 0) {
                maxUnitLength++;
            }
            // Arbitrary space between projected and geographic coordinates.
            maxGeogLength += 4;
        }
        /*
             * At this point all coordinates have been formatted in advance.
             */
        final String separator = (projected != null && geographic != null) ? "    —" : "";
        for (int i = 0; i < BOUND_KEY.length; i++) {
            final String p = (projected != null) ? projected[i] : "";
            final String u = (unitSymbol != null) ? unitSymbol[i] : "";
            final String g = (geographic != null) ? geographic[i] : "";
            if (!p.isEmpty() || !g.isEmpty()) {
                vocabulary.appendLabel(BOUND_KEY[i], table);
                nextColumn(table);
                table.append(CharSequences.spaces(maxProjLength - p.length())).append(p);
                table.append(CharSequences.spaces(maxUnitLength - u.length())).append(u).append(separator);
                table.append(CharSequences.spaces(maxGeogLength - g.length())).append(g);
                table.nextLine();
            }
        }
    }
    if (crs != null) {
        append(table, vocabulary, Vocabulary.Keys.CoordinateRefSys, IdentifiedObjects.getName(crs, null));
    }
    /*
         * Organization responsible for defining the characteristics of the location instance.
         */
    final AbstractParty administrator = location.getAdministrator();
    if (administrator != null) {
        append(table, vocabulary, Vocabulary.Keys.Administrator, toString(administrator.getName(), locale));
    }
    table.appendHorizontalSeparator();
    table.flush();
    if (warning != null) {
        vocabulary.appendLabel(Vocabulary.Keys.Warnings, toAppendTo);
        toAppendTo.append(warning.toString()).append(lineSeparator);
    }
}
Also used : Locale(java.util.Locale) Vocabulary(org.apache.sis.util.resources.Vocabulary) DirectPosition(org.opengis.geometry.DirectPosition) DefaultExtent(org.apache.sis.metadata.iso.extent.DefaultExtent) Extent(org.opengis.metadata.extent.Extent) FactoryException(org.opengis.util.FactoryException) CoordinateSystem(org.opengis.referencing.cs.CoordinateSystem) UnitFormat(org.apache.sis.measure.UnitFormat) RoundingMode(java.math.RoundingMode) TableAppender(org.apache.sis.io.TableAppender) Latitude(org.apache.sis.measure.Latitude) GeographicBoundingBox(org.opengis.metadata.extent.GeographicBoundingBox) InternationalString(org.opengis.util.InternationalString) AngleFormat(org.apache.sis.measure.AngleFormat) Envelope(org.opengis.geometry.Envelope) DefaultExtent(org.apache.sis.metadata.iso.extent.DefaultExtent) CoordinateReferenceSystem(org.opengis.referencing.crs.CoordinateReferenceSystem) GeographicCRS(org.opengis.referencing.crs.GeographicCRS) AbstractParty(org.apache.sis.metadata.iso.citation.AbstractParty) TransformException(org.opengis.referencing.operation.TransformException) Longitude(org.apache.sis.measure.Longitude) Date(java.util.Date) ParseException(java.text.ParseException) TransformException(org.opengis.referencing.operation.TransformException) IOException(java.io.IOException) FactoryException(org.opengis.util.FactoryException) Angle(org.apache.sis.measure.Angle) InternationalString(org.opengis.util.InternationalString) NumberFormat(java.text.NumberFormat)

Aggregations

FactoryException (org.opengis.util.FactoryException)84 TransformException (org.opengis.referencing.operation.TransformException)27 GeometryWrapper (org.apache.jena.geosparql.implementation.GeometryWrapper)21 MismatchedDimensionException (org.opengis.geometry.MismatchedDimensionException)19 ExprEvalException (org.apache.jena.sparql.expr.ExprEvalException)17 MathTransform (org.opengis.referencing.operation.MathTransform)15 DatatypeFormatException (org.apache.jena.datatypes.DatatypeFormatException)12 CoordinateReferenceSystem (org.opengis.referencing.crs.CoordinateReferenceSystem)10 IdentifiedObject (org.opengis.referencing.IdentifiedObject)8 Envelope (org.locationtech.jts.geom.Envelope)7 ArrayList (java.util.ArrayList)6 ParameterValueGroup (org.opengis.parameter.ParameterValueGroup)6 NoninvertibleTransformException (org.opengis.referencing.operation.NoninvertibleTransformException)6 Literal (org.apache.jena.rdf.model.Literal)5 UnavailableFactoryException (org.apache.sis.referencing.factory.UnavailableFactoryException)5 CoordinateSystem (org.opengis.referencing.cs.CoordinateSystem)5 ParseException (java.text.ParseException)4 AbstractIdentifiedObject (org.apache.sis.referencing.AbstractIdentifiedObject)4 BackingStoreException (org.apache.sis.util.collection.BackingStoreException)4 Test (org.junit.Test)4