Search in sources :

Example 1 with DefaultOperationMethod

use of org.apache.sis.referencing.operation.DefaultOperationMethod in project sis by apache.

the class OperationMethodSetTest method testMixedCases.

/**
 * Tests a non-empty set.
 */
@Test
@DependsOnMethod("testEmpty")
public void testMixedCases() {
    final DefaultOperationMethod merA = createMethod(CylindricalProjection.class, "Mercator (variant A)");
    final DefaultOperationMethod merB = createMethod(CylindricalProjection.class, "Mercator (variant B)");
    final DefaultOperationMethod merC = createMethod(CylindricalProjection.class, "Mercator (variant C)");
    final DefaultOperationMethod dup = createMethod(CylindricalProjection.class, "Mercator (variant B)");
    final DefaultOperationMethod lamb = createMethod(ConicProjection.class, "Lambert");
    final DefaultOperationMethod[] methods = new DefaultOperationMethod[] { merA, merB, merC, dup, lamb };
    final OperationMethodSet mercators = create(CylindricalProjection.class, methods);
    final OperationMethodSet lambert = create(ConicProjection.class, methods);
    final OperationMethodSet all = create(Projection.class, methods);
    /*
         * Mercator case.
         *   - Intentionally start the iteration without checking 'hasNext()' - the iterator shall be robust to that.
         *   - Intentionally start an other iteration (indirectly) in the middle of the first one.
         */
    final Iterator<OperationMethod> iterator = mercators.iterator();
    assertSame(merA, iterator.next());
    assertSame(merB, iterator.next());
    assertArrayEquals("toArray", new DefaultOperationMethod[] { merA, merB, merC }, mercators.toArray());
    assertSame(merC, iterator.next());
    assertFalse(iterator.hasNext());
    assertFalse("isEmpty", mercators.isEmpty());
    assertEquals("size", 3, mercators.size());
    /*
         * Lambert case. Test twice since the two excecutions will take different code paths.
         */
    assertEquals(Collections.singleton(lamb), lambert);
    assertEquals(Collections.singleton(lamb), lambert);
    /*
         * Test filtering: the test should not contain any conic projection.
         */
    assertEmpty(create(PlanarProjection.class, methods));
    /*
         * Opportunist tests.
         */
    assertFalse(lambert.containsAll(all));
    assertTrue(all.containsAll(lambert));
    assertTrue(all.containsAll(mercators));
}
Also used : PlanarProjection(org.opengis.referencing.operation.PlanarProjection) DefaultOperationMethod(org.apache.sis.referencing.operation.DefaultOperationMethod) DefaultOperationMethod(org.apache.sis.referencing.operation.DefaultOperationMethod) OperationMethod(org.opengis.referencing.operation.OperationMethod) Test(org.junit.Test) DependsOnMethod(org.apache.sis.test.DependsOnMethod)

Example 2 with DefaultOperationMethod

use of org.apache.sis.referencing.operation.DefaultOperationMethod in project sis by apache.

the class DefaultMathTransformFactory method getOperationMethod.

/**
 * Returns the operation method for the specified name or identifier. The given argument shall be either
 * a method {@linkplain DefaultOperationMethod#getName() name} (e.g. <cite>"Transverse Mercator"</cite>)
 * or one of its {@linkplain DefaultOperationMethod#getIdentifiers() identifiers} (e.g. {@code "EPSG:9807"}).
 *
 * <p>The search is case-insensitive. Comparisons against method names can be
 * {@linkplain DefaultOperationMethod#isHeuristicMatchForName(String) heuristic}.</p>
 *
 * <p>If more than one method match the given identifier, then the first (according iteration order)
 * non-{@linkplain org.apache.sis.util.Deprecable#isDeprecated() deprecated} matching method is returned.
 * If all matching methods are deprecated, the first one is returned.</p>
 *
 * @param  identifier  the name or identifier of the operation method to search.
 * @return the coordinate operation method for the given name or identifier.
 * @throws NoSuchIdentifierException if there is no operation method registered for the specified identifier.
 *
 * @see org.apache.sis.referencing.operation.DefaultCoordinateOperationFactory#getOperationMethod(String)
 */
public OperationMethod getOperationMethod(String identifier) throws NoSuchIdentifierException {
    identifier = CharSequences.trimWhitespaces(identifier);
    ArgumentChecks.ensureNonEmpty("identifier", identifier);
    OperationMethod method = methodsByName.get(identifier);
    if (method == null) {
        final ReferencingServices services = ReferencingServices.getInstance();
        synchronized (methods) {
            method = services.getOperationMethod(methods, identifier);
        }
        if (method == null) {
            throw new NoSuchIdentifierException(Resources.format(Resources.Keys.NoSuchOperationMethod_1, identifier), identifier);
        }
        /*
             * Remember the method we just found, for faster check next time.
             */
        final OperationMethod previous = methodsByName.putIfAbsent(identifier.intern(), method);
        if (previous != null) {
            method = previous;
        }
    }
    return method;
}
Also used : ReferencingServices(org.apache.sis.internal.metadata.ReferencingServices) NoSuchIdentifierException(org.opengis.util.NoSuchIdentifierException) DefaultOperationMethod(org.apache.sis.referencing.operation.DefaultOperationMethod) OperationMethod(org.opengis.referencing.operation.OperationMethod)

Example 3 with DefaultOperationMethod

use of org.apache.sis.referencing.operation.DefaultOperationMethod in project sis by apache.

the class EPSGDataAccess method createCoordinateOperation.

/**
 * Creates an operation for transforming coordinates in the source CRS to coordinates in the target CRS.
 * The returned object will either be a {@link Conversion} or a {@link Transformation}, depending on the code.
 *
 * <div class="note"><b>Example:</b>
 * some EPSG codes for coordinate transformations are:
 *
 * <table class="sis" summary="EPSG codes examples">
 *   <tr><th>Code</th> <th>Description</th></tr>
 *   <tr><td>1133</td> <td>ED50 to WGS 84 (1)</td></tr>
 *   <tr><td>1241</td> <td>NAD27 to NAD83 (1)</td></tr>
 *   <tr><td>1173</td> <td>NAD27 to WGS 84 (4)</td></tr>
 *   <tr><td>6326</td> <td>NAD83(2011) to NAVD88 height (1)</td></tr>
 * </table></div>
 *
 * @param  code  value allocated by EPSG.
 * @return the operation 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.
 */
@Override
@SuppressWarnings("null")
public synchronized CoordinateOperation createCoordinateOperation(final String code) throws NoSuchAuthorityCodeException, FactoryException {
    ArgumentChecks.ensureNonNull("code", code);
    CoordinateOperation returnValue = null;
    try {
        try (ResultSet result = executeQuery("Coordinate_Operation", "COORD_OP_CODE", "COORD_OP_NAME", "SELECT COORD_OP_CODE," + " COORD_OP_NAME," + " COORD_OP_TYPE," + " SOURCE_CRS_CODE," + " TARGET_CRS_CODE," + " COORD_OP_METHOD_CODE," + " COORD_TFM_VERSION," + " COORD_OP_ACCURACY," + " AREA_OF_USE_CODE," + " COORD_OP_SCOPE," + " REMARKS," + " DEPRECATED" + " FROM [Coordinate_Operation]" + " WHERE COORD_OP_CODE = ?", code)) {
            while (result.next()) {
                final Integer epsg = getInteger(code, result, 1);
                final String name = getString(code, result, 2);
                final String type = getString(code, result, 3).toLowerCase(Locale.US);
                final boolean isTransformation = type.equals("transformation");
                final boolean isConversion = type.equals("conversion");
                final boolean isConcatenated = type.equals("concatenated operation");
                final String sourceCode, targetCode;
                final Integer methodCode;
                if (isConversion) {
                    // Optional for conversions, mandatory for all others.
                    sourceCode = getOptionalString(result, 4);
                    targetCode = getOptionalString(result, 5);
                } else {
                    sourceCode = getString(code, result, 4);
                    targetCode = getString(code, result, 5);
                }
                if (isConcatenated) {
                    // Not applicable to concatenated operation, mandatory for all others.
                    methodCode = getOptionalInteger(result, 6);
                } else {
                    methodCode = getInteger(code, result, 6);
                }
                final String version = getOptionalString(result, 7);
                final double accuracy = getOptionalDouble(result, 8);
                final String area = getOptionalString(result, 9);
                final String scope = getOptionalString(result, 10);
                final String remarks = getOptionalString(result, 11);
                final boolean deprecated = getOptionalBoolean(result, 12);
                /*
                     * Create the source and target CRS for the codes fetched above.  Those CRS are optional only for
                     * conversions (the above calls to getString(code, result, …) verified that those CRS are defined
                     * for other kinds of operation). Conversions in EPSG database are usually "defining conversions"
                     * without source and target CRS.
                     *
                     * In EPSG database 6.7, all defining conversions are projections and their dimensions are always 2.
                     * However, this default number of dimensions is not generalizable to other kind of operation methods.
                     * For example the "Geocentric translation" operation method has 3-dimensional source and target CRS.
                     */
                boolean isDimensionKnown = true;
                final int sourceDimensions, targetDimensions;
                final CoordinateReferenceSystem sourceCRS, targetCRS;
                if (sourceCode != null) {
                    sourceCRS = owner.createCoordinateReferenceSystem(sourceCode);
                    sourceDimensions = sourceCRS.getCoordinateSystem().getDimension();
                } else {
                    sourceCRS = null;
                    // Acceptable default for projections only.
                    sourceDimensions = 2;
                    isDimensionKnown = false;
                }
                if (targetCode != null) {
                    targetCRS = owner.createCoordinateReferenceSystem(targetCode);
                    targetDimensions = targetCRS.getCoordinateSystem().getDimension();
                } else {
                    targetCRS = null;
                    // Acceptable default for projections only.
                    targetDimensions = 2;
                    isDimensionKnown = false;
                }
                /*
                     * Get the operation method. This is mandatory for conversions and transformations
                     * (it was checked by getInteger(code, result, …) above in this method) but optional
                     * for concatenated operations. Fetching parameter values is part of this block.
                     */
                final boolean isDeferred = Semaphores.query(Semaphores.METADATA_ONLY);
                ParameterValueGroup parameters = null;
                OperationMethod method = null;
                if (methodCode != null && !isDeferred) {
                    method = owner.createOperationMethod(methodCode.toString());
                    if (isDimensionKnown) {
                        method = DefaultOperationMethod.redimension(method, sourceDimensions, targetDimensions);
                    }
                    parameters = method.getParameters().createValue();
                    fillParameterValues(methodCode, epsg, parameters);
                }
                /*
                     * Creates common properties. The 'version' and 'accuracy' are usually defined
                     * for transformations only. However, we check them for all kind of operations
                     * (including conversions) and copy the information unconditionally if present.
                     *
                     * NOTE: This block must be executed last before object creations below, because
                     *       methods like createCoordinateReferenceSystem and createOperationMethod
                     *       overwrite the properties map.
                     */
                Map<String, Object> opProperties = createProperties("Coordinate_Operation", name, epsg, area, scope, remarks, deprecated);
                opProperties.put(CoordinateOperation.OPERATION_VERSION_KEY, version);
                if (!Double.isNaN(accuracy)) {
                    opProperties.put(CoordinateOperation.COORDINATE_OPERATION_ACCURACY_KEY, TransformationAccuracy.create(accuracy));
                }
                /*
                     * Creates the operation. Conversions should be the only operations allowed to have
                     * null source and target CRS. In such case, the operation is a defining conversion
                     * (usually to be used later as part of a ProjectedCRS creation).
                     */
                final CoordinateOperation operation;
                final CoordinateOperationFactory copFactory = owner.copFactory;
                if (isDeferred) {
                    operation = new DeferredCoordinateOperation(opProperties, sourceCRS, targetCRS, owner);
                } else if (isConversion && (sourceCRS == null || targetCRS == null)) {
                    operation = copFactory.createDefiningConversion(opProperties, method, parameters);
                } else if (isConcatenated) {
                    /*
                         * Concatenated operation: we need to close the current result set, because
                         * we are going to invoke this method recursively in the following lines.
                         */
                    result.close();
                    // Because this class uses a shared map.
                    opProperties = new HashMap<>(opProperties);
                    final List<String> codes = new ArrayList<>();
                    try (ResultSet cr = executeQuery("Coordinate_Operation Path", "SELECT SINGLE_OPERATION_CODE" + " FROM [Coordinate_Operation Path]" + " WHERE (CONCAT_OPERATION_CODE = ?)" + " ORDER BY OP_PATH_STEP", epsg)) {
                        while (cr.next()) {
                            codes.add(getString(code, cr, 1));
                        }
                    }
                    final CoordinateOperation[] operations = new CoordinateOperation[codes.size()];
                    ensureNoCycle(CoordinateOperation.class, epsg);
                    try {
                        for (int i = 0; i < operations.length; i++) {
                            operations[i] = owner.createCoordinateOperation(codes.get(i));
                        }
                    } finally {
                        endOfRecursivity(CoordinateOperation.class, epsg);
                    }
                    return copFactory.createConcatenatedOperation(opProperties, operations);
                } else {
                    /*
                         * At this stage, the parameters are ready for use. Create the math transform and wrap it in the
                         * final operation (a Conversion or a Transformation). We need to give to MathTransformFactory
                         * some information about the context (source and target CRS) for allowing the factory to set
                         * the values of above-mentioned implicit parameters (semi-major and semi-minor axis lengths).
                         *
                         * The first special case may be removed in a future SIS version if the missing method is added
                         * to GeoAPI. Actually GeoAPI has a method doing part of the job, but incomplete (e.g. the pure
                         * GeoAPI method can not handle Molodensky transform because it does not give the target datum).
                         */
                    final MathTransform mt;
                    final MathTransformFactory mtFactory = owner.mtFactory;
                    if (mtFactory instanceof DefaultMathTransformFactory) {
                        mt = ((DefaultMathTransformFactory) mtFactory).createParameterizedTransform(parameters, ReferencingUtilities.createTransformContext(sourceCRS, targetCRS, null));
                    } else {
                        // Fallback for non-SIS implementations. Work for map projections but not for Molodensky.
                        mt = mtFactory.createBaseToDerived(sourceCRS, parameters, targetCRS.getCoordinateSystem());
                    }
                    /*
                         * Give a hint to the factory about the type of the coordinate operation. ISO 19111 defines
                         * Conversion and Transformation, but SIS also have more specific sub-types.  We begin with
                         * what we can infer from the EPSG database.  Next, if the SIS MathTransform providers give
                         * more information, then we refine the type.
                         */
                    Class<? extends SingleOperation> opType;
                    if (isTransformation) {
                        opType = Transformation.class;
                    } else if (isConversion) {
                        opType = Conversion.class;
                    } else {
                        opType = SingleOperation.class;
                    }
                    final OperationMethod provider = mtFactory.getLastMethodUsed();
                    if (provider instanceof DefaultOperationMethod) {
                        // SIS-specific
                        final Class<?> s = ((DefaultOperationMethod) provider).getOperationType();
                        if (s != null && opType.isAssignableFrom(s)) {
                            opType = s.asSubclass(SingleOperation.class);
                        }
                    }
                    opProperties.put(ReferencingServices.OPERATION_TYPE_KEY, opType);
                    opProperties.put(ReferencingServices.PARAMETERS_KEY, parameters);
                    /*
                         * Following restriction will be removed in a future SIS version if the method is added to GeoAPI.
                         */
                    if (!(copFactory instanceof DefaultCoordinateOperationFactory)) {
                        throw new UnsupportedOperationException(error().getString(Errors.Keys.UnsupportedImplementation_1, copFactory.getClass()));
                    }
                    operation = ((DefaultCoordinateOperationFactory) copFactory).createSingleOperation(opProperties, sourceCRS, targetCRS, null, method, mt);
                }
                returnValue = ensureSingleton(operation, returnValue, code);
                if (result.isClosed()) {
                    return returnValue;
                }
            }
        }
    } catch (SQLException exception) {
        throw databaseFailure(CoordinateOperation.class, code, exception);
    }
    if (returnValue == null) {
        throw noSuchAuthorityCode(CoordinateOperation.class, code);
    }
    return returnValue;
}
Also used : ParameterValueGroup(org.opengis.parameter.ParameterValueGroup) DefaultMathTransformFactory(org.apache.sis.referencing.operation.transform.DefaultMathTransformFactory) SQLException(java.sql.SQLException) ArrayList(java.util.ArrayList) DefaultCoordinateOperationFactory(org.apache.sis.referencing.operation.DefaultCoordinateOperationFactory) DeferredCoordinateOperation(org.apache.sis.internal.referencing.DeferredCoordinateOperation) InternationalString(org.opengis.util.InternationalString) SimpleInternationalString(org.apache.sis.util.iso.SimpleInternationalString) DefaultOperationMethod(org.apache.sis.referencing.operation.DefaultOperationMethod) ResultSet(java.sql.ResultSet) DefaultCoordinateOperationFactory(org.apache.sis.referencing.operation.DefaultCoordinateOperationFactory) DefaultOperationMethod(org.apache.sis.referencing.operation.DefaultOperationMethod) DefaultMathTransformFactory(org.apache.sis.referencing.operation.transform.DefaultMathTransformFactory) DeferredCoordinateOperation(org.apache.sis.internal.referencing.DeferredCoordinateOperation) AbstractIdentifiedObject(org.apache.sis.referencing.AbstractIdentifiedObject) IdentifiedObject(org.opengis.referencing.IdentifiedObject)

Example 4 with DefaultOperationMethod

use of org.apache.sis.referencing.operation.DefaultOperationMethod in project sis by apache.

the class OperationMethodSetTest method createMethod.

/**
 * Creates a new two-dimensional operation method for an operation of the given name.
 *
 * @param  type    the value to be returned by {@link DefaultOperationMethod#getOperationType()}.
 * @param  method  the operation name (example: "Mercator (variant A)").
 * @return the operation method.
 */
@SuppressWarnings("serial")
private static DefaultOperationMethod createMethod(final Class<? extends Projection> type, final String method) {
    Map<String, ?> properties = Collections.singletonMap(DefaultOperationMethod.NAME_KEY, method);
    final ParameterDescriptorGroup parameters = new DefaultParameterDescriptorGroup(properties, 1, 1);
    /*
         * Recycle the ParameterDescriptorGroup name for DefaultOperationMethod.
         * This save us one object creation, and is often the same name anyway.
         */
    properties = Collections.singletonMap(DefaultOperationMethod.NAME_KEY, parameters.getName());
    return new DefaultOperationMethod(properties, 2, 2, parameters) {

        @Override
        public Class<? extends Projection> getOperationType() {
            return type;
        }
    };
}
Also used : DefaultParameterDescriptorGroup(org.apache.sis.parameter.DefaultParameterDescriptorGroup) DefaultParameterDescriptorGroup(org.apache.sis.parameter.DefaultParameterDescriptorGroup) ParameterDescriptorGroup(org.opengis.parameter.ParameterDescriptorGroup) DefaultOperationMethod(org.apache.sis.referencing.operation.DefaultOperationMethod)

Example 5 with DefaultOperationMethod

use of org.apache.sis.referencing.operation.DefaultOperationMethod in project sis by apache.

the class EPSGDataAccess method createOperationMethod.

/**
 * Creates description of the algorithm and parameters used to perform a coordinate operation.
 * An {@code OperationMethod} is a kind of metadata: it does not perform any coordinate operation
 * (e.g. map projection) by itself, but tells us what is needed in order to perform such operation.
 *
 * <div class="note"><b>Example:</b>
 * some EPSG codes for operation methods are:
 *
 * <table class="sis" summary="EPSG codes examples">
 *   <tr><th>Code</th> <th>Description</th></tr>
 *   <tr><td>9804</td> <td>Mercator (variant A)</td></tr>
 *   <tr><td>9802</td> <td>Lambert Conic Conformal (2SP)</td></tr>
 *   <tr><td>9810</td> <td>Polar Stereographic (variant A)</td></tr>
 *   <tr><td>9624</td> <td>Affine parametric transformation</td></tr>
 * </table></div>
 *
 * @param  code  value allocated by EPSG.
 * @return the operation method 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.
 */
@Override
public synchronized OperationMethod createOperationMethod(final String code) throws NoSuchAuthorityCodeException, FactoryException {
    ArgumentChecks.ensureNonNull("code", code);
    OperationMethod returnValue = null;
    try (ResultSet result = executeQuery("Coordinate_Operation Method", "COORD_OP_METHOD_CODE", "COORD_OP_METHOD_NAME", "SELECT COORD_OP_METHOD_CODE," + " COORD_OP_METHOD_NAME," + " REMARKS," + " DEPRECATED" + " FROM [Coordinate_Operation Method]" + " WHERE COORD_OP_METHOD_CODE = ?", code)) {
        while (result.next()) {
            final Integer epsg = getInteger(code, result, 1);
            final String name = getString(code, result, 2);
            final String remarks = getOptionalString(result, 3);
            final boolean deprecated = getOptionalBoolean(result, 4);
            final Integer[] dim = getDimensionsForMethod(epsg);
            final ParameterDescriptor<?>[] descriptors = createParameterDescriptors(epsg);
            Map<String, Object> properties = createProperties("Coordinate_Operation Method", name, epsg, remarks, deprecated);
            // We do not store the formula at this time, because the text is very verbose and rarely used.
            final OperationMethod method = new DefaultOperationMethod(properties, dim[0], dim[1], new DefaultParameterDescriptorGroup(properties, 1, 1, descriptors));
            returnValue = ensureSingleton(method, returnValue, code);
        }
    } catch (SQLException exception) {
        throw databaseFailure(OperationMethod.class, code, exception);
    }
    if (returnValue == null) {
        throw noSuchAuthorityCode(OperationMethod.class, code);
    }
    return returnValue;
}
Also used : SQLException(java.sql.SQLException) ParameterDescriptor(org.opengis.parameter.ParameterDescriptor) DefaultParameterDescriptor(org.apache.sis.parameter.DefaultParameterDescriptor) InternationalString(org.opengis.util.InternationalString) SimpleInternationalString(org.apache.sis.util.iso.SimpleInternationalString) DefaultOperationMethod(org.apache.sis.referencing.operation.DefaultOperationMethod) DefaultParameterDescriptorGroup(org.apache.sis.parameter.DefaultParameterDescriptorGroup) ResultSet(java.sql.ResultSet) AbstractIdentifiedObject(org.apache.sis.referencing.AbstractIdentifiedObject) IdentifiedObject(org.opengis.referencing.IdentifiedObject) DefaultOperationMethod(org.apache.sis.referencing.operation.DefaultOperationMethod)

Aggregations

DefaultOperationMethod (org.apache.sis.referencing.operation.DefaultOperationMethod)8 OperationMethod (org.opengis.referencing.operation.OperationMethod)5 DefaultParameterDescriptorGroup (org.apache.sis.parameter.DefaultParameterDescriptorGroup)3 ParameterDescriptorGroup (org.opengis.parameter.ParameterDescriptorGroup)3 ResultSet (java.sql.ResultSet)2 SQLException (java.sql.SQLException)2 AbstractIdentifiedObject (org.apache.sis.referencing.AbstractIdentifiedObject)2 SimpleInternationalString (org.apache.sis.util.iso.SimpleInternationalString)2 Test (org.junit.Test)2 IdentifiedObject (org.opengis.referencing.IdentifiedObject)2 FactoryException (org.opengis.util.FactoryException)2 InternationalString (org.opengis.util.InternationalString)2 NoSuchIdentifierException (org.opengis.util.NoSuchIdentifierException)2 ArrayList (java.util.ArrayList)1 HashMap (java.util.HashMap)1 IdentityHashMap (java.util.IdentityHashMap)1 ReferencingServices (org.apache.sis.internal.metadata.ReferencingServices)1 DeferredCoordinateOperation (org.apache.sis.internal.referencing.DeferredCoordinateOperation)1 DefaultParameterDescriptor (org.apache.sis.parameter.DefaultParameterDescriptor)1 InvalidGeodeticParameterException (org.apache.sis.referencing.factory.InvalidGeodeticParameterException)1