Search in sources :

Example 11 with ParameterDescriptorGroup

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

the class DefaultOperationMethodTest method create.

/**
 * Creates a new two-dimensional operation method for an operation of the given name and identifier.
 *
 * @param  method      the operation name (example: "Mercator (variant A)").
 * @param  identifier  the EPSG numeric identifier (example: "9804").
 * @param  formula     formula citation (example: "EPSG guidance note #7-2").
 * @param  dimension   the number of input and output dimension, or {@code null}.
 * @param  parameters  the parameters (can be empty).
 * @return the operation method.
 */
static DefaultOperationMethod create(final String method, final String identifier, final String formula, final Integer dimension, final ParameterDescriptor<?>... parameters) {
    final Map<String, Object> properties = new HashMap<>(8);
    assertNull(properties.put(OperationMethod.NAME_KEY, method));
    assertNull(properties.put(ReferenceIdentifier.CODESPACE_KEY, "EPSG"));
    assertNull(properties.put(Identifier.AUTHORITY_KEY, Citations.EPSG));
    /*
         * The parameter group for a Mercator projection is actually not empty, but it is not the purpose of
         * this class to test DefaultParameterDescriptorGroup. So we use an empty group of parameters here.
         */
    final ParameterDescriptorGroup pg = new DefaultParameterDescriptorGroup(properties, 1, 1, parameters);
    /*
         * NAME_KEY share the same Identifier instance for saving a little bit of memory.
         * Then define the other properties to be given to OperationMethod.
         */
    assertNotNull(properties.put(OperationMethod.NAME_KEY, pg.getName()));
    assertNull(properties.put(OperationMethod.IDENTIFIERS_KEY, new ImmutableIdentifier(Citations.EPSG, "EPSG", identifier)));
    assertNull(properties.put(OperationMethod.FORMULA_KEY, new DefaultCitation(formula)));
    return new DefaultOperationMethod(properties, dimension, dimension, pg);
}
Also used : DefaultParameterDescriptorGroup(org.apache.sis.parameter.DefaultParameterDescriptorGroup) HashMap(java.util.HashMap) DefaultCitation(org.apache.sis.metadata.iso.citation.DefaultCitation) DefaultParameterDescriptorGroup(org.apache.sis.parameter.DefaultParameterDescriptorGroup) ParameterDescriptorGroup(org.opengis.parameter.ParameterDescriptorGroup) ImmutableIdentifier(org.apache.sis.metadata.iso.ImmutableIdentifier)

Example 12 with ParameterDescriptorGroup

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

the class SingleOperationMarshallingTest method verifyMethod.

/**
 * Verifies the unmarshalled parameter descriptors.
 */
private static void verifyMethod(final OperationMethod method) {
    assertIdentifierEquals("name", null, null, null, "Mercator (1SP)", method.getName());
    assertEquals("formula", "See EPSG guide.", method.getFormula().getFormula().toString());
    assertEquals("sourceDimensions", Integer.valueOf(2), method.getSourceDimensions());
    assertEquals("targetDimensions", Integer.valueOf(2), method.getTargetDimensions());
    final ParameterDescriptorGroup parameters = method.getParameters();
    assertEquals("parameters.name", "Mercator (1SP)", parameters.getName().getCode());
    final Iterator<GeneralParameterDescriptor> it = parameters.descriptors().iterator();
    CC_OperationParameterGroupTest.verifyMethodParameter(Mercator1SP.LATITUDE_OF_ORIGIN, (ParameterDescriptor<?>) it.next());
    CC_OperationParameterGroupTest.verifyMethodParameter(Mercator1SP.LONGITUDE_OF_ORIGIN, (ParameterDescriptor<?>) it.next());
    assertFalse("Unexpected parameter.", it.hasNext());
}
Also used : ParameterDescriptorGroup(org.opengis.parameter.ParameterDescriptorGroup) GeneralParameterDescriptor(org.opengis.parameter.GeneralParameterDescriptor)

Example 13 with ParameterDescriptorGroup

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

the class GeocentricAffine method createParameters.

/**
 * Returns the parameters for creating a datum shift operation.
 * The operation method will be one of the {@code GeocentricAffine} subclasses,
 * unless the specified {@code method} argument is {@link DatumShiftMethod#NONE}.
 * If no single operation method can be used, then this method returns {@code null}.
 *
 * <p>This method does <strong>not</strong> change the coordinate system type.
 * The source and target coordinate systems can be both {@code EllipsoidalCS} or both {@code CartesianCS}.
 * Any other type or mix of types (e.g. a {@code EllipsoidalCS} source and {@code CartesianCS} target)
 * will cause this method to return {@code null}. In such case, it is caller's responsibility to apply
 * the datum shift itself in Cartesian geocentric coordinates.</p>
 *
 * @param  sourceCS    the source coordinate system. Only the type and number of dimensions is checked.
 * @param  targetCS    the target coordinate system. Only the type and number of dimensions is checked.
 * @param  datumShift  the datum shift as a matrix, or {@code null} if there is no datum shift information.
 * @param  method      the preferred datum shift method. Note that {@code createParameters(…)} may overwrite.
 * @return the parameter values, or {@code null} if no single operation method can be found.
 */
public static ParameterValueGroup createParameters(final CoordinateSystem sourceCS, final CoordinateSystem targetCS, final Matrix datumShift, DatumShiftMethod method) {
    final boolean isEllipsoidal = (sourceCS instanceof EllipsoidalCS);
    if (!(isEllipsoidal ? (targetCS instanceof EllipsoidalCS) : (targetCS instanceof CartesianCS && sourceCS instanceof CartesianCS))) {
        // Coordinate systems are not two EllipsoidalCS or two CartesianCS.
        return null;
    }
    @SuppressWarnings("null") int dimension = sourceCS.getDimension();
    if (dimension != targetCS.getDimension()) {
        // Any value greater than 3 means "mismatched dimensions" for this method.
        dimension = 4;
    }
    if (method == DatumShiftMethod.NONE) {
        if (dimension <= 3) {
            return Affine.identity(dimension);
        } else if (isEllipsoidal) {
            final ParameterDescriptorGroup descriptor;
            switch(sourceCS.getDimension()) {
                case 2:
                    descriptor = Geographic2Dto3D.PARAMETERS;
                    break;
                case 3:
                    descriptor = Geographic3Dto2D.PARAMETERS;
                    break;
                default:
                    return null;
            }
            return descriptor.createValue();
        } else {
            return null;
        }
    }
    /*
         * Try to convert the matrix into (tX, tY, tZ, rX, rY, rZ, dS) parameters.
         * The matrix may not be convertible, in which case we will let the caller
         * uses the matrix directly in Cartesian geocentric coordinates.
         */
    final BursaWolfParameters parameters = new BursaWolfParameters(null, null);
    if (datumShift != null)
        try {
            parameters.setPositionVectorTransformation(datumShift, BURSAWOLF_TOLERANCE);
        } catch (IllegalArgumentException e) {
            log(Loggers.COORDINATE_OPERATION, "createParameters", e);
            return null;
        }
    else {
        /*
             * If there is no datum shift parameters (not to be confused with identity), then those parameters
             * are assumed unknown. Using the most accurate methods would give a false impression of accuracy,
             * so we use the fastest method instead. Since all parameter values are zero, Apache SIS should use
             * the AbridgedMolodenskyTransform2D optimization.
             */
        method = DatumShiftMethod.ABRIDGED_MOLODENSKY;
    }
    final boolean isTranslation = parameters.isTranslation();
    final ParameterDescriptorGroup descriptor;
    /*
         * Following "if" blocks are ordered from most accurate to less accurate datum shift method
         * supported by GeocentricAffine subclasses (except NONE which has already been handled).
         * Special cases:
         *
         *   - If the datum shift is applied between geocentric CRS, then the Molodensky approximations do not apply
         *     as they are designed for transformations between geographic CRS only. User preference is then ignored.
         *
         *   - Molodensky methods are approximations for datum shifts having only translation terms in their Bursa-Wolf
         *     parameters. If there is also a scale or rotation terms, then we can not use Molodensky methods. The user
         *     preference is then ignored.
         */
    if (!isEllipsoidal) {
        method = DatumShiftMethod.GEOCENTRIC_DOMAIN;
        descriptor = isTranslation ? GeocentricTranslation.PARAMETERS : PositionVector7Param.PARAMETERS;
    } else if (!isTranslation) {
        method = DatumShiftMethod.GEOCENTRIC_DOMAIN;
        descriptor = (dimension >= 3) ? PositionVector7Param3D.PARAMETERS : PositionVector7Param2D.PARAMETERS;
    } else
        switch(method) {
            case GEOCENTRIC_DOMAIN:
                {
                    descriptor = (dimension >= 3) ? GeocentricTranslation3D.PARAMETERS : GeocentricTranslation2D.PARAMETERS;
                    break;
                }
            case MOLODENSKY:
                {
                    descriptor = Molodensky.PARAMETERS;
                    break;
                }
            case ABRIDGED_MOLODENSKY:
                {
                    descriptor = AbridgedMolodensky.PARAMETERS;
                    break;
                }
            default:
                throw new AssertionError(method);
        }
    /*
         * Following lines will set all Bursa-Wolf parameter values (scale, translation
         * and rotation terms). In the particular case of Molodensky method, we have an
         * additional parameter for the number of source and target dimensions (2 or 3).
         */
    final Parameters values = createParameters(descriptor, parameters, isTranslation);
    switch(method) {
        case MOLODENSKY:
        case ABRIDGED_MOLODENSKY:
            {
                if (dimension <= 3) {
                    values.getOrCreate(Molodensky.DIMENSION).setValue(dimension);
                }
                break;
            }
    }
    return values;
}
Also used : CartesianCS(org.opengis.referencing.cs.CartesianCS) Parameters(org.apache.sis.parameter.Parameters) BursaWolfParameters(org.apache.sis.referencing.datum.BursaWolfParameters) ParameterDescriptorGroup(org.opengis.parameter.ParameterDescriptorGroup) EllipsoidalCS(org.opengis.referencing.cs.EllipsoidalCS) BursaWolfParameters(org.apache.sis.referencing.datum.BursaWolfParameters)

Example 14 with ParameterDescriptorGroup

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

the class CC_OperationParameterGroup method merge.

/**
 * Invoked by {@link DefaultParameterDescriptorGroup#setDescriptors(GeneralParameterDescriptor[])}
 * for merging into a single set the descriptors which are repeated twice in a GML document.
 *
 * <p>The {@code descriptors} argument gives the descriptors listed explicitely inside a
 * {@code <gml:OperationParameterGroup>} or {@code <gml:OperationMethod>} element. Those
 * descriptors are said "incomplete" (from SIS point of view) because they are missing the
 * {@link ParameterDescriptor#getValueClass()} property, which does not exist in GML but
 * is mandatory for us. However an exception to this "incompleteness" happen when SIS has
 * been able to match the {@code <gml:OperationMethod>} parent to one of the pre-defined
 * operations in the {@link org.apache.sis.internal.referencing.provider} package.</p>
 *
 * <p>The {@code fromValues} argument gives the descriptors declared in each {@code <gml:ParameterValue>}
 * instances of a {@code <gml:ParameterValueGroup>} or {@code <gml:AbstractSingleOperation>} element.
 * Contrarily to the {@code descriptors} argument, the {@code fromValues} instances should have non-null
 * {@link ParameterDescriptor#getValueClass()} property inferred by SIS from the parameter value.</p>
 *
 * <p>So the preferred descriptors from more complete to less complete are:</p>
 * <ol>
 *   <li>{@code descriptors} if and only if they contain pre-defined parameters inferred by SIS from the {@code <gml:OperationMethod>} name.</li>
 *   <li>{@code fromValues}, which contain the descriptors declared in the {@code <gml:ParameterValue>} instances.</li>
 *   <li>{@code descriptors}, which contain the descriptor listed in {@code <gml:OperationParameterGroup>} or {@code <gml:OperationMethod>}.</li>
 * </ol>
 *
 * <div class="note"><b>Note:</b>
 * this code is defined in this {@code CC_OperationParameterGroup} class instead than in the
 * {@link DefaultParameterDescriptorGroup} class in the hope to reduce the amount of code
 * processed by the JVM in the common case where JAXB (un)marshalling is not needed.</div>
 *
 * @param  descriptors   the descriptors declared in the {@code ParameterDescriptorGroup}.
 * @param  fromValues    the descriptors declared in the {@code ParameterValue} instances.
 *                       They are said "valid" because they contain the mandatory {@code valueClass} property.
 * @param  replacements  an {@code IdentityHashMap} where to store the replacements that the caller needs to
 *                       apply in the {@code GeneralParameterValue} instances.
 * @return a sequence containing the merged set of parameter descriptors.
 *
 * @see <a href="http://issues.apache.org/jira/browse/SIS-290">SIS-290</a>
 */
public static GeneralParameterDescriptor[] merge(final List<GeneralParameterDescriptor> descriptors, final GeneralParameterDescriptor[] fromValues, final Map<GeneralParameterDescriptor, GeneralParameterDescriptor> replacements) {
    if (descriptors.isEmpty()) {
        return fromValues;
    }
    final Map<String, GeneralParameterDescriptor> union = new LinkedHashMap<>(Containers.hashMapCapacity(descriptors.size()));
    /*
         * Collect the descriptors declared explicitely in the ParameterDescriptorGroup. We should never have
         * two descriptors of the same name since the DefaultParameterDescriptorGroup constructor checked for
         * name ambiguity. If a name collision is nevertheless detected, this would mean that a descriptor's
         * name mutated.
         */
    for (final GeneralParameterDescriptor p : descriptors) {
        final String name = p.getName().getCode();
        if (union.put(name, p) != null) {
            throw new CorruptedObjectException(name);
        }
    }
    /*
         * Verify if any descriptors found in the ParameterValue instances could replace the descriptors in the group.
         * We give precedence to the descriptors having a non-null 'valueClass' property, which normally appear in the
         * 'fromValues' array.
         */
    for (final GeneralParameterDescriptor valueDescriptor : fromValues) {
        final String name = valueDescriptor.getName().getCode();
        GeneralParameterDescriptor complete = valueDescriptor;
        GeneralParameterDescriptor previous = union.put(name, complete);
        if (previous != null) {
            if (previous instanceof ParameterDescriptor<?>) {
                verifyEquivalence(name, complete instanceof ParameterDescriptor<?>);
                final Class<?> valueClass = ((ParameterDescriptor<?>) previous).getValueClass();
                if (valueClass != null) {
                    /*
                         * This may happen if the 'descriptors' argument contain the parameters of a pre-defined
                         * method from the 'org.apache.sis.internal.referencing.provider' package instead than a
                         * descriptor from the GML file.  In such case, presume that 'previous' is actually more
                         * complete than 'complete'.
                         *
                         * Note that 'r' should never be null unless JAXB unmarshalled the elements in reverse
                         * order (e.g. <gml:ParameterValue> before <gml:OperationMethod>). Since this behavior
                         * may depend on JAXB implementation, we are better to check for such case.
                         */
                    final Class<?> r = ((ParameterDescriptor<?>) complete).getValueClass();
                    if (r != null) {
                        verifyEquivalence(name, valueClass == r);
                    }
                    // Restore the previous value in the map and swap 'previous' with 'replacement'.
                    previous = union.put(name, complete = previous);
                }
            } else if (previous instanceof ParameterDescriptorGroup) {
                verifyEquivalence(name, complete instanceof ParameterDescriptorGroup);
            }
            /*
                 * Verify that the replacement contains at least all the information provided by the previous
                 * descriptor. The replacement is allowed to contain more information however.
                 */
            final GeneralParameterDescriptor replacement = CC_GeneralOperationParameter.merge(previous, complete);
            if (replacement != valueDescriptor) {
                union.put(name, replacement);
                if (replacements.put(valueDescriptor, replacement) != null) {
                    // Should never happen, unless the parameter name changed during execution of this loop.
                    throw new CorruptedObjectException(name);
                }
            }
        }
    }
    return union.values().toArray(new GeneralParameterDescriptor[union.size()]);
}
Also used : GeneralParameterDescriptor(org.opengis.parameter.GeneralParameterDescriptor) ParameterDescriptor(org.opengis.parameter.ParameterDescriptor) DefaultParameterDescriptorGroup(org.apache.sis.parameter.DefaultParameterDescriptorGroup) ParameterDescriptorGroup(org.opengis.parameter.ParameterDescriptorGroup) GeneralParameterDescriptor(org.opengis.parameter.GeneralParameterDescriptor) CorruptedObjectException(org.apache.sis.util.CorruptedObjectException) LinkedHashMap(java.util.LinkedHashMap)

Example 15 with ParameterDescriptorGroup

use of org.opengis.parameter.ParameterDescriptorGroup 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)

Aggregations

ParameterDescriptorGroup (org.opengis.parameter.ParameterDescriptorGroup)27 DefaultParameterDescriptorGroup (org.apache.sis.parameter.DefaultParameterDescriptorGroup)13 GeneralParameterDescriptor (org.opengis.parameter.GeneralParameterDescriptor)13 Test (org.junit.Test)8 HashMap (java.util.HashMap)7 DependsOnMethod (org.apache.sis.test.DependsOnMethod)6 ParameterDescriptor (org.opengis.parameter.ParameterDescriptor)5 ParameterValueGroup (org.opengis.parameter.ParameterValueGroup)5 DefaultOperationMethod (org.apache.sis.referencing.operation.DefaultOperationMethod)4 ParameterNotFoundException (org.opengis.parameter.ParameterNotFoundException)4 OperationMethod (org.opengis.referencing.operation.OperationMethod)3 ArrayList (java.util.ArrayList)2 IdentityHashMap (java.util.IdentityHashMap)2 FormattableObject (org.apache.sis.io.wkt.FormattableObject)2 ParameterBuilder (org.apache.sis.parameter.ParameterBuilder)2 GeneralParameterValue (org.opengis.parameter.GeneralParameterValue)2 FactoryException (org.opengis.util.FactoryException)2 Collection (java.util.Collection)1 LinkedHashMap (java.util.LinkedHashMap)1 UnmodifiableArrayList (org.apache.sis.internal.util.UnmodifiableArrayList)1