Search in sources :

Example 56 with ParameterValueGroup

use of org.opengis.parameter.ParameterValueGroup in project sis by apache.

the class DefaultParameterValueGroup method groups.

/**
 * Returns all subgroups with the specified name.
 *
 * <p>This method do not create new groups: if the requested group is optional (i.e.
 * <code>{@linkplain DefaultParameterDescriptor#getMinimumOccurs() minimumOccurs} == 0</code>)
 * and no value were defined previously, then this method returns an empty set.</p>
 *
 * @param  name  the name of the parameter to search for.
 * @return the set of all parameter group for the given name.
 * @throws ParameterNotFoundException if no descriptor was found for the given name.
 */
@Override
public List<ParameterValueGroup> groups(final String name) throws ParameterNotFoundException {
    ArgumentChecks.ensureNonNull("name", name);
    // Protect against accidental changes.
    final ParameterValueList values = this.values;
    final List<ParameterValueGroup> groups = new ArrayList<>(4);
    final int size = values.size();
    for (int i = 0; i < size; i++) {
        final GeneralParameterDescriptor descriptor = values.descriptor(i);
        if (descriptor instanceof ParameterDescriptorGroup) {
            if (IdentifiedObjects.isHeuristicMatchForName(descriptor, name)) {
                groups.add((ParameterValueGroup) values.get(i));
            }
        }
    }
    /*
         * No groups were found. Check if the group actually exists (i.e. is declared in the
         * descriptor). If it doesn't exists, then an exception is thrown. If it exists (i.e.
         * it is simply an optional group not yet defined), then returns an empty list.
         */
    if (groups.isEmpty()) {
        final ParameterDescriptorGroup descriptor = values.descriptor;
        if (!(descriptor.descriptor(name) instanceof ParameterDescriptorGroup)) {
            throw new ParameterNotFoundException(Resources.format(Resources.Keys.ParameterNotFound_2, Verifier.getDisplayName(descriptor), name), name);
        }
    }
    return groups;
}
Also used : ParameterValueGroup(org.opengis.parameter.ParameterValueGroup) ParameterDescriptorGroup(org.opengis.parameter.ParameterDescriptorGroup) ArrayList(java.util.ArrayList) GeneralParameterDescriptor(org.opengis.parameter.GeneralParameterDescriptor) ParameterNotFoundException(org.opengis.parameter.ParameterNotFoundException)

Example 57 with ParameterValueGroup

use of org.opengis.parameter.ParameterValueGroup in project sis by apache.

the class DefaultParameterValueGroup method addGroup.

/**
 * Creates a new subgroup of the specified name, and adds it to the list of subgroups.
 * The argument shall be the name of a {@linkplain DefaultParameterDescriptorGroup descriptor group}
 * which is a child of this group.
 *
 * <div class="note"><b>API note:</b>
 * There is no {@code removeGroup(String)} method. To remove a group, users shall inspect the
 * {@link #values()} list, decide which occurrences to remove if there is many of them for the
 * same name, and whether to iterate recursively into sub-groups or not.</div>
 *
 * @param  name  the name of the parameter group to create.
 * @return a newly created parameter group for the given name.
 * @throws ParameterNotFoundException if no descriptor was found for the given name.
 * @throws InvalidParameterCardinalityException if this parameter group already contains the
 *         {@linkplain ParameterDescriptorGroup#getMaximumOccurs() maximum number of occurrences}
 *         of subgroups of the given name.
 */
@Override
public ParameterValueGroup addGroup(final String name) throws ParameterNotFoundException, InvalidParameterCardinalityException {
    // Protect against accidental changes.
    final ParameterValueList values = this.values;
    final ParameterDescriptorGroup descriptor = values.descriptor;
    final GeneralParameterDescriptor child = descriptor.descriptor(name);
    if (!(child instanceof ParameterDescriptorGroup)) {
        throw new ParameterNotFoundException(Resources.format(Resources.Keys.ParameterNotFound_2, descriptor.getName(), name), name);
    }
    final ParameterValueGroup value = ((ParameterDescriptorGroup) child).createValue();
    values.add(value);
    return value;
}
Also used : ParameterValueGroup(org.opengis.parameter.ParameterValueGroup) ParameterDescriptorGroup(org.opengis.parameter.ParameterDescriptorGroup) GeneralParameterDescriptor(org.opengis.parameter.GeneralParameterDescriptor) ParameterNotFoundException(org.opengis.parameter.ParameterNotFoundException)

Example 58 with ParameterValueGroup

use of org.opengis.parameter.ParameterValueGroup in project sis by apache.

the class AbstractCoordinateOperation method formatTo.

/**
 * Formats this coordinate operation in Well Known Text (WKT) version 2 format.
 *
 * @param  formatter  the formatter to use.
 * @return {@code "CoordinateOperation"}.
 *
 * @see <a href="http://docs.opengeospatial.org/is/12-063r5/12-063r5.html#113">WKT 2 specification §17</a>
 */
@Override
protected String formatTo(final Formatter formatter) {
    super.formatTo(formatter);
    formatter.newLine();
    /*
         * If the WKT is a component of a ConcatenatedOperation, do not format the source CRS since it is identical
         * to the target CRS of the previous step, or to the source CRS of the enclosing "ConcatenatedOperation" if
         * this step is the first step.
         *
         * This decision is SIS-specific since the WKT 2 specification does not define concatenated operations.
         * This choice may change in any future SIS version.
         */
    final FormattableObject enclosing = formatter.getEnclosingElement(1);
    final boolean isSubOperation = (enclosing instanceof PassThroughOperation);
    final boolean isComponent = (enclosing instanceof ConcatenatedOperation);
    if (!isSubOperation && !isComponent) {
        append(formatter, getSourceCRS(), WKTKeywords.SourceCRS);
        append(formatter, getTargetCRS(), WKTKeywords.TargetCRS);
    }
    final OperationMethod method = getMethod();
    if (method != null) {
        formatter.append(DefaultOperationMethod.castOrCopy(method));
        ParameterValueGroup parameters;
        try {
            parameters = getParameterValues();
        } catch (UnsupportedOperationException e) {
            final IdentifiedObject c = getParameterDescriptors();
            formatter.setInvalidWKT(c != null ? c : this, e);
            parameters = null;
        }
        if (parameters != null) {
            /*
                 * Format the parameter values. Apache SIS uses the EPSG geodetic dataset as the main source of
                 * parameter definitions. When a parameter is defined by both OGC and EPSG with different names,
                 * the Formatter class is responsible for choosing an appropriate name. But when the difference
                 * is more fundamental, we may have duplication. For example in the "Molodensky" operation, OGC
                 * uses source and target axis lengths while EPSG uses only difference between those lengths.
                 * In this case, OGC and EPSG parameters are defined separately and are redundant. To simplify
                 * the CoordinateOperation WKT, we omit non-EPSG parameters when we have determined that we are
                 * about to describe an EPSG operation. We could generalize this filtering to any authority, but
                 * we don't because few authorities are as complete as EPSG, so other authorities are more likely
                 * to mix EPSG or someone else components with their own. Note also that we don't apply filtering
                 * on MathTransform WKT neither for more reliable debugging.
                 */
            final boolean filter = // NOT method.getName()
            WKTUtilities.isEPSG(parameters.getDescriptor(), false) && Constants.EPSG.equalsIgnoreCase(Citations.getCodeSpace(formatter.getNameAuthority()));
            formatter.newLine();
            formatter.indent(+1);
            for (final GeneralParameterValue param : parameters.values()) {
                if (!filter || WKTUtilities.isEPSG(param.getDescriptor(), true)) {
                    WKTUtilities.append(param, formatter);
                }
            }
            formatter.indent(-1);
        }
    }
    if (!isSubOperation && !(this instanceof ConcatenatedOperation)) {
        append(formatter, getInterpolationCRS(), WKTKeywords.InterpolationCRS);
        final double accuracy = getLinearAccuracy();
        if (accuracy > 0) {
            formatter.append(new FormattableObject() {

                @Override
                protected String formatTo(final Formatter formatter) {
                    formatter.append(accuracy);
                    return WKTKeywords.OperationAccuracy;
                }
            });
        }
    }
    if (formatter.getConvention().majorVersion() == 1) {
        formatter.setInvalidWKT(this, null);
    }
    if (isComponent) {
        formatter.setInvalidWKT(this, null);
        return "CoordinateOperationStep";
    }
    return WKTKeywords.CoordinateOperation;
}
Also used : GeneralParameterValue(org.opengis.parameter.GeneralParameterValue) ParameterValueGroup(org.opengis.parameter.ParameterValueGroup) Formatter(org.apache.sis.io.wkt.Formatter) InternationalString(org.opengis.util.InternationalString) FormattableObject(org.apache.sis.io.wkt.FormattableObject) OperationMethod(org.opengis.referencing.operation.OperationMethod) PassThroughOperation(org.opengis.referencing.operation.PassThroughOperation) ConcatenatedOperation(org.opengis.referencing.operation.ConcatenatedOperation) IdentifiedObject(org.opengis.referencing.IdentifiedObject) AbstractIdentifiedObject(org.apache.sis.referencing.AbstractIdentifiedObject)

Example 59 with ParameterValueGroup

use of org.opengis.parameter.ParameterValueGroup in project sis by apache.

the class CoordinateOperationFinder method createOperationStep.

/**
 * Creates an operation between two geodetic (geographic or geocentric) coordinate reference systems.
 * The default implementation can:
 *
 * <ul>
 *   <li>adjust axis order and orientation, for example converting from (<cite>North</cite>, <cite>West</cite>)
 *       axes to (<cite>East</cite>, <cite>North</cite>) axes,</li>
 *   <li>apply units conversion if needed,</li>
 *   <li>perform longitude rotation if needed,</li>
 *   <li>perform datum shift if {@linkplain BursaWolfParameters Bursa-Wolf parameters} are available
 *       for the area of interest.</li>
 * </ul>
 *
 * <p>This method returns only <em>one</em> step for a chain of concatenated operations (to be built by the caller).
 * But a list is returned because the same step may be implemented by different operation methods. Only one element
 * in the returned list should be selected (usually the first one).</p>
 *
 * @param  sourceCRS  input coordinate reference system.
 * @param  targetCRS  output coordinate reference system.
 * @return a coordinate operation from {@code sourceCRS} to {@code targetCRS}.
 * @throws FactoryException if the operation can not be constructed.
 */
@SuppressWarnings("null")
protected List<CoordinateOperation> createOperationStep(final GeodeticCRS sourceCRS, final GeodeticCRS targetCRS) throws FactoryException {
    final GeodeticDatum sourceDatum = sourceCRS.getDatum();
    final GeodeticDatum targetDatum = targetCRS.getDatum();
    Matrix datumShift = null;
    /*
         * If the prime meridian is not the same, we will concatenate a longitude rotation before or after datum shift
         * (that concatenation will be performed by the customized DefaultMathTransformFactory.Context created below).
         * Actually we do not know if the longitude rotation should be before or after datum shift. But this ambiguity
         * can usually be ignored because Bursa-Wolf parameters are always used with source and target prime meridians
         * set to Greenwich in EPSG dataset 8.9.  For safety, the SIS's DefaultGeodeticDatum class ensures that if the
         * prime meridians are not the same, then the target meridian must be Greenwich.
         */
    final DefaultMathTransformFactory.Context context = ReferencingUtilities.createTransformContext(sourceCRS, targetCRS, new MathTransformContext(sourceDatum, targetDatum));
    /*
         * If both CRS use the same datum and the same prime meridian, then the coordinate operation is only axis
         * swapping, unit conversion or change of coordinate system type (Ellipsoidal ↔ Cartesian ↔ Spherical).
         * Otherwise (if the datum are not the same), we will need to perform a scale, translation and rotation
         * in Cartesian space using the Bursa-Wolf parameters. If the user does not require the best accuracy,
         * then the Molodensky approximation may be used for avoiding the conversion step to geocentric CRS.
         */
    Identifier identifier;
    boolean isGeographicToGeocentric = false;
    final CoordinateSystem sourceCS = context.getSourceCS();
    final CoordinateSystem targetCS = context.getTargetCS();
    if (equalsIgnoreMetadata(sourceDatum, targetDatum)) {
        final boolean isGeocentricToGeographic;
        isGeographicToGeocentric = (sourceCS instanceof EllipsoidalCS && targetCS instanceof CartesianCS);
        isGeocentricToGeographic = (sourceCS instanceof CartesianCS && targetCS instanceof EllipsoidalCS);
        /*
             * Above booleans should never be true in same time. If it nevertheless happen (we are paranoiac;
             * maybe a lazy user implemented all interfaces in a single class), do not apply any geographic ↔
             * geocentric conversion. Instead do as if the coordinate system types were the same.
             */
        if (isGeocentricToGeographic ^ isGeographicToGeocentric) {
            identifier = GEOCENTRIC_CONVERSION;
        } else {
            identifier = AXIS_CHANGES;
        }
    } else {
        identifier = ELLIPSOID_CHANGE;
        if (sourceDatum instanceof DefaultGeodeticDatum) {
            datumShift = ((DefaultGeodeticDatum) sourceDatum).getPositionVectorTransformation(targetDatum, areaOfInterest);
            if (datumShift != null) {
                identifier = DATUM_SHIFT;
            }
        }
    }
    /*
         * Conceptually, all transformations below could done by first converting from the source coordinate
         * system to geocentric Cartesian coordinates (X,Y,Z), apply an affine transform represented by the
         * datum shift matrix, then convert from the (X′,Y′,Z′) coordinates to the target coordinate system.
         * However there is two exceptions to this path:
         *
         *   1) In the particular where both the source and target CS are ellipsoidal, we may use the
         *      Molodensky approximation as a shortcut (if the desired accuracy allows).
         *
         *   2) Even if we really go through the XYZ coordinates without Molodensky approximation, there is
         *      at least 9 different ways to name this operation depending on whether the source and target
         *      CRS are geocentric or geographic, 2- or 3-dimensional, whether there is a translation or not,
         *      the rotation sign, etc. We try to use the most specific name if we can find one, and fallback
         *      on an arbitrary name only in last resort.
         */
    final DefaultMathTransformFactory mtFactory = factorySIS.getDefaultMathTransformFactory();
    MathTransform before = null, after = null;
    ParameterValueGroup parameters;
    if (identifier == DATUM_SHIFT || identifier == ELLIPSOID_CHANGE) {
        /*
             * If the transform can be represented by a single coordinate operation, returns that operation.
             * Possible operations are:
             *
             *    - Position Vector transformation (in geocentric, geographic-2D or geographic-3D domains)
             *    - Geocentric translation         (in geocentric, geographic-2D or geographic-3D domains)
             *    - [Abridged] Molodensky          (as an approximation of geocentric translation)
             *    - Identity                       (if the desired accuracy is so large than we can skip datum shift)
             *
             * TODO: if both CS are ellipsoidal but with different number of dimensions, then we should use
             * an intermediate 3D geographic CRS in order to enable the use of Molodensky method if desired.
             */
        final DatumShiftMethod preferredMethod = DatumShiftMethod.forAccuracy(desiredAccuracy);
        parameters = GeocentricAffine.createParameters(sourceCS, targetCS, datumShift, preferredMethod);
        if (parameters == null) {
            /*
                 * Failed to select a coordinate operation. Maybe because the coordinate system types are not the same.
                 * Convert unconditionally to XYZ geocentric coordinates and apply the datum shift in that CS space.
                 *
                 * TODO: operation name should not be "Affine" if 'before' or 'after' transforms are not identity.
                 *       Reminder: the parameter group name here determines the OperationMethod later in this method.
                 */
            if (datumShift != null) {
                parameters = TensorParameters.WKT1.createValueGroup(properties(Constants.AFFINE), datumShift);
            } else {
                // Dimension of geocentric CRS.
                parameters = Affine.identity(3);
            }
            final CoordinateSystem normalized = CommonCRS.WGS84.geocentric().getCoordinateSystem();
            before = mtFactory.createCoordinateSystemChange(sourceCS, normalized, sourceDatum.getEllipsoid());
            after = mtFactory.createCoordinateSystemChange(normalized, targetCS, targetDatum.getEllipsoid());
            context.setSource(normalized);
            context.setTarget(normalized);
        }
    } else if (identifier == GEOCENTRIC_CONVERSION) {
        parameters = (isGeographicToGeocentric ? GeographicToGeocentric.PARAMETERS : GeocentricToGeographic.PARAMETERS).createValue();
    } else {
        /*
             * Coordinate system change (including change in the number of dimensions) without datum shift.
             */
        final int sourceDim = sourceCS.getDimension();
        final int targetDim = targetCS.getDimension();
        if (// sourceDim == 2 or 3.
        (sourceDim & ~1) == 2 && // abs(sourceDim - targetDim) == 1.
        (sourceDim ^ targetDim) == 1 && (sourceCS instanceof EllipsoidalCS) && (targetCS instanceof EllipsoidalCS)) {
            parameters = (sourceDim == 2 ? Geographic2Dto3D.PARAMETERS : Geographic3Dto2D.PARAMETERS).createValue();
        } else {
            /*
                 * TODO: instead than creating parameters for an identity operation, we should create the
                 *       CoordinateOperation directly from the MathTransform created by mtFactory below.
                 *       The intent if to get the correct OperationMethod, which should not be "Affine"
                 *       if there is a CS type change.
                 */
            parameters = Affine.identity(targetDim);
            /*
                 * createCoordinateSystemChange(…) needs the ellipsoid associated to the ellipsoidal coordinate system,
                 * if any. If none or both coordinate systems are ellipsoidal, then the ellipsoid will be ignored (see
                 * createCoordinateSystemChange(…) javadoc for the rational) so it does not matter which one we pick.
                 */
            before = mtFactory.createCoordinateSystemChange(sourceCS, targetCS, (sourceCS instanceof EllipsoidalCS ? sourceDatum : targetDatum).getEllipsoid());
            context.setSource(targetCS);
        }
    }
    /*
         * Transform between differents datums using Bursa Wolf parameters. The Bursa Wolf parameters are used
         * with "standard" geocentric CS, i.e. with X axis towards the prime meridian, Y axis towards East and
         * Z axis toward North, unless the Molodensky approximation is used. The following steps are applied:
         *
         *     source CRS                        →
         *     normalized CRS with source datum  →
         *     normalized CRS with target datum  →
         *     target CRS
         *
         * Those steps may be either explicit with the 'before' and 'after' transform, or implicit with the
         * Context parameter.
         */
    MathTransform transform = mtFactory.createParameterizedTransform(parameters, context);
    final OperationMethod method = mtFactory.getLastMethodUsed();
    if (before != null) {
        transform = mtFactory.createConcatenatedTransform(before, transform);
        if (after != null) {
            transform = mtFactory.createConcatenatedTransform(transform, after);
        }
    }
    return asList(createFromMathTransform(properties(identifier), sourceCRS, targetCRS, transform, method, parameters, null));
}
Also used : DefaultMathTransformFactory(org.apache.sis.referencing.operation.transform.DefaultMathTransformFactory) ParameterValueGroup(org.opengis.parameter.ParameterValueGroup) DefaultGeodeticDatum(org.apache.sis.referencing.datum.DefaultGeodeticDatum) NamedIdentifier(org.apache.sis.referencing.NamedIdentifier) Identifier(org.opengis.metadata.Identifier) DefaultGeodeticDatum(org.apache.sis.referencing.datum.DefaultGeodeticDatum) DatumShiftMethod(org.apache.sis.internal.referencing.provider.DatumShiftMethod)

Example 60 with ParameterValueGroup

use of org.opengis.parameter.ParameterValueGroup in project sis by apache.

the class CoordinateOperationRegistry method fromDefiningConversion.

/**
 * Creates a complete coordinate operation from a defining conversion. Defining conversions usually have
 * null source and target CRS, but this method nevertheless checks that, in order to reuse the operation
 * CRS if it happens to have some.
 *
 * @param  operation  the operation specified by the authority.
 * @param  sourceCRS  the source CRS specified by the user.
 * @param  targetCRS  the target CRS specified by the user
 * @return a new operation from the given source CRS to target CRS.
 * @throws FactoryException if an error occurred while creating the new operation.
 */
private CoordinateOperation fromDefiningConversion(final SingleOperation operation, CoordinateReferenceSystem sourceCRS, CoordinateReferenceSystem targetCRS) throws FactoryException {
    final ParameterValueGroup parameters = operation.getParameterValues();
    if (parameters != null) {
        CoordinateReferenceSystem crs;
        if (Utilities.equalsApproximatively(sourceCRS, crs = operation.getSourceCRS()))
            sourceCRS = crs;
        if (Utilities.equalsApproximatively(targetCRS, crs = operation.getTargetCRS()))
            targetCRS = crs;
        final MathTransformFactory mtFactory = factorySIS.getMathTransformFactory();
        if (mtFactory instanceof DefaultMathTransformFactory) {
            MathTransform mt = ((DefaultMathTransformFactory) mtFactory).createParameterizedTransform(parameters, ReferencingUtilities.createTransformContext(sourceCRS, targetCRS, null));
            return factorySIS.createSingleOperation(IdentifiedObjects.getProperties(operation), sourceCRS, targetCRS, null, operation.getMethod(), mt);
        }
    } else {
        // Should never happen because parameters are mandatory, but let be safe.
        log(Resources.forLocale(null).getLogRecord(Level.WARNING, Resources.Keys.MissingParameterValues_1, IdentifiedObjects.getIdentifierOrName(operation)), null);
    }
    return null;
}
Also used : DefaultMathTransformFactory(org.apache.sis.referencing.operation.transform.DefaultMathTransformFactory) ParameterValueGroup(org.opengis.parameter.ParameterValueGroup) DefaultMathTransformFactory(org.apache.sis.referencing.operation.transform.DefaultMathTransformFactory) CoordinateReferenceSystem(org.opengis.referencing.crs.CoordinateReferenceSystem)

Aggregations

ParameterValueGroup (org.opengis.parameter.ParameterValueGroup)98 Test (org.junit.Test)54 DependsOnMethod (org.apache.sis.test.DependsOnMethod)27 GeneralParameterValue (org.opengis.parameter.GeneralParameterValue)12 ProjectedCRS (org.opengis.referencing.crs.ProjectedCRS)11 OperationMethod (org.opengis.referencing.operation.OperationMethod)11 ParameterValue (org.opengis.parameter.ParameterValue)8 GeneralParameterDescriptor (org.opengis.parameter.GeneralParameterDescriptor)7 ParameterNotFoundException (org.opengis.parameter.ParameterNotFoundException)7 SingleOperation (org.opengis.referencing.operation.SingleOperation)6 FactoryException (org.opengis.util.FactoryException)6 DefaultGeodeticDatum (org.apache.sis.referencing.datum.DefaultGeodeticDatum)5 IdentifiedObject (org.opengis.referencing.IdentifiedObject)5 Matrix (org.opengis.referencing.operation.Matrix)5 ArrayList (java.util.ArrayList)4 DefaultConversion (org.apache.sis.referencing.operation.DefaultConversion)4 ParameterDescriptorGroup (org.opengis.parameter.ParameterDescriptorGroup)4 CoordinateReferenceSystem (org.opengis.referencing.crs.CoordinateReferenceSystem)4 CoordinateOperation (org.opengis.referencing.operation.CoordinateOperation)4 URL (java.net.URL)3