in endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/factory/sql/EPSGDataAccess.java [2796:2997]
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) {
sourceCode = getOptionalString(result, 4); // Optional for conversions, mandatory for all others.
targetCode = getOptionalString(result, 5);
} else {
sourceCode = getString(code, result, 4);
targetCode = getString(code, result, 5);
}
if (isConcatenated) {
methodCode = getOptionalInteger(result, 6); // Not applicable to concatenated operation, mandatory for all others.
} 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.
*/
final CoordinateReferenceSystem sourceCRS, targetCRS;
if (sourceCode != null) {
sourceCRS = owner.createCoordinateReferenceSystem(sourceCode);
} else {
sourceCRS = null;
}
if (targetCode != null) {
targetCRS = owner.createCoordinateReferenceSystem(targetCode);
} else {
targetCRS = null;
}
/*
* 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());
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,
PositionalAccuracyConstant.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();
opProperties = new HashMap<>(opProperties); // Because this class uses a shared map.
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 {
endOfRecursion(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 cannot handle Molodensky transform because it does not give the target datum).
*/
opProperties = new HashMap<>(opProperties); // Because this class uses a shared map.
final var builder = new ParameterizedTransformBuilder(owner.mtFactory, null);
builder.setParameters(parameters, true);
builder.setSourceAxes(sourceCRS);
builder.setTargetAxes(targetCRS);
final MathTransform mt = builder.create();
/*
* 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 = builder.getMethod().orElse(null);
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(CoordinateOperations.OPERATION_TYPE_KEY, opType);
opProperties.put(CoordinateOperations.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;
}