Search in sources :

Example 1 with GeneralDerivedCRS

use of org.opengis.referencing.crs.GeneralDerivedCRS in project sis by apache.

the class CoordinateOperationMethods method computeUnionOfAllDomainOfValidity.

/**
 * For each {@link OperationMethod} (identified by their name), computes the union of the domain of validity
 * of all CRS using that operation method. The result is a map where keys are {@link OperationMethod} names,
 * and values are the union of the domain of validity of all CRS using that {@code OperationMethod}.
 *
 * <p>This is a costly operation.</p>
 *
 * @todo This method is not yet used. This is pending the implementation of {@code CRSAuthorityFactory} is SIS.
 *
 * @param  factory  the factory to use for getting CRS.
 * @return the union of domain of validity of all map projections using a method of the given name.
 * @throws FactoryException if an error occurred while fetching the list of CRS.
 */
public static Map<String, DefaultGeographicBoundingBox> computeUnionOfAllDomainOfValidity(final CRSAuthorityFactory factory) throws FactoryException {
    final Map<String, DefaultGeographicBoundingBox> domainOfValidity = new HashMap<>();
    for (final String code : factory.getAuthorityCodes(GeneralDerivedCRS.class)) {
        final CoordinateReferenceSystem crs;
        try {
            crs = factory.createCoordinateReferenceSystem(code);
        } catch (FactoryException e) {
            // Ignore and inspect the next element.
            continue;
        }
        if (crs instanceof GeneralDerivedCRS) {
            final GeographicBoundingBox candidate = CRS.getGeographicBoundingBox(crs);
            if (candidate != null) {
                final String name = ((GeneralDerivedCRS) crs).getConversionFromBase().getMethod().getName().getCode();
                DefaultGeographicBoundingBox validity = domainOfValidity.get(name);
                if (validity == null) {
                    validity = new DefaultGeographicBoundingBox(candidate);
                    domainOfValidity.put(name, validity);
                } else {
                    validity.add(candidate);
                }
            }
        }
    }
    return domainOfValidity;
}
Also used : DefaultGeographicBoundingBox(org.apache.sis.metadata.iso.extent.DefaultGeographicBoundingBox) HashMap(java.util.HashMap) LinkedHashMap(java.util.LinkedHashMap) FactoryException(org.opengis.util.FactoryException) 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)

Example 2 with GeneralDerivedCRS

use of org.opengis.referencing.crs.GeneralDerivedCRS in project sis by apache.

the class CoordinateOperationSet method createObject.

/**
 * Creates a coordinate operation for the specified EPSG code.
 */
@Override
protected CoordinateOperation createObject(final String code) throws FactoryException {
    final Integer base = projections.get(code);
    if (base != null) {
        /*
             * First case documented in class Javadoc:
             *
             *     SELECT PROJECTION_CONV_CODE FROM "Coordinate Reference System" …
             *
             * The result is usually a ProjectedCRS, but not always.
             */
        CoordinateReferenceSystem crs;
        crs = ((CRSAuthorityFactory) factory).createCoordinateReferenceSystem(String.valueOf(base));
        if (crs instanceof GeneralDerivedCRS) {
            return ((GeneralDerivedCRS) crs).getConversionFromBase();
        }
    }
    /*
         * Following line is either for the second case documented in class Javadoc, or the first case
         * when the result is not a derived CRS. Note that we could create a derived CRS here as below:
         *
         *     CoordinateOperation op = …,
         *     if (crs != null && op instanceof Conversion) {
         *         return DefaultDerivedCRS.create(IdentifiedObjects.getProperties(crs), baseCRS,
         *                 (Conversion) op, crs.getCoordinateSystem()).getConversionFromBase();
         *     }
         *
         * We don't do that for now because because EPSGDataAccess.createCoordinateReferenceSystem(String)
         * would be a better place, by generalizing the work done for ProjectedCRS.
         *
         * https://issues.apache.org/jira/browse/SIS-357
         */
    return ((CoordinateOperationAuthorityFactory) factory).createCoordinateOperation(code);
}
Also used : CoordinateOperationAuthorityFactory(org.opengis.referencing.operation.CoordinateOperationAuthorityFactory) GeneralDerivedCRS(org.opengis.referencing.crs.GeneralDerivedCRS) CoordinateReferenceSystem(org.opengis.referencing.crs.CoordinateReferenceSystem)

Example 3 with GeneralDerivedCRS

use of org.opengis.referencing.crs.GeneralDerivedCRS in project sis by apache.

the class DefaultOperationMethod method formatTo.

/**
 * Formats this operation as a <cite>Well Known Text</cite> {@code Method[…]} element.
 *
 * @return {@code "Method"} (WKT 2) or {@code "Projection"} (WKT 1).
 *
 * @see <a href="http://docs.opengeospatial.org/is/12-063r5/12-063r5.html#118">WKT 2 specification §17.2.3</a>
 */
@Override
protected String formatTo(final Formatter formatter) {
    final boolean isWKT1 = formatter.getConvention().majorVersion() == 1;
    /*
         * The next few lines below are basically a copy of the work done by super.formatTo(formatter),
         * which search for the name to write inside METHOD["name"]. The difference is in the fallback
         * executed if we do not find a name for the given authority.
         */
    final Citation authority = formatter.getNameAuthority();
    String name = IdentifiedObjects.getName(this, authority);
    ElementKind kind = ElementKind.METHOD;
    if (name == null) {
        /*
             * No name found for the given authority. We may use the primary name as a fallback.
             * But before doing that, maybe we can find the name that we are looking for in the
             * hard-coded values in the 'org.apache.sis.internal.referencing.provider' package.
             * The typical use case is when this DefaultOperationMethod has been instantiated
             * by the EPSG factory using only the information found in the EPSG database.
             *
             * We can find the hard-coded names by looking at the ParameterDescriptorGroup of the
             * enclosing ProjectedCRS or DerivedCRS. This is because that parameter descriptor was
             * typically provided by the 'org.apache.sis.internal.referencing.provider' package in
             * order to create the MathTransform associated with the enclosing CRS.  The enclosing
             * CRS is either the immediate parent in WKT 1, or the parent of the parent in WKT 2.
             */
        final FormattableObject parent = formatter.getEnclosingElement(isWKT1 ? 1 : 2);
        if (parent instanceof GeneralDerivedCRS) {
            final Conversion conversion = ((GeneralDerivedCRS) parent).getConversionFromBase();
            if (conversion != null) {
                // Should never be null, but let be safe.
                final ParameterDescriptorGroup descriptor;
                if (conversion instanceof Parameterized) {
                    // Usual case in SIS implementation.
                    descriptor = ((Parameterized) conversion).getParameterDescriptors();
                } else {
                    descriptor = conversion.getParameterValues().getDescriptor();
                }
                name = IdentifiedObjects.getName(descriptor, authority);
            }
        }
        if (name == null) {
            name = IdentifiedObjects.getName(this, null);
            if (name == null) {
                name = Vocabulary.getResources(formatter.getLocale()).getString(Vocabulary.Keys.Unnamed);
                // Because the "Unnamed" string is not a real OperationMethod name.
                kind = ElementKind.NAME;
            }
        }
    }
    formatter.append(name, kind);
    if (isWKT1) {
        /*
             * The WKT 1 keyword is "PROJECTION", which imply that the operation method should be of type
             * org.opengis.referencing.operation.Projection. So strictly speaking only the first check in
             * the following 'if' statement is relevant.
             *
             * Unfortunately in many cases we do not know the operation type, because the method that we
             * invoked - getOperationType() - is not a standard OGC/ISO property, so this information is
             * usually not provided in XML documents for example.  The user could also have instantiated
             * DirectOperationMethod directly without creating a subclass. Consequently we also accept to
             * format the keyword as "PROJECTION" if the operation type *could* be a projection. This is
             * the second check in the following 'if' statement.
             *
             * In other words, the combination of those two checks exclude the following operation types:
             * Transformation, ConcatenatedOperation, PassThroughOperation, or any user-defined type that
             * do not extend Projection. All other operation types are accepted.
             */
        final Class<? extends SingleOperation> type = getOperationType();
        if (Projection.class.isAssignableFrom(type) || type.isAssignableFrom(Projection.class)) {
            return WKTKeywords.Projection;
        }
        formatter.setInvalidWKT(this, null);
    }
    return WKTKeywords.Method;
}
Also used : ElementKind(org.apache.sis.io.wkt.ElementKind) Parameterized(org.apache.sis.parameter.Parameterized) ParameterDescriptorGroup(org.opengis.parameter.ParameterDescriptorGroup) DefaultParameterDescriptorGroup(org.apache.sis.parameter.DefaultParameterDescriptorGroup) Projection(org.opengis.referencing.operation.Projection) GeneralDerivedCRS(org.opengis.referencing.crs.GeneralDerivedCRS) Citation(org.opengis.metadata.citation.Citation) InternationalString(org.opengis.util.InternationalString) SimpleInternationalString(org.apache.sis.util.iso.SimpleInternationalString) FormattableObject(org.apache.sis.io.wkt.FormattableObject) Conversion(org.opengis.referencing.operation.Conversion)

Example 4 with GeneralDerivedCRS

use of org.opengis.referencing.crs.GeneralDerivedCRS 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)

Example 5 with GeneralDerivedCRS

use of org.opengis.referencing.crs.GeneralDerivedCRS in project sis by apache.

the class DefinitionVerifier method diffCode.

/**
 * Returns a code indicating in which part the two given CRS differ. The given iterators usually iterate over
 * exactly one element, but may iterate over more elements if the CRS were instance of {@code CompoundCRS}.
 * The returned value is one of {@link #METHOD}, {@link #CONVERSION}, {@link #CS}, {@link #DATUM},
 * {@link #PRIME_MERIDIAN} or {@link #OTHER} constants.
 */
private static int diffCode(final Iterator<SingleCRS> authoritative, final Iterator<SingleCRS> given) {
    while (authoritative.hasNext() && given.hasNext()) {
        final SingleCRS crsA = authoritative.next();
        final SingleCRS crsG = given.next();
        if (!Utilities.equalsApproximatively(crsA, crsG)) {
            if (crsA instanceof GeneralDerivedCRS && crsG instanceof GeneralDerivedCRS) {
                final Conversion cnvA = ((GeneralDerivedCRS) crsA).getConversionFromBase();
                final Conversion cnvG = ((GeneralDerivedCRS) crsG).getConversionFromBase();
                if (!Utilities.equalsApproximatively(cnvA, cnvG)) {
                    return Utilities.equalsApproximatively(cnvA.getMethod(), cnvG.getMethod()) ? CONVERSION : METHOD;
                }
            }
            if (!Utilities.equalsApproximatively(crsA.getCoordinateSystem(), crsG.getCoordinateSystem())) {
                return CS;
            }
            final Datum datumA = crsA.getDatum();
            final Datum datumG = crsG.getDatum();
            if (!Utilities.equalsApproximatively(datumA, datumG)) {
                if ((datumA instanceof GeodeticDatum) && (datumG instanceof GeodeticDatum) && !Utilities.equalsApproximatively(((GeodeticDatum) datumA).getPrimeMeridian(), ((GeodeticDatum) datumG).getPrimeMeridian())) {
                    return PRIME_MERIDIAN;
                }
                return DATUM;
            }
            break;
        }
    }
    return OTHER;
}
Also used : SingleCRS(org.opengis.referencing.crs.SingleCRS) Datum(org.opengis.referencing.datum.Datum) GeodeticDatum(org.opengis.referencing.datum.GeodeticDatum) GeneralDerivedCRS(org.opengis.referencing.crs.GeneralDerivedCRS) GeodeticDatum(org.opengis.referencing.datum.GeodeticDatum) Conversion(org.opengis.referencing.operation.Conversion)

Aggregations

GeneralDerivedCRS (org.opengis.referencing.crs.GeneralDerivedCRS)5 CoordinateReferenceSystem (org.opengis.referencing.crs.CoordinateReferenceSystem)3 DefaultGeographicBoundingBox (org.apache.sis.metadata.iso.extent.DefaultGeographicBoundingBox)2 GeographicBoundingBox (org.opengis.metadata.extent.GeographicBoundingBox)2 Conversion (org.opengis.referencing.operation.Conversion)2 HashMap (java.util.HashMap)1 LinkedHashMap (java.util.LinkedHashMap)1 ElementKind (org.apache.sis.io.wkt.ElementKind)1 FormattableObject (org.apache.sis.io.wkt.FormattableObject)1 DefaultParameterDescriptorGroup (org.apache.sis.parameter.DefaultParameterDescriptorGroup)1 Parameterized (org.apache.sis.parameter.Parameterized)1 SimpleInternationalString (org.apache.sis.util.iso.SimpleInternationalString)1 Citation (org.opengis.metadata.citation.Citation)1 ParameterDescriptorGroup (org.opengis.parameter.ParameterDescriptorGroup)1 GeodeticCRS (org.opengis.referencing.crs.GeodeticCRS)1 SingleCRS (org.opengis.referencing.crs.SingleCRS)1 Datum (org.opengis.referencing.datum.Datum)1 GeodeticDatum (org.opengis.referencing.datum.GeodeticDatum)1 CoordinateOperationAuthorityFactory (org.opengis.referencing.operation.CoordinateOperationAuthorityFactory)1 Projection (org.opengis.referencing.operation.Projection)1