Search in sources :

Example 6 with DefaultGeographicBoundingBox

use of org.apache.sis.metadata.iso.extent.DefaultGeographicBoundingBox in project sis by apache.

the class ServicesForMetadata method setBounds.

/**
 * Sets the geographic, vertical and temporal extents with the values inferred from the given envelope.
 * If the given {@code target} has more geographic or vertical extents than needed (0 or 1), then the
 * extraneous extents are removed.
 *
 * @param  envelope  the source envelope.
 * @param  target    the target spatiotemporal extent where to store envelope information.
 * @throws TransformException if no temporal component can be extracted from the given envelope.
 */
@Override
public void setBounds(final Envelope envelope, final DefaultSpatialTemporalExtent target) throws TransformException {
    final CoordinateReferenceSystem crs = envelope.getCoordinateReferenceSystem();
    final SingleCRS horizontalCRS = CRS.getHorizontalComponent(crs);
    final VerticalCRS verticalCRS = CRS.getVerticalComponent(crs, true);
    final TemporalCRS temporalCRS = CRS.getTemporalComponent(crs);
    if (horizontalCRS == null && verticalCRS == null && temporalCRS == null) {
        throw new TransformException(dimensionNotFound(Resources.Keys.MissingSpatioTemporalDimension_1, crs));
    }
    /*
         * Try to set the geographic bounding box first, because this operation may fail with a
         * TransformException while the other operations (vertical and temporal) should not fail.
         * So doing the geographic part first help us to get a "all or nothing" behavior.
         */
    DefaultGeographicBoundingBox box = null;
    boolean useExistingBox = (horizontalCRS != null);
    final Collection<GeographicExtent> spatialExtents = target.getSpatialExtent();
    final Iterator<GeographicExtent> it = spatialExtents.iterator();
    while (it.hasNext()) {
        final GeographicExtent extent = it.next();
        if (extent instanceof GeographicBoundingBox) {
            if (useExistingBox && (extent instanceof DefaultGeographicBoundingBox)) {
                box = (DefaultGeographicBoundingBox) extent;
                useExistingBox = false;
            } else {
                it.remove();
            }
        }
    }
    if (horizontalCRS != null) {
        if (box == null) {
            box = new DefaultGeographicBoundingBox();
            spatialExtents.add(box);
        }
        GeographicCRS normalizedCRS = ReferencingUtilities.toNormalizedGeographicCRS(crs);
        if (normalizedCRS == null) {
            normalizedCRS = CommonCRS.defaultGeographic();
        }
        setGeographicExtent(envelope, box, crs, normalizedCRS);
    }
    /*
         * Other dimensions (vertical and temporal).
         */
    if (verticalCRS != null) {
        VerticalExtent e = target.getVerticalExtent();
        if (!(e instanceof DefaultVerticalExtent)) {
            e = new DefaultVerticalExtent();
            target.setVerticalExtent(e);
        }
        setVerticalExtent(envelope, (DefaultVerticalExtent) e, crs, verticalCRS);
    } else {
        target.setVerticalExtent(null);
    }
    if (temporalCRS != null) {
        setTemporalExtent(envelope, target, crs, temporalCRS);
    } else {
        target.setExtent(null);
    }
}
Also used : SingleCRS(org.opengis.referencing.crs.SingleCRS) DefaultVerticalExtent(org.apache.sis.metadata.iso.extent.DefaultVerticalExtent) TransformException(org.opengis.referencing.operation.TransformException) DefaultVerticalExtent(org.apache.sis.metadata.iso.extent.DefaultVerticalExtent) VerticalExtent(org.opengis.metadata.extent.VerticalExtent) GeographicBoundingBox(org.opengis.metadata.extent.GeographicBoundingBox) DefaultGeographicBoundingBox(org.apache.sis.metadata.iso.extent.DefaultGeographicBoundingBox) GeographicExtent(org.opengis.metadata.extent.GeographicExtent) DefaultTemporalCRS(org.apache.sis.referencing.crs.DefaultTemporalCRS) TemporalCRS(org.opengis.referencing.crs.TemporalCRS) DefaultGeographicBoundingBox(org.apache.sis.metadata.iso.extent.DefaultGeographicBoundingBox) VerticalCRS(org.opengis.referencing.crs.VerticalCRS) CoordinateReferenceSystem(org.opengis.referencing.crs.CoordinateReferenceSystem) GeographicCRS(org.opengis.referencing.crs.GeographicCRS)

Example 7 with DefaultGeographicBoundingBox

use of org.apache.sis.metadata.iso.extent.DefaultGeographicBoundingBox in project sis by apache.

the class EPSGDataAccess method createExtent.

/**
 * Creates information about spatial, vertical, and temporal extent (usually a domain of validity) from a code.
 *
 * <div class="note"><b>Example:</b>
 * some EPSG codes for extents are:
 *
 * <table class="sis" summary="EPSG codes examples">
 *   <tr><th>Code</th> <th>Description</th></tr>
 *   <tr><td>1262</td> <td>World</td></tr>
 *   <tr><td>3391</td> <td>World - between 80°S and 84°N</td></tr>
 * </table></div>
 *
 * @param  code  value allocated by EPSG.
 * @return the extent for the given code.
 * @throws NoSuchAuthorityCodeException if the specified {@code code} was not found.
 * @throws FactoryException if the object creation failed for some other reason.
 *
 * @see #createCoordinateReferenceSystem(String)
 * @see #createDatum(String)
 * @see org.apache.sis.metadata.iso.extent.DefaultExtent
 */
@Override
public synchronized Extent createExtent(final String code) throws NoSuchAuthorityCodeException, FactoryException {
    ArgumentChecks.ensureNonNull("code", code);
    Extent returnValue = null;
    try (ResultSet result = executeQuery("Area", "AREA_CODE", "AREA_NAME", "SELECT AREA_OF_USE," + " AREA_SOUTH_BOUND_LAT," + " AREA_NORTH_BOUND_LAT," + " AREA_WEST_BOUND_LON," + " AREA_EAST_BOUND_LON" + " FROM [Area]" + " WHERE AREA_CODE = ?", code)) {
        while (result.next()) {
            final String description = getOptionalString(result, 1);
            double ymin = getOptionalDouble(result, 2);
            double ymax = getOptionalDouble(result, 3);
            double xmin = getOptionalDouble(result, 4);
            double xmax = getOptionalDouble(result, 5);
            DefaultGeographicBoundingBox bbox = null;
            if (!Double.isNaN(ymin) || !Double.isNaN(ymax) || !Double.isNaN(xmin) || !Double.isNaN(xmax)) {
                /*
                     * Fix an error found in EPSG:3790 New Zealand - South Island - Mount Pleasant mc
                     * for older database (this error is fixed in EPSG database 8.2).
                     *
                     * Do NOT apply anything similar for the x axis, because xmin > xmax is not error:
                     * it describes a bounding box spanning the anti-meridian (±180° of longitude).
                     */
                if (ymin > ymax) {
                    final double t = ymin;
                    ymin = ymax;
                    ymax = t;
                }
                bbox = new DefaultGeographicBoundingBox(xmin, xmax, ymin, ymax);
            }
            if (description != null || bbox != null) {
                DefaultExtent extent = new DefaultExtent(description, bbox, null, null);
                extent.freeze();
                returnValue = ensureSingleton(extent, returnValue, code);
            }
        }
    } catch (SQLException exception) {
        throw databaseFailure(Extent.class, code, exception);
    }
    if (returnValue == null) {
        throw noSuchAuthorityCode(Extent.class, code);
    }
    return returnValue;
}
Also used : DefaultGeographicBoundingBox(org.apache.sis.metadata.iso.extent.DefaultGeographicBoundingBox) DefaultExtent(org.apache.sis.metadata.iso.extent.DefaultExtent) DefaultExtent(org.apache.sis.metadata.iso.extent.DefaultExtent) Extent(org.opengis.metadata.extent.Extent) SQLException(java.sql.SQLException) ResultSet(java.sql.ResultSet) InternationalString(org.opengis.util.InternationalString) SimpleInternationalString(org.apache.sis.util.iso.SimpleInternationalString)

Example 8 with DefaultGeographicBoundingBox

use of org.apache.sis.metadata.iso.extent.DefaultGeographicBoundingBox in project sis by apache.

the class AbstractReferenceSystemTest method testWKT.

/**
 * Tests WKT formatting with a name that contains the quote character and optional information.
 * We test that the closing quote character is doubled and the optional information properly formatted.
 */
@Test
@DependsOnMethod("testCreateFromMap")
public void testWKT() {
    final Map<String, Object> properties = new HashMap<>(8);
    assertNull(properties.put(NAME_KEY, "My “object”."));
    assertNull(properties.put(SCOPE_KEY, "Large scale topographic mapping and cadastre."));
    assertNull(properties.put(REMARKS_KEY, "注です。"));
    assertNull(properties.put(IDENTIFIERS_KEY, new ImmutableIdentifier(Citations.EPSG, "EPSG", "4326", "8.2", null)));
    assertNull(properties.put(DOMAIN_OF_VALIDITY_KEY, new DefaultExtent("Netherlands offshore.", new DefaultGeographicBoundingBox(2.54, 6.40, 51.43, 55.77), new DefaultVerticalExtent(10, 1000, VerticalCRSMock.DEPTH), // TODO: needs sis-temporal module for testing that one.
    new DefaultTemporalExtent())));
    final AbstractReferenceSystem object = new AbstractReferenceSystem(properties);
    assertTrue(object.toString(Convention.WKT1).startsWith("ReferenceSystem[\"My “object”.\", AUTHORITY[\"EPSG\", \"4326\"]]"));
    assertWktEquals(Convention.WKT1, "ReferenceSystem[“My \"object\".”, AUTHORITY[“EPSG”, “4326”]]", object);
    assertWktEquals(Convention.WKT2, // Quotes replaced
    "ReferenceSystem[“My \"object\".”,\n" + "  SCOPE[“Large scale topographic mapping and cadastre.”],\n" + "  AREA[“Netherlands offshore.”],\n" + "  BBOX[51.43, 2.54, 55.77, 6.40],\n" + "  VERTICALEXTENT[-1000, -10, LENGTHUNIT[“metre”, 1]],\n" + "  ID[“EPSG”, 4326, “8.2”, URI[“urn:ogc:def:referenceSystem:EPSG:8.2:4326”]],\n" + "  REMARK[“注です。”]]", object);
    assertWktEquals(Convention.WKT2_SIMPLIFIED, "ReferenceSystem[“My \"object\".”,\n" + "  Scope[“Large scale topographic mapping and cadastre.”],\n" + "  Area[“Netherlands offshore.”],\n" + "  BBox[51.43, 2.54, 55.77, 6.40],\n" + "  VerticalExtent[-1000, -10],\n" + "  Id[“EPSG”, 4326, “8.2”, URI[“urn:ogc:def:referenceSystem:EPSG:8.2:4326”]],\n" + "  Remark[“注です。”]]", object);
    assertWktEquals(Convention.INTERNAL, // Quote doubled
    "ReferenceSystem[“My “object””.”,\n" + "  Scope[“Large scale topographic mapping and cadastre.”],\n" + "  Area[“Netherlands offshore.”],\n" + "  BBox[51.43, 2.54, 55.77, 6.40],\n" + "  VerticalExtent[-1000, -10],\n" + "  Id[“EPSG”, 4326, “8.2”],\n" + "  Remark[“注です。”]]", object);
}
Also used : DefaultGeographicBoundingBox(org.apache.sis.metadata.iso.extent.DefaultGeographicBoundingBox) DefaultExtent(org.apache.sis.metadata.iso.extent.DefaultExtent) DefaultVerticalExtent(org.apache.sis.metadata.iso.extent.DefaultVerticalExtent) HashMap(java.util.HashMap) DefaultTemporalExtent(org.apache.sis.metadata.iso.extent.DefaultTemporalExtent) ImmutableIdentifier(org.apache.sis.metadata.iso.ImmutableIdentifier) Test(org.junit.Test) DependsOnMethod(org.apache.sis.test.DependsOnMethod)

Example 9 with DefaultGeographicBoundingBox

use of org.apache.sis.metadata.iso.extent.DefaultGeographicBoundingBox in project sis by apache.

the class ServicesForMetadataTest method testSetGeographicBoundsFrom3D.

/**
 * Tests (indirectly) {@link ServicesForMetadata#setBounds(Envelope, DefaultGeographicBoundingBox)}
 * from a three-dimensional envelope.
 *
 * @throws TransformException should never happen.
 */
@Test
public void testSetGeographicBoundsFrom3D() throws TransformException {
    final DefaultGeographicBoundingBox box = new DefaultGeographicBoundingBox();
    box.setBounds(createEnvelope(HardCodedCRS.WGS84_3D));
    verifySpatialExtent(box);
}
Also used : DefaultGeographicBoundingBox(org.apache.sis.metadata.iso.extent.DefaultGeographicBoundingBox) Test(org.junit.Test)

Example 10 with DefaultGeographicBoundingBox

use of org.apache.sis.metadata.iso.extent.DefaultGeographicBoundingBox in project sis by apache.

the class CRS method suggestCommonTarget.

/**
 * Suggests a coordinate reference system which could be a common target for coordinate operations having the
 * given sources. This method compares the {@linkplain #getGeographicBoundingBox(CoordinateReferenceSystem)
 * domain of validity} of all given CRSs. If a CRS has a domain of validity that contains the domain of all other
 * CRS, than that CRS is returned. Otherwise this method verifies if a {@linkplain GeneralDerivedCRS#getBaseCRS()
 * base CRS} (usually a {@linkplain org.apache.sis.referencing.crs.DefaultGeographicCRS geographic CRS} instance)
 * would be suitable. If no suitable CRS is found, then this method returns {@code null}.
 *
 * <div class="note"><b>Use case:</b>
 * before to test if two arbitrary envelopes {@linkplain GeneralEnvelope#intersects(Envelope) intersect} each other,
 * they need to be {@linkplain Envelopes#transform(Envelope, CoordinateReferenceSystem) transformed} in the same CRS.
 * However if one CRS is a Transverse Mercator projection while the other CRS is a world-wide geographic CRS, then
 * attempts to use the Transverse Mercator projection as the common CRS is likely to fail since the geographic envelope
 * may span an area far outside the projection domain of validity. This {@code suggestCommonTarget(…)} method can used
 * for choosing a common CRS which is less likely to fail.</div>
 *
 * @param  regionOfInterest  the geographic area for which the coordinate operations will be applied,
 *                           or {@code null} if unknown.
 * @param  sourceCRS         the coordinate reference systems for which a common target CRS is desired.
 * @return a CRS that may be used as a common target for all the given source CRS in the given region of interest,
 *         or {@code null} if this method did not find a common target CRS. The returned CRS may be different than
 *         all given CRS.
 *
 * @since 0.8
 */
public static CoordinateReferenceSystem suggestCommonTarget(GeographicBoundingBox regionOfInterest, CoordinateReferenceSystem... sourceCRS) {
    CoordinateReferenceSystem bestCRS = null;
    /*
         * Compute the union of the domain of validity of all CRS. If a CRS does not specify a domain of validity,
         * then assume that the CRS is valid for the whole world if the CRS is geodetic or return null otherwise.
         * Opportunistically remember the domain of validity of each CRS in this loop since we will need them later.
         */
    boolean worldwide = false;
    DefaultGeographicBoundingBox domain = null;
    final GeographicBoundingBox[] domains = new GeographicBoundingBox[sourceCRS.length];
    for (int i = 0; i < sourceCRS.length; i++) {
        final CoordinateReferenceSystem crs = sourceCRS[i];
        final GeographicBoundingBox bbox = getGeographicBoundingBox(crs);
        if (bbox == null) {
            /*
                 * If no domain of validity is specified and we can not fallback
                 * on some knowledge about what the CRS is, abandon.
                 */
            if (!(crs instanceof GeodeticCRS)) {
                return null;
            }
            /*
                 * Geodetic CRS (geographic or geocentric) can generally be presumed valid in a worldwide area.
                 * The 'worldwide' flag is a little optimization for remembering that we do not need to compute
                 * the union anymore, but we still need to continue the loop for fetching all bounding boxes.
                 */
            // Fallback to be used if we don't find anything better.
            bestCRS = crs;
            worldwide = true;
        } else {
            domains[i] = bbox;
            if (!worldwide) {
                if (domain == null) {
                    domain = new DefaultGeographicBoundingBox(bbox);
                } else {
                    domain.add(bbox);
                }
            }
        }
    }
    /*
         * At this point we got the union of the domain of validity of all CRS. We are interested only in the
         * part that intersect the region of interest. If the union is whole world, we do not need to compute
         * the intersection; we can just leave the region of interest unchanged.
         */
    if (domain != null && !worldwide) {
        if (regionOfInterest != null) {
            domain.intersect(regionOfInterest);
        }
        regionOfInterest = domain;
        domain = null;
    }
    /*
         * Iterate again over the domain of validity of all CRS.  For each domain of validity, compute the area
         * which is inside the domain or interest and the area which is outside. The "best CRS" will be the one
         * which comply with the following rules, in preference order:
         *
         *   1) The CRS which is valid over the largest area of the region of interest.
         *   2) If two CRS are equally good according rule 1, then the CRS with the smallest "outside area".
         *
         * Example: given two source CRS, a geographic one and a projected one:
         *
         *   - If the projected CRS contains fully the region of interest, then it will be returned.
         *     The preference is given to the projected CRS because geometric are likely to be more
         *     accurate in that space. Furthermore forward conversions from geographic to projected
         *     CRS are usually faster than inverse conversions.
         *
         *   - Otherwise (i.e. if the region of interest is likely to be wider than the projected CRS
         *     domain of validity), then the geographic CRS will be returned.
         */
    // NaN if 'regionOfInterest' is null.
    final double roiArea = Extents.area(regionOfInterest);
    double maxInsideArea = 0;
    double minOutsideArea = Double.POSITIVE_INFINITY;
    boolean tryDerivedCRS = false;
    do {
        for (int i = 0; i < domains.length; i++) {
            final GeographicBoundingBox bbox = domains[i];
            if (bbox != null) {
                double insideArea = Extents.area(bbox);
                double outsideArea = 0;
                if (regionOfInterest != null) {
                    if (domain == null) {
                        domain = new DefaultGeographicBoundingBox(bbox);
                    } else {
                        domain.setBounds(bbox);
                    }
                    domain.intersect(regionOfInterest);
                    final double area = insideArea;
                    insideArea = Extents.area(domain);
                    outsideArea = area - insideArea;
                }
                if (insideArea > maxInsideArea || (insideArea == maxInsideArea && outsideArea < minOutsideArea)) {
                    maxInsideArea = insideArea;
                    minOutsideArea = outsideArea;
                    bestCRS = sourceCRS[i];
                }
            }
        }
        /*
             * If the best CRS does not cover fully the region of interest, then we will redo the check again
             * but using base CRS instead. For example if the list of source CRS had some projected CRS, we
             * will try with the geographic CRS on which those projected CRS are based.
             */
        if (maxInsideArea < roiArea) {
            // Do not try twice.
            if (tryDerivedCRS)
                break;
            final CoordinateReferenceSystem[] derivedCRS = new CoordinateReferenceSystem[sourceCRS.length];
            for (int i = 0; i < derivedCRS.length; i++) {
                GeographicBoundingBox bbox = null;
                final CoordinateReferenceSystem crs = sourceCRS[i];
                if (crs instanceof GeneralDerivedCRS) {
                    final CoordinateReferenceSystem baseCRS = ((GeneralDerivedCRS) crs).getBaseCRS();
                    bbox = getGeographicBoundingBox(baseCRS);
                    if (bbox == null && bestCRS == null && baseCRS instanceof GeodeticCRS) {
                        // Fallback to be used if we don't find anything better.
                        bestCRS = baseCRS;
                    }
                    tryDerivedCRS = true;
                    derivedCRS[i] = baseCRS;
                }
                domains[i] = bbox;
            }
            sourceCRS = derivedCRS;
        } else {
            break;
        }
    } while (tryDerivedCRS);
    return bestCRS;
}
Also used : DefaultGeographicBoundingBox(org.apache.sis.metadata.iso.extent.DefaultGeographicBoundingBox) GeneralDerivedCRS(org.opengis.referencing.crs.GeneralDerivedCRS) CoordinateReferenceSystem(org.opengis.referencing.crs.CoordinateReferenceSystem) GeographicBoundingBox(org.opengis.metadata.extent.GeographicBoundingBox) DefaultGeographicBoundingBox(org.apache.sis.metadata.iso.extent.DefaultGeographicBoundingBox) GeodeticCRS(org.opengis.referencing.crs.GeodeticCRS)

Aggregations

DefaultGeographicBoundingBox (org.apache.sis.metadata.iso.extent.DefaultGeographicBoundingBox)16 DefaultExtent (org.apache.sis.metadata.iso.extent.DefaultExtent)6 Test (org.junit.Test)6 HashMap (java.util.HashMap)4 CoordinateReferenceSystem (org.opengis.referencing.crs.CoordinateReferenceSystem)4 DefaultTemporalExtent (org.apache.sis.metadata.iso.extent.DefaultTemporalExtent)3 DefaultVerticalExtent (org.apache.sis.metadata.iso.extent.DefaultVerticalExtent)3 GeographicBoundingBox (org.opengis.metadata.extent.GeographicBoundingBox)3 ImmutableIdentifier (org.apache.sis.metadata.iso.ImmutableIdentifier)2 DefaultTemporalCRS (org.apache.sis.referencing.crs.DefaultTemporalCRS)2 DependsOnMethod (org.apache.sis.test.DependsOnMethod)2 Extent (org.opengis.metadata.extent.Extent)2 GeographicExtent (org.opengis.metadata.extent.GeographicExtent)2 ReferenceIdentifier (org.opengis.referencing.ReferenceIdentifier)2 GeneralDerivedCRS (org.opengis.referencing.crs.GeneralDerivedCRS)2 SingleCRS (org.opengis.referencing.crs.SingleCRS)2 TemporalCRS (org.opengis.referencing.crs.TemporalCRS)2 VerticalCRS (org.opengis.referencing.crs.VerticalCRS)2 TransformException (org.opengis.referencing.operation.TransformException)2 GenericName (org.opengis.util.GenericName)2